45 Commits

Author SHA1 Message Date
9650bd8b3c added tests and readme for eduroam initial smoketest 2026-01-19 18:35:51 +01:00
33112f6142 Add dev backend compose and nginx config 2026-01-14 19:55:36 +01:00
fc00c3f627 Add education-flagger submodule 2026-01-14 19:55:25 +01:00
436a81e2a8 chore: add Makefile for proxy/app lifecycle commands 2025-09-15 19:38:57 +02:00
fa6780d032 chore: update .gitignore for infra secrets and acme.json 2025-09-15 19:38:57 +02:00
19d41f3041 docs(infra): document new infra v2 structure and usage 2025-09-15 19:38:57 +02:00
a32e2da6c3 chore(infra): add env templates and bootstrap script; ignore secrets in git 2025-09-15 19:38:57 +02:00
062b30e379 feat(infra): add Nextcloud stack with MariaDB and Redis, Traefik labels 2025-09-15 19:38:57 +02:00
b186c22bf2 chore(infra): scaffold Traefik core (static+dynamic config, compose) 2025-09-15 19:38:57 +02:00
61853ac2cd update repo dependency 2025-03-19 14:42:56 +01:00
56e6a53f0a add backend overwrite compose file for laravel and sql 2025-03-19 14:41:47 +01:00
62a96dc243 improve kirby 2025-03-13 19:24:28 +01:00
d4f202f204 add readme to env folder 2025-03-05 15:21:52 +01:00
49badb74a7 different script approaches to start the app 2025-03-05 15:20:01 +01:00
71d080a87e added some fallback values 2025-03-05 15:19:42 +01:00
138525835d move adminer to develop 2025-03-05 15:11:14 +01:00
f6e3793193 added scripts for deploy backend 2025-03-04 18:23:37 +01:00
c9b55aa0ed restructure backend folder and database 2025-03-04 18:22:02 +01:00
b8a6abe100 update env stucture for frontend deployment 2025-03-04 18:18:21 +01:00
2930854814 setup scripts for global env, project root and proxy env 2025-02-28 18:09:25 +01:00
d4abe64b0b changes in development environtments 2025-02-28 18:08:52 +01:00
1d04638be8 add healthcheck and init user script for database 2025-02-28 18:08:23 +01:00
02f20a277c add invoiceninja as git module 2025-02-28 18:07:07 +01:00
0f08168947 update the docker files for almost all apps 2025-02-28 18:06:47 +01:00
c976fea1c3 clean up 2025-02-28 10:12:13 +01:00
e981a365cc fall back für ROOT_DIR based on the current directory in file system 2025-02-26 12:48:56 +01:00
42b71394df updating the readme file environment explaination 2025-02-26 12:46:46 +01:00
a9143ae8f8 delete the docker compose files with where not neccesary for the code to run 2025-02-26 12:46:24 +01:00
dff86e0486 move deploy scripts to start folder 2025-02-24 14:08:48 +01:00
f14186deca update env Variables 2025-02-24 14:08:16 +01:00
9afa8808db add raw state of with jenkins 2025-02-24 14:07:22 +01:00
69323be965 create all .env.files but without content so far 2025-02-18 17:36:43 +01:00
76e3b3938f add an .env.all file that contains all default values 2025-02-18 17:36:15 +01:00
97b28e9540 updated formattings of env files 2025-02-18 17:35:44 +01:00
b886adf877 add minor improvement and fallback values 2025-02-18 17:35:19 +01:00
c45823ee91 add the inital README for project structure 2025-02-18 11:02:21 +01:00
2cb9a141b2 update gitignore to avoid builds 2025-02-18 11:01:48 +01:00
fd36d17e12 add script to set the ENV variable Server-IP based on the host 2025-02-18 11:01:10 +01:00
ba5d253c19 kirby cms website 2025-02-18 10:26:52 +01:00
a5b26ebde1 created initial script for different service profiles 2025-02-18 10:26:06 +01:00
ac27973330 environment variables all the different production setups ( not yet adapted, sensitive info as well) 2025-02-17 18:13:54 +01:00
ebe6da4fbb creating env files especially for the project structure (development only) 2025-02-17 18:10:50 +01:00
3686959644 rename config to env 2025-02-17 18:10:12 +01:00
c0a3fc7a10 compose files for basically all apps 2025-02-17 18:09:34 +01:00
83e78eb652 Environment Variables for local development and dev status 2025-02-17 18:08:50 +01:00
105 changed files with 2989 additions and 276 deletions

7
.gitignore vendored
View File

@@ -1,3 +1,10 @@
volumes volumes
apps/proxy apps/proxy
.DS_Store .DS_Store
apps/administration/*
apps/tools/app/*
env/secrets.env
infra/core/traefik/data/acme.json
infra/**/.env
infra/**/*.env.local
infra/secrets/*

6
.gitmodules vendored
View File

@@ -4,3 +4,9 @@
[submodule "apps/frontend/src"] [submodule "apps/frontend/src"]
path = apps/frontend/src path = apps/frontend/src
url = https://gitea.mindboost.team/Mindboost/mindboost-webapp.git url = https://gitea.mindboost.team/Mindboost/mindboost-webapp.git
[submodule "apps/tools/invoiceninja/dockerfiles"]
path = apps/tools/invoiceninja/dockerfiles
url = https://github.com/invoiceninja/dockerfiles.git
[submodule "apps/security/Eduroam Analyzer/asn-updater"]
path = apps/security/Eduroam Analyzer/asn-updater
url = https://gitea.mindboost.team/mindboost/education-flagger.git

34
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,34 @@
pipeline {
agent any
stages {
stage('Checkout Code & Submodules') {
steps {
git branch: 'main', url: 'git@github.com:your-org/my-main-repo.git', credentialsId: 'git-credentials'
sh 'git submodule update --init --recursive'
}
}
stage('Run Backend Pipeline') {
steps {
build job: 'backend-pipeline', wait: true
}
}
stage('Run Frontend Pipeline') {
steps {
build job: 'frontend-pipeline', wait: true
}
}
stage('Deploy Infrastructure') {
steps {
sshagent(['jenkins-ssh-key']) {
sh "ssh user@server 'cd /opt/myapp && git pull origin main && docker compose up -d'"
}
}
}
}
}

42
Makefile Normal file
View File

@@ -0,0 +1,42 @@
SHELL := /bin/bash
# Environment selection
ENV ?= development
COMMON_ENV := infra/env/$(ENV)/common.env
# Helper to pass env files if present
define with_env
$(foreach f,$(1),$(if $(wildcard $(f)),--env-file $(f),))
endef
.PHONY: bootstrap proxy-up proxy-down proxy-logs app-up app-down app-logs ps
bootstrap:
@bash scripts/infra/bootstrap.sh
proxy-up:
@docker compose -f infra/core/traefik/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/traefik/.env) up -d
proxy-down:
@docker compose -f infra/core/traefik/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/traefik/.env) down
proxy-logs:
@docker compose -f infra/core/traefik/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/traefik/.env) logs -f
# Usage: make app-up APP=nextcloud
APP ?=
app-up:
@test -n "$(APP)" || (echo "APP not set. Example: make app-up APP=nextcloud" && exit 1)
@docker compose -f infra/apps/$(APP)/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/$(APP)/.env) up -d
app-down:
@test -n "$(APP)" || (echo "APP not set. Example: make app-down APP=nextcloud" && exit 1)
@docker compose -f infra/apps/$(APP)/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/$(APP)/.env) down
app-logs:
@test -n "$(APP)" || (echo "APP not set. Example: make app-logs APP=nextcloud" && exit 1)
@docker compose -f infra/apps/$(APP)/docker-compose.yml $(call with_env,$(COMMON_ENV) infra/apps/$(APP)/.env) logs -f
ps:
@docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Networks}}'

211
README.md
View File

@@ -1,3 +1,214 @@
# mindboost-infrastructure # mindboost-infrastructure
All the software used and hosted by mindboost organized in containers. All the software used and hosted by mindboost organized in containers.
## New Infra (v2) Overview
This repo now includes a modular, bestpractice infrastructure under `infra/` to make replication and selective deployment easy. It is centered on Traefik as the reverse proxy with automatic TLS via Let's Encrypt, environment layering, and pickwhatyouneed application stacks.
- Core: `infra/core/traefik` — Traefik with HTTPS (ACME), dashboard, and sane defaults
- Apps: `infra/apps/<service>` — selfcontained stacks (e.g., `nextcloud`)
- Env: `infra/env/<environment>/common.env` — environment defaults (dev/prod)
- Secrets: `infra/secrets/` — local secret storage (ignored by git)
- Make targets: toplevel `Makefile` to bootstrap, start proxy, and start apps
Quickstart
- Copy `infra/env/development/common.env` and adjust domains and ACME email.
- Create the shared proxy network and ACME storage: `make bootstrap`
- Start Traefik: `make proxy-up`
- Start a service, e.g. Nextcloud: `make app-up APP=nextcloud`
Notes
- Traefik dashboard is exposed at `TRAEFIK_DASHBOARD_DOMAIN` with optional basic auth.
- Services connect to an external `proxy` network for routing, plus their own internal network.
- Each app has its own `.env.example`; copy to `.env` and adjust.
- The legacy `apps/` structure remains as-is; new infra is additive and can coexist.
## Project Structure
./apps/
├── docker-compose.all.yml # Orchestriert alle Docker Compose Stacks
├── frontend/
│ ├── docker-compose.yml
│ └── src/ # Vue.js frontend source code
├── backend/
│ ├── docker-compose.yml
│ └── src/ # Laravel backend source code
├── database/
│ └── docker-compose.yml # MariaDB stack
├── website/
│ └── docker-compose.yml # KirbyCMS public site stack
├── administration/
│ └── docker-compose.yml # Portainer stack
├── proxy/
│ └── docker-compose.yml # Traefik, Crowdsec, and Bouncer stack
├── develop/
│ └── docker-compose.yml # Gitea, Jenkins, and Adminer stack
└── tools/
└── docker-compose.yml # Nextcloud, LimeSurvey, and LinkStack stack
## Current Services
1. Frontend (Vue.js)
2. Backend (Laravel)
3. Database (MariaDB)
4. Proxy (Traefik, Crowdsec, Bouncer)
## Upcoming Services
1. Website (KirbyCMS)
2. Administration (Portainer)
3. Development Tools (Gitea, Jenkins, Adminer)
4. Utility Tools (Nextcloud, LimeSurvey, LinkStack)
## Service Descriptions
### Current Services
- **Frontend**: Vue.js based user interface for the mindboost application.
- **Backend**: Laravel based API and server-side logic for the mindboost application.
- **Database**: MariaDB for data storage and management.
- **Proxy**: Traefik for reverse proxy, Crowdsec and Bouncer for security.
### Upcoming Services
- **Website**: KirbyCMS for the public-facing website.
- **Administration**: Portainer for container management and monitoring.
- **Development Tools**:
- Gitea: Self-hosted Git service
- Jenkins: Continuous Integration/Continuous Deployment (CI/CD) tool
- Adminer: Database management tool
- **Utility Tools**:
- Nextcloud: File hosting and collaboration platform
- LimeSurvey: Online survey tool
- LinkStack: Link management tool
## Deployment
Each service or group of related services has its own `docker-compose.yml` file in its respective folder under `./apps/`. This structure allows for modular deployment and easier management of individual services.
To deploy a service, navigate to its directory and run:
```bash
docker-compose up -d
```
For the entire infrastructure, a root `docker-compose.yml` file can be created to orchestrate all services together.
## Environment Configuration
Environment variables are managed in a centralized `env` folder at the root of the project. This structure allows for easy management of different environments and services.
./env/
├── development/
│ ├── frontend.env
│ ├── backend.env
│ ├── database.env
│ └── ...
├── staging/
│ ├── frontend.env
│ ├── backend.env
│ ├── database.env
│ └── ...
└── production/
├── frontend.env
├── backend.env
├── database.env
└── ...
Each service's `docker-compose.yml` file references the appropriate `.env` file based on the current environment. For example:
```yaml
services:
backend:
env_file:
- ../../env/${ENVIRONMENT:-development}/backend.env
```
## Networking
Our infrastructure uses a two-tier network model to enhance security and isolate services:
1. Proxy Network (proxy_network):
- Exposed to the internet and contains the Traefik reverse proxy.
- Only services that need to be publicly accessible should be connected to this network.
- Example services: Traefik, frontend application.
2. Internal Networks:
- Separate internal networks are created for each public service that needs to communicate with internal services.
- These networks are not directly accessible from the internet and provide secure communication between public and internal services.
- Examples: backend_network, database_network, etc.
This structure ensures that:
- The proxy (Traefik) can route traffic to public-facing services.
- Internal services (like databases) are not directly accessible from the proxy network.
- Each connection between a public and an internal service has its own isolated network.
This configuration minimizes the attack surface by isolating networks and ensuring that services only have access to the networks they absolutely need. Each connection between a public and an internal service is protected by a dedicated internal network, further enhancing security.
## Volumes
Persistent data should be managed using named volumes or bind mounts, depending on the requirements of each service. This ensures data persistence across container restarts and updates.
The `volumes/` folder contains subdirectories for different volumes used by various applications in the infrastructure. This centralized structure allows for easier management and backup of persistent data.
./volumes/
├── backend/ # Volume for backend-specific data
├── database/ # Volume for MariaDB data
├── website/ # Volume for KirbyCMS data
├── administration/ # Volume for Portainer data
├── develop/
│ ├── gitea/ # Volume for Gitea repositories and data
│ └── jenkins/ # Volume for Jenkins data and job configurations
└── tools/
├── nextcloud/ # Volume for Nextcloud files and data
├── limesurvey/ # Volume for LimeSurvey data
└── linkstack/ # Volume for LinkStack data
Each subdirectory corresponds to a specific service or group of services, containing the persistent data that needs to be preserved across container restarts or redeployments.
When configuring Docker Compose files, reference these volume paths to ensure data persistence.
```yaml
volumes:
- ./volumes/database:/var/lib/mysql
```
## Scripts
The `scripts/` folder contains a collection of utility scripts for deployment, backup, and maintenance tasks. These scripts are designed to automate common operations and ensure consistency across different environments.
./scripts/
├── deployment/
│ ├── deploy-app.sh # Script for deploying the main application
│ └── deploy-traefik.sh # Script for deploying Traefik
├── backup/
│ ├── backup-database.sh # Script for backing up the database
│ └── backup-files.sh # Script for backing up file storage
└── maintenance/
├── update-services.sh # Script for updating all services
└── health-check.sh # Script for performing health checks on services
These scripts can be run from the command line to perform various tasks related to the infrastructure. Always review and test scripts in a safe environment before using them in production.
To use a script, navigate to the scripts directory and run:
```bash
./script-name.sh

View File

@@ -0,0 +1,36 @@
### Database (./apps/database/docker-compose.yml)
# - [ ] Create a MariaDB service
# - [ ] Configure volumes for persistent storage of database data
secrets:
mariadb_root:
file: ${ROOT_DIR:-../../..}/env/secrets.env
services:
database:
secrets:
- mariadb_root
profiles: ["all", "database", "backend", "app"]
image: mariadb:latest
container_name: ${INFRASTRUCTURE_LABEL:-default}-mariadb-${ENVIRONMENT:-development}
command: --bind-address=0.0.0.0
env_file:
- ${ROOT_DIR:-../../..}/env/${ENVIRONMENT:-development}/.env.database
volumes:
- backend_mariadb_data:/var/lib/mysql
- ./healthcheck.sh:/usr/local/bin/healthcheck.sh
networks:
- backend
- database
healthcheck:
test: ["CMD", "bash", "/usr/local/bin/healthcheck.sh"]
interval: 1s
retries: 3
# TODO: ADMINER IS NOT PREPARED FOR TRAEFIK
networks:
backend:
name: ${INFRASTRUCTURE_LABEL:-default}-backend-${ENVIRONMENT:-development}
database:
name: ${INFRASTRUCTURE_LABEL:-default}-database-${ENVIRONMENT:-development}
volumes:
backend_mariadb_data:
driver: local
name: ${INFRASTRUCTURE_LABEL:-default}_mariadb_${ENVIRONMENT:-development}

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# Credentials from environment variables
MYSQL_USER="${MARIADB_USER:-default}"
MYSQL_PASSWORD="${MARIADB_PASSWORD:-default}"
MYSQL_HOST="127.0.0.1"
ROOT_PASSWORD=$(cat /run/secrets/mariadb_root)
echo "🔑 READ ROOT PASSWORD FROM SECRETS"
# Check if MariaDB is running
if ! mariadb -h "$MYSQL_HOST" -u root -p"$ROOT_PASSWORD" -e "SELECT 1;" &>/dev/null; then
echo "❌ MariaDB is not responding"
exit 1
fi
# Check if a specific user exists
USER_EXISTS=$(mariadb -h "$MYSQL_HOST" -u root -p"$ROOT_PASSWORD" -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '${MYSQL_USER}');" | tail -n 1)
if [ "$USER_EXISTS" -ne 1 ]; then
echo "❌ User '${MYSQL_USER}' does not exist"
exit 1
fi
# Check if the user can log in with the provided password
if ! mariadb -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1;" &>/dev/null; then
echo "❌ User '${MYSQL_USER}' exists, but authentication failed with the provided password."
exit 1
fi
echo "✅ MariaDB is healthy"
exit 0

View File

@@ -0,0 +1,74 @@
#!/bin/bash
echo "🔄 Running MariaDB initialization script..."
# Wait until MariaDB is ready
until mysqladmin ping -h localhost --silent; do
sleep 2
done
echo "✅ MariaDB is ready. Checking root credentials..."
# Try logging in with the root password
if ! mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "SELECT 1;" &>/dev/null; then
echo "❌ ERROR: Root password in .env does not match the database!"
echo "🔄 Attempting to reset the root password..."
# Stop MariaDB safely
echo "⚠️ Stopping MariaDB..."
service mysql stop || pkill mysqld
sleep 5
# Start MariaDB in recovery mode
echo "🚀 Starting MariaDB in recovery mode..."
mysqld_safe --skip-grant-tables --skip-networking &
sleep 5
# Reset root password
echo "🔐 Resetting root password..."
mysql -u root <<EOSQL
ALTER USER 'root'@'localhost' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}';
ALTER USER 'root'@'%' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}';
FLUSH PRIVILEGES;
EOSQL
echo "✅ Root password reset successfully!"
# Restart MariaDB in normal mode
echo "🔄 Restarting MariaDB in production mode..."
service mysql stop || pkill mysqld
sleep 3
mysqld_safe &
sleep 5
else
echo "✅ Root password is correct."
fi
# Check if the database exists
DB_EXISTS=$(mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "SHOW DATABASES LIKE '${MARIADB_DATABASE}';" | grep "${MARIADB_DATABASE}" > /dev/null; echo "$?")
if [ "$DB_EXISTS" -ne 0 ]; then
echo "⚠️ Database '${MARIADB_DATABASE}' does not exist. Creating it now..."
mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "CREATE DATABASE ${MARIADB_DATABASE};"
echo "✅ Database '${MARIADB_DATABASE}' created!"
else
echo "✅ Database '${MARIADB_DATABASE}' already exists."
fi
# Ensure the database user exists and has the correct password
USER_EXISTS=$(mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '${MARIADB_USER}');" | tail -n 1)
if [ "$USER_EXISTS" -eq 0 ]; then
echo "⚠️ User '${MARIADB_USER}' does not exist. Creating it now..."
mysql -u root -p"$MARIADB_ROOT_PASSWORD" <<EOSQL
CREATE USER '${MARIADB_USER}'@'%' IDENTIFIED BY '${MARIADB_PASSWORD}';
GRANT ALL PRIVILEGES ON ${MARIADB_DATABASE}.* TO '${MARIADB_USER}'@'%';
FLUSH PRIVILEGES;
EOSQL
echo "✅ User '${MARIADB_USER}' created and granted access to '${MARIADB_DATABASE}'!"
else
echo "✅ User '${MARIADB_USER}' already exists. Ensuring correct password."
mysql -u root -p"$MARIADB_ROOT_PASSWORD" -e "ALTER USER '${MARIADB_USER}'@'%' IDENTIFIED BY '${MARIADB_PASSWORD}'; FLUSH PRIVILEGES;"
echo "✅ Password for '${MARIADB_USER}' updated!"
fi
echo "🎉 MariaDB initialization complete!"

View File

@@ -0,0 +1,48 @@
### Backend (./apps/backend/docker-compose.yml)
include:
- ./database/docker-compose.yml
services:
backend:
container_name: ${INFRASTRUCTURE_LABEL:-default}-backend-laravel-${ENVIRONMENT:-development}
profiles: ["laravel", "backend", "all", "app"]
ports:
- "${LARAVEL_PORT:-8000}:8000"
- "${LARAVEL_VITE_PORT:-5173}:5173"
env_file:
- ../../env/${ENVIRONMENT:-development}/.env.backend
volumes:
- ./src/entrypoint.sh:/usr/local/bin/entrypoint.sh
depends_on:
- database
build:
context: ./src
dockerfile: Dockerfile
networks:
- backend
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.backend.entrypoints=${TRAEFIK_ENTRYPOINT}"
- "traefik.http.routers.backend.rule=Host(`${BACKEND_DOMAIN}`)"
- "traefik.http.routers.backend.tls=true"
- "traefik.http.routers.backend.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.routers.backend.tls.domains[0].main=`${BACKEND_DOMAIN}`"
- "traefik.http.services.backend.loadbalancer.server.port=${BACKEND_PORT:-8000}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
backend-redis:
image: redis:alpine
container_name: ${INFRASTRUCTURE_LABEL:-default}-backend-redis-${ENVIRONMENT:-development}
profiles: ["redis", "backend", "all"]
env_file:
- ../../env/${ENVIRONMENT:-development}/.env.backend
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-laravel-redis-passwort} # Redis Passwort eingeben
volumes:
- backend_redis_data:/data
networks:
- backend
volumes:
backend_redis_data:
driver: local
name: "${INFRASTRUCTURE_LABEL:-default}_backend_redis_data"

View File

@@ -0,0 +1,45 @@
### Backend (./apps/backend/docker-compose.yml)
include:
- ./database/docker-compose.yml
services:
backend:
container_name: ${INFRASTRUCTURE_LABEL:-default}-backend-laravel-${ENVIRONMENT:-development}
profiles: ["laravel", "backend", "all", "app"]
env_file:
- ../../env/${ENVIRONMENT:-development}/.env.backend
volumes:
- ./src/entrypoint.sh:/usr/local/bin/entrypoint.sh
depends_on:
- database
build:
context: ./src
dockerfile: Dockerfile
networks:
- backend
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.backend.entrypoints=${TRAEFIK_ENTRYPOINT}"
- "traefik.http.routers.backend.rule=Host(`${BACKEND_DOMAIN}`)"
- "traefik.http.routers.backend.tls=true"
- "traefik.http.routers.backend.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.routers.backend.tls.domains[0].main=`${BACKEND_DOMAIN}`"
- "traefik.http.services.backend.loadbalancer.server.port=${BACKEND_PORT:-8000}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
backend-redis:
image: redis:alpine
container_name: ${INFRASTRUCTURE_LABEL:-default}-backend-redis-${ENVIRONMENT:-development}
profiles: ["redis", "backend", "all"]
env_file:
- ../../env/${ENVIRONMENT:-development}/.env.backend
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-laravel-redis-passwort} # Redis Passwort eingeben
volumes:
- backend_redis_data:/data
networks:
- backend
volumes:
backend_redis_data:
driver: local
name: "${INFRASTRUCTURE_LABEL}_backend_redis_data"

View File

@@ -0,0 +1,8 @@
services:
adminer:
profiles: ["all", "database", "backend", "adminer", "app"]
image: adminer
container_name: ${INFRASTRUCTURE_LABEL:-default}-adminer-${ENVIRONMENT:-development}
restart: always
ports:
- ${ADMINER_PORT:-0}:8080

View File

@@ -0,0 +1,20 @@
services:
adminer:
profiles: ["all", "database", "backend", "adminer", "app"]
image: adminer
container_name: ${INFRASTRUCTURE_LABEL:-default}-adminer-${ENVIRONMENT:-development}
restart: always
ports:
- ${ADMINER_PORT:-0}:8080
networks:
- database
- proxy
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.rule=Host(`${ADMINER_DOMAIN:-adminer.local}`)"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.tls=true"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-http_resolver}"
- 'traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_adminer.service=adminer'
- "traefik.http.adminer.cloud.loadbalancer.server.port=8080"
- "traefik.docker.network=${TRAEFIK_NETWORK:-default}"

View File

@@ -0,0 +1,9 @@
### Develop (./apps/develop/docker-compose.yml)
# - [ ] Create services for Gitea, Jenkins, and Adminer
# - [ ] Configure volumes for persistent storage of Git repositories, Jenkins data, and Adminer settings
# - [ ] Set up environment variables using the new structure (../../env/${ENVIRONMENT:-development}/develop.env)
# - [ ] Configure networking to allow these services to communicate with each other and the necessary application services
# - [ ] Set up access controls and security measures for development tools
include:
- ./gitea/docker-compose.yml

View File

@@ -0,0 +1,44 @@
services:
gitea:
image: gitea/gitea:latest
container_name: ${INFRASTRUCTURE_LABEL:-mindboost}-gitea
profiles: ["all", "gitea","develop"]
restart: always
volumes:
- ${GITEA_VOLUME_PATH}:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
- gitea_db
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.gitea.entrypoints=${TRAEFIK_ENTRYPOINT}"
- "traefik.http.routers.gitea.rule=(Host(`${GITEA_DOMAIN})`)"
- "traefik.http.routers.gitea.tls=true"
- "traefik.http.routers.gitea.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.routers.gitea.service=gitea"
- 'traefik.http.services.gitea.loadbalancer.gitea.port=3000'
- "traefik.http.routers.gitea.tls.domains[0].main=`${GITEA_TLS_DOMAIN_MAIN}`"
# SSH routing, can't route based on host so anything to port 222 will come to this container
- "traefik.tcp.routers.gitea-ssh.rule=HostSNI(`*`)"
- "traefik.tcp.routers.gitea-ssh.entrypoints=ssh"
- "traefik.tcp.routers.gitea-ssh.service=gitea-ssh-svc"
- "traefik.tcp.services.gitea-ssh-svc.loadbalancer.gitea.port=22"
gitea_db:
image: mysql:latest
container_name: ${INFRASTRUCTURE_LABEL:-mindboost}-gitea_db
profiles: ["all", "gitea","develop"]
restart: always
environment:
- MYSQL_ROOT_PASSWORD=${GITEA_MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${GITEA_MYSQL_DATABASE}
- MYSQL_USER=${GITEA_MYSQL_USER}
- MYSQL_PASSWORD=${GITEA_MYSQL_PASSWORD}
volumes:
- ${GITEA_DATABASE_VOLUME_PATH}:/var/lib/mysql
networks:
gitea:

View File

@@ -0,0 +1,40 @@
### Jenkins (./apps/frontend/docker-compose.yml)
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
ports:
- "50000:50000" # Jenkins Agent Port
volumes:
- ../../../volumes/develop/jenkins:/var/jenkins_home
- ./plugins.yml:/usr/share/jenkins/ref/plugins.yml
depends_on:
- jenkins-plugins
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.jenkins.rule=Host(`j.haslach2025.de`)"
- "traefik.http.routers.jenkins.entrypoints=websecure"
- "traefik.http.routers.jenkins.tls=true"
- "traefik.http.routers.jenkins.tls.certresolver=http_resolver"
- "traefik.http.services.jenkins.loadbalancer.server.port=8080" # interner Port von Jenkins
- "traefik.docker.network=proxy"
jenkins-plugins:
image: jenkins/jenkins:lts-jdk17
command: >
jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.yml --available-updates --output txt > /usr/share/jenkins/ref/plugins.yml
volumes:
- ./plugins.yml:/usr/share/jenkins/ref/plugins.yml
restart: "no"
volumes:
jenkins_home:
driver: local
networks:
proxy:
external: true

View File

View File

@@ -0,0 +1,50 @@
##
## ONE SCRIPT TO RULE THEM ALL
##
## Dieses Compose-File startet alle verfügbaren Services, abhängig von dem angegebenen ENVIRONMENT.
## Um diese Konfiguration zu verwenden, kannst du folgende Befehle nutzen:
## Um alle Services zu starten:
## docker compose -f docker-compose.all.yml --env-file ../env/.env.all --profile all up -d
## Um nur bestimmte Services zu starten (z.B. frontend und backend):
## docker compose -f docker-compose.all.yml --env-file ../env/.env.all --profile frontend --profile backend up -d
##
## Stellen Sie sicher, dass die .env.all Datei im angegebenen Verzeichnis existiert und den ENVIRONMENT Wert enthält.
##
configs:
all:
file: ../env/.env.all
include:
- path: ./proxy/docker-compose.yml
env_file:
- ../env/.env.all
- ../env/${ENVIRONMENT:-development}/.env.proxy
- path: ./frontend/docker-compose.yml
env_file:
- ../env/.env.all
- ../env/${ENVIRONMENT:-development}/.env.frontend
- path: ./backend/docker-compose.yml
- path: ./database/docker-compose.yml
- path: ./website/docker-compose.yml
env_file:
- ../env/.env.all
- ../env/${ENVIRONMENT:-development}/.env.website
- ../env/${ENVIRONMENT:-development}/.env.proxy
- path: ./administration/docker-compose.yml
env_file:
- ../env/.env.all
- ../env/${ENVIRONMENT:-development}/.env.administration
- ../env/${ENVIRONMENT:-development}/.env.proxy
- path: ./develop/docker-compose.yml
env_file:
- ../env/.env.all
- ../env/${ENVIRONMENT:-development}/.env.develop
- ../env/${ENVIRONMENT:-development}/.env.proxy
- path: ./tools/docker-compose.yml
env_file:
- ../env/.env.all
- ../env/${ENVIRONMENT:-development}/.env.tools
- ../env/${ENVIRONMENT:-development}/.env.proxy

View File

@@ -1,65 +0,0 @@
##
## DIESES COMPOSE FILE IST FÜR DIE LOKALE ENTWICKLUNG MITTELS DOCKER
##
## Der Inhalt von frontend und von backend wird über ein volume eingebunden, dass
## bedeutet Änderungen innerhalb der Projektordner ./frontend/src und ./backend/src
## Ändern direkt die Werte innerhalb des Containers wie z.B. das Austauschen einer Grafik.
##
## Datenbank ebenfalls lokal und KEIN reverse-Proxy (traefik)
## Image der DB ist auf ARM Archtektur (Apple Silicon) ausgelegt
##
services:
mariadb:
image: mariadb:latest
container_name: local_mariadb
command: --bind-address=0.0.0.0
environment:
- ALLOW_EMPTY_PASSWORD
- MARIADB_USER=mindboost
- MARIADB_DATABASE=mindboost
- MARIADB_PASSWORD=mindboost
- MARIADB_ROOT_PASSWORD=root-mindboost
volumes:
- ../volumes/daten/mariadb:/var/lib/mysql
networks:
- backend
frontend:
build:
context: ./frontend/src
dockerfile: Dockerfile.dev
container_name: local_frontend
volumes:
- ./frontend/src:/app
- /app/node_modules
ports:
- "3000:3000"
networks:
- backend
environment:
NODE_ENV: development
backend:
build:
context: ./backend/src
dockerfile: Dockerfile.dev
container_name: local_backend
ports:
- "8000:8000"
- "5173:5173"
volumes:
- ./backend/src:/var/www
networks:
- backend
depends_on:
- mariadb
adminer:
image: adminer
container_name: local_adminer
restart: always
ports:
- 8080:8080
networks:
- backend
networks:
backend:
external: false

View File

@@ -1,107 +0,0 @@
##
## DIESES COMPOSE FILE IST FÜR DIE LOKALE ENTWICKLUNG MITTELS DOCKER
##
## Der Inhalt von frontend und von backend wird über ein volume eingebunden, dass
## bedeutet Änderungen innerhalb der Projektordner ./frontend/src und ./backend/src
## Ändern direkt die Werte innerhalb des Containers wie z.B. das Austauschen einer Grafik.
##
## Datenbank ebenfalls lokal und KEIN reverse-Proxy (traefik)
## Image der DB ist auf ARM Archtektur (Apple Silicon) ausgelegt
##
services:
prod-mariadb:
image: mariadb:latest
container_name: prod-mariadb
hostname: mariadb
command: --bind-address=0.0.0.0
env_file:
- ../config/.env.db
networks:
- ${BACKEND_NETWORK}
volumes:
- ../volumes/daten/mariadb:/var/lib/mysql
prod-redis:
image: redis:alpine
container_name: prod-redis
hostname: redis
networks:
- ${BACKEND_NETWORK}
restart: unless-stopped
command: redis-server --appendonly yes --requirepass laravel-redis-passwort # Redis Passwort eingeben
volumes:
- ../volumes/daten/redis:/data
prod-frontend:
build:
context: ./frontend/src
dockerfile: Dockerfile
container_name: prod-frontend
networks:
- ${BACKEND_NETWORK}
- ${TRAEFIK_NETWORK}
env_file:
- ../config/.env.frontend
- ../config/.env.traefik
labels:
- "traefik.enable=${TRAEFIK_ENABLE}"
- "traefik.http.routers.prod-frontend.entrypoints=${TRAEFIK_ROUTER_FRONTEND_ENTRYPOINT}"
- "traefik.http.routers.prod-frontend.rule=${TRAEFIK_ROUTER_FRONTEND_RULE}"
- "traefik.http.routers.prod-frontend.tls=${TRAEFIK_ROUTER_FRONTEND_TLS}"
- "traefik.http.routers.prod-frontend.tls.certresolver=${TRAEFIK_ROUTER_FRONTEND_CERTRESOLVER}"
- "traefik.http.routers.prod-frontend.tls.domains[0].main=${TRAEFIK_ROUTER_FRONTEND_TLS_DOMAIN_MAIN}"
- "traefik.http.routers.prod-frontend.tls.domains[0].sans=${TRAEFIK_ROUTER_FRONTEND_TLS_DOMAIN_SANS}"
- "traefik.http.services.prod-frontend.loadbalancer.server.port=${TRAEFIK_SERVICE_FRONTEND_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
prod-backend:
build:
context: ./backend/src
dockerfile: Dockerfile
env_file:
- ../config/.env.backend
- ../config/.env.traefik
labels:
- "traefik.enable=${TRAEFIK_ENABLE}"
- "traefik.http.routers.prod-backend.entrypoints=${TRAEFIK_ROUTER_BACKEND_ENTRYPOINT}"
- "traefik.http.routers.prod-backend.rule=${TRAEFIK_ROUTER_BACKEND_RULE}"
- "traefik.http.routers.prod-backend.tls=${TRAEFIK_ROUTER_BACKEND_TLS}"
- "traefik.http.routers.prod-backend.tls.certresolver=${TRAEFIK_ROUTER_BACKEND_CERTRESOLVER}"
- "traefik.http.routers.prod-backend.tls.domains[0].main=${TRAEFIK_ROUTER_BACKEND_TLS_DOMAIN_MAIN}"
- "traefik.http.services.prod-backend.loadbalancer.server.port=${TRAEFIK_SERVICE_BACKEND_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
- ${BACKEND_NETWORK}
- ${TRAEFIK_NETWORK}
depends_on:
- prod-mariadb
# Traefik-Crowdsec Stack
crowdsec:
extends:
file: ./proxy/docker-compose.yml
service: crowdsec
networks:
- ${TRAEFIK_NETWORK}
traefik:
extends:
file: ./proxy/docker-compose.yml
service: traefik
networks:
- ${TRAEFIK_NETWORK}
depends_on:
- crowdsec
traefik_crowdsec_bouncer:
extends:
file: ./proxy/docker-compose.yml
service: traefik_crowdsec_bouncer
networks:
- ${TRAEFIK_NETWORK}
depends_on:
- crowdsec
- traefik
networks:
prod-backend:
external: false
proxy:
external: true

View File

@@ -0,0 +1,19 @@
services:
webapp:
build:
context: ./src
dockerfile: Dockerfile
args:
BACKEND_URL: ${BACKEND_URL:-http://localhost:8000} # this argument is important on build to set the server url!
container_name: ${INFRASTRUCTURE_LABEL:-default}-frontend-${ENVIRONMENT:-development}
profiles: ["webapp", "frontend", "all", "app"]
ports:
- 3000:3000
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.webapp.service=webapp"
- "traefik.http.routers.webapp.entrypoints=${TRAEFIK_ENTRYPOINT}"
- 'traefik.http.routers.webapp.rule=Host(`${FRONTEND_DOMAIN}`) || Host(`${FRONTEND_DOMAIN_2}`)'
- "traefik.http.services.webapp.loadbalancer.server.port=3000"
- "traefik.docker.network=${TRAEFIK_NETWORK}"

View File

@@ -0,0 +1,17 @@
services:
webapp:
build:
context: ./src
dockerfile: Dockerfile
args:
BACKEND_URL: ${BACKEND_URL:-http://localhost:8000} # this argument is important on build to set the backend server url!
container_name: ${INFRASTRUCTURE_LABEL:-default}-frontend-${ENVIRONMENT:-development}
profiles: ["webapp", "frontend", "all", "app"]
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.http.routers.webapp.service=webapp"
- "traefik.http.routers.webapp.entrypoints=${TRAEFIK_ENTRYPOINT}"
- 'traefik.http.routers.webapp.rule=Host(`${FRONTEND_DOMAIN}`) || Host(`${FRONTEND_DOMAIN_2}`)'
- "traefik.http.services.webapp.loadbalancer.server.port=3000"
- "traefik.docker.network=${TRAEFIK_NETWORK}"

View File

@@ -0,0 +1,11 @@
# MaxMind (create a free GeoLite2 license key in your MaxMind account)
MAXMIND_LICENSE_KEY=your_maxmind_license_key
# PeeringDB (optional; reduces rate limits)
PDB_API_KEY=your_peeringdb_api_key
# existing Traefik/proxy network name (must already exist)
PROXY_NETWORK=proxy
# update interval in seconds (30 days)
UPDATE_INTERVAL_SECONDS=2592000

View File

@@ -0,0 +1 @@
.env

View File

@@ -0,0 +1,16 @@
FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY main.go ./
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/asn-header-service
FROM alpine:3.20
RUN adduser -D -H -u 10001 app
USER 10001
WORKDIR /app
COPY --from=build /out/asn-header-service /app/asn-header-service
EXPOSE 8080
ENV ADDR=:8080
ENTRYPOINT ["/app/asn-header-service"]

View File

@@ -0,0 +1,89 @@
# NREN / ASN Detection Service
Dieses Projekt stellt einen **minimalen Microservice** bereit, um **Hochschul- und Forschungsnetzwerke (NRENs)** anhand der **Autonomous System Number (ASN)** zu erkennen.
Der Zweck ist es, **Anfragen aus Hochschulnetzen (z. B. eduroam)** zu identifizieren, um **Research-bezogene Services kostenlos oder bevorzugt bereitzustellen**.
Das System dient ausschließlich der **Netzwerk-Klassifikation** und **ersetzt keine Authentifizierung**.
---
## Ziel
- Erkennen, ob eine Anfrage aus einem **Hochschul- oder Forschungsnetz** stammt
- Bereitstellung eines **Header-Hinweises** für nachgelagerte Services
- Grundlage für Entscheidungen wie:
- kostenfreie Research-Features
- angepasste UI-Hinweise
- alternative Rate-Limits
---
## Funktionsweise (Kurzfassung)
```
Client
→ Traefik
→ ForwardAuth
→ ASN Detection Service
→ Header wird ergänzt
```
1. Die Client-IP wird ermittelt
2. Die zugehörige ASN wird lokal nachgeschlagen
3. Die ASN wird mit einer NREN-ASN-Liste verglichen
4. Das Ergebnis wird als HTTP-Header zurückgegeben
---
## Datenquellen
- **GeoLite2 ASN (MaxMind)**
- kostenlos
- lokal
- monatliche Aktualisierung
- **NREN-ASN-Liste**
- abgeleitet aus PeeringDB
- Kategorie: `Research and Education`
- monatliche Aktualisierung
---
## Bereitgestellte Header
| Header | Beschreibung |
|------|-------------|
| `X-ASN` | ASN der Client-IP |
| `X-ASN-ORG` | Organisation (optional) |
| `X-NREN` | `1` wenn ASN zu einem Hochschul-/Forschungsnetz gehört, sonst `0` |
---
## Integration
Der Service wird als **Traefik ForwardAuth Middleware** eingebunden.
Die Header werden über `authResponseHeaders` an die eigentliche Anwendung weitergereicht.
Der Service ist **nicht öffentlich exponiert** und kommuniziert ausschließlich über das interne Docker-Netzwerk.
---
## Update-Strategie
- monatliche Aktualisierung der ASN-Daten
- keine externen Requests während der Anfrageverarbeitung
---
## Einschränkungen
- Die Erkennung ist **heuristisch**
- Es gibt **keine Garantie**, dass jede Anfrage aus einem Hochschulnetz erkannt wird
- Die Information darf **nicht als Authentifizierungsmerkmal** verwendet werden
---
## Zusammenfassung
Dieses Projekt ermöglicht eine **performante, datenschutzfreundliche Erkennung von Hochschulnetzen**, um **Research-Angebote kontextabhängig bereitzustellen**, ohne Nutzer zu identifizieren oder externe Dienste zur Laufzeit zu kontaktieren.

View File

@@ -0,0 +1,89 @@
# NREN / ASN Detection Service
Dieses Projekt stellt einen **minimalen Microservice** bereit, um **Hochschul- und Forschungsnetzwerke (NRENs)** anhand der **Autonomous System Number (ASN)** zu erkennen.
Der Zweck ist es, **Anfragen aus Hochschulnetzen (z. B. eduroam)** zu identifizieren, um **Research-bezogene Services kostenlos oder bevorzugt bereitzustellen**.
Das System dient ausschließlich der **Netzwerk-Klassifikation** und **ersetzt keine Authentifizierung**.
---
## Ziel
- Erkennen, ob eine Anfrage aus einem **Hochschul- oder Forschungsnetz** stammt
- Bereitstellung eines **Header-Hinweises** für nachgelagerte Services
- Grundlage für Entscheidungen wie:
- kostenfreie Research-Features
- angepasste UI-Hinweise
- alternative Rate-Limits
---
## Funktionsweise (Kurzfassung)
```
Client
→ Traefik
→ ForwardAuth
→ ASN Detection Service
→ Header wird ergänzt
```
1. Die Client-IP wird ermittelt
2. Die zugehörige ASN wird lokal nachgeschlagen
3. Die ASN wird mit einer NREN-ASN-Liste verglichen
4. Das Ergebnis wird als HTTP-Header zurückgegeben
---
## Datenquellen
- **GeoLite2 ASN (MaxMind)**
- kostenlos
- lokal
- monatliche Aktualisierung
- **NREN-ASN-Liste**
- abgeleitet aus PeeringDB
- Kategorie: `Research and Education`
- monatliche Aktualisierung
---
## Bereitgestellte Header
| Header | Beschreibung |
|------|-------------|
| `X-ASN` | ASN der Client-IP |
| `X-ASN-ORG` | Organisation (optional) |
| `X-NREN` | `1` wenn ASN zu einem Hochschul-/Forschungsnetz gehört, sonst `0` |
---
## Integration
Der Service wird als **Traefik ForwardAuth Middleware** eingebunden.
Die Header werden über `authResponseHeaders` an die eigentliche Anwendung weitergereicht.
Der Service ist **nicht öffentlich exponiert** und kommuniziert ausschließlich über das interne Docker-Netzwerk.
---
## Update-Strategie
- monatliche Aktualisierung der ASN-Daten
- keine externen Requests während der Anfrageverarbeitung
---
## Einschränkungen
- Die Erkennung ist **heuristisch**
- Es gibt **keine Garantie**, dass jede Anfrage aus einem Hochschulnetz erkannt wird
- Die Information darf **nicht als Authentifizierungsmerkmal** verwendet werden
---
## Zusammenfassung
Dieses Projekt ermöglicht eine **performante, datenschutzfreundliche Erkennung von Hochschulnetzen**, um **Research-Angebote kontextabhängig bereitzustellen**, ohne Nutzer zu identifizieren oder externe Dienste zur Laufzeit zu kontaktieren.

View File

@@ -0,0 +1,36 @@
services:
asn-header:
build: .
container_name: asn-header
restart: unless-stopped
env_file: .env
environment:
MMDB_PATH: /data/GeoLite2-ASN.mmdb
ASN_LIST_PATH: /data/nren_asns.txt
ADDR: ":8080"
volumes:
- asn_data:/data:ro
networks:
- proxy
asn-updater:
build: ./asn-updater
container_name: asn-updater
restart: unless-stopped
env_file: .env
environment:
OUT_DIR: /data
PDB_INFO_TYPE: "Research and Education"
INTERVAL_SECONDS: "${UPDATE_INTERVAL_SECONDS}"
volumes:
- asn_data:/data
networks:
- proxy
networks:
proxy:
external: true
name: ${PROXY_NETWORK}
volumes:
asn_data:

View File

@@ -0,0 +1,6 @@
module asn-header-service
go 1.22
require github.com/oschwald/maxminddb-golang v1.13.1

View File

@@ -0,0 +1,158 @@
package main
import (
"bufio"
"log"
"net"
"net/http"
"os"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/oschwald/maxminddb-golang"
)
type asnRecord struct {
ASN uint `maxminddb:"autonomous_system_number"`
Org string `maxminddb:"autonomous_system_organization"`
}
type server struct {
db *maxminddb.Reader
nrenASNs map[uint]struct{}
ready atomic.Bool
versionTag string
}
func loadASNSet(path string) (map[uint]struct{}, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
set := make(map[uint]struct{}, 4096)
sc := bufio.NewScanner(f)
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
v, err := strconv.ParseUint(line, 10, 32)
if err != nil {
continue
}
set[uint(v)] = struct{}{}
}
return set, sc.Err()
}
func firstForwardedFor(r *http.Request) string {
xff := r.Header.Get("X-Forwarded-For")
if xff == "" {
return ""
}
parts := strings.Split(xff, ",")
if len(parts) == 0 {
return ""
}
return strings.TrimSpace(parts[0])
}
func remoteIP(r *http.Request) string {
// Prefer XFF (because Traefik is proxy)
ip := firstForwardedFor(r)
if ip != "" {
return ip
}
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil {
return host
}
return r.RemoteAddr
}
func (s *server) authHandler(w http.ResponseWriter, r *http.Request) {
if !s.ready.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
ipStr := remoteIP(r)
parsed := net.ParseIP(ipStr)
if parsed == nil {
// Always 200: we enrich, not block
w.Header().Set("X-NREN", "0")
w.WriteHeader(http.StatusOK)
return
}
var rec asnRecord
if err := s.db.Lookup(parsed, &rec); err != nil || rec.ASN == 0 {
w.Header().Set("X-NREN", "0")
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("X-ASN", strconv.FormatUint(uint64(rec.ASN), 10))
if rec.Org != "" {
// optional: keep it short; some org strings can be long
w.Header().Set("X-ASN-ORG", rec.Org)
}
_, ok := s.nrenASNs[rec.ASN]
if ok {
w.Header().Set("X-NREN", "1")
} else {
w.Header().Set("X-NREN", "0")
}
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("X-Service", s.versionTag)
w.WriteHeader(http.StatusOK)
}
func main() {
mmdbPath := getenv("MMDB_PATH", "/data/GeoLite2-ASN.mmdb")
asnListPath := getenv("ASN_LIST_PATH", "/data/nren_asns.txt")
addr := getenv("ADDR", ":8080")
version := getenv("VERSION_TAG", "asn-header-service")
db, err := maxminddb.Open(mmdbPath)
if err != nil {
log.Fatalf("failed to open mmdb: %v", err)
}
defer db.Close()
set, err := loadASNSet(asnListPath)
if err != nil {
log.Fatalf("failed to load asn list: %v", err)
}
s := &server{db: db, nrenASNs: set, versionTag: version}
s.ready.Store(true)
mux := http.NewServeMux()
mux.HandleFunc("/auth", s.authHandler)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })
srv := &http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: 2 * time.Second,
}
log.Printf("listening on %s (asn_count=%d)", addr, len(set))
log.Fatal(srv.ListenAndServe())
}
func getenv(k, def string) string {
v := strings.TrimSpace(os.Getenv(k))
if v == "" {
return def
}
return v
}

View File

@@ -0,0 +1,30 @@
services:
wireguard:
image: linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- SERVERURL=${SERVER_IP:?"❌ ERROR = SERVERURL is not set. Run set-server-ip.sh first."}
- SERVERPORT=51820
- PEERS=3 # Number of VPN clients to generate
- PEERDNS=auto
- INTERNAL_SUBNET=22.22.22.0
volumes:
- ../../volumes/security/wireguard/config:/config
- /lib/modules:/lib/modules
ports:
- "51820:51820/udp"
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
networks:
- wireguard_network
networks:
wireguard_network:
driver: bridge

View File

@@ -0,0 +1,50 @@
volumes:
etc_wireguard:
services:
wg-easy:
environment:
# Change Language:
# (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi, ja, si)
- LANG=${WG_LANG:-de}
# ⚠️ Required:
# Change this to your host's public address
- WG_HOST=${SERVER_IP:-localhost}
# Optional:
# - PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG # (needs double $$, hash of 'foobar123'; see "How_to_generate_an_bcrypt_hash.md" for generate the hash)
# - PORT=51821
# - WG_PORT=51820
# - WG_CONFIG_PORT=92820
- WG_DEFAULT_ADDRESS=${WG_DEFAULT_ADDRESS:-22.22.22.0}
# - WG_DEFAULT_DNS=1.1.1.1
# - WG_MTU=1420
# - WG_ALLOWED_IPS=192.168.15.0/24, 10.0.1.0/24
# - WG_PERSISTENT_KEEPALIVE=25
# - WG_PRE_UP=echo "Pre Up" > /etc/wireguard/pre-up.txt
# - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt
# - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt
# - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt
# - UI_TRAFFIC_STATS=true
# - UI_CHART_TYPE=0 # (0 Charts disabled, 1 # Line chart, 2 # Area chart, 3 # Bar chart)
# - WG_ENABLE_ONE_TIME_LINKS=true
# - UI_ENABLE_SORT_CLIENTS=true
# - WG_ENABLE_EXPIRES_TIME=true
# - ENABLE_PROMETHEUS_METRICS=false
# - PROMETHEUS_METRICS_PASSWORD=$$2a$$12$$vkvKpeEAHD78gasyawIod.1leBMKg8sBwKW.pQyNsq78bXV3INf2G # (needs double $$, hash of 'prometheus_password'; see "How_to_generate_an_bcrypt_hash.md" for generate the hash)
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
volumes:
- ../../volumes/wireguardeasy/:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
# - NET_RAW # ⚠️ Uncomment if using Podman
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1

View File

@@ -0,0 +1,2 @@
#!/bin/bash
export SERVER_IP=$(curl -s https://api.ipify.org)

View File

@@ -0,0 +1,11 @@
### Tools (./apps/tools/docker-compose.yml)
# - [ ] Create services for Nextcloud, LimeSurvey, and LinkStack
# - [ ] Configure volumes for persistent storage of files, survey data, and link management data
# - [ ] Set up environment variables using the new structure (../../env/${ENVIRONMENT:-development}/tools.env)
# - [ ] Configure networking to expose these services to the internet via the proxy
# - [ ] Set up regular backup jobs for critical data in these services
include:
- path: ./nextcloud/docker-compose.yml
- path: ./limesurvey/docker-compose.yml
- path: ./invoiceninja/dockerfiles/debian/docker-compose.yml

View File

View File

@@ -0,0 +1,59 @@
services:
nextcloud-db:
image: mariadb:10.6
container_name: ${INFRASTRUCTURE_LABEL:-default}-nextcloud-db-${ENVIRONMENT:-development}
profiles: ["all", "tools", "nextcloud"]
command: --transaction-isolation=READ-COMMITTED --innodb_read_only_compressed=OFF
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ../../volumes/tools/${INFRASTRUCTURE_LABEL:-default}_cloud/database:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=headpiece-constant1-denim-mindboost #SQL root Passwort eingeben
- MYSQL_PASSWORD=idealist9-frayed-murkiness-mindboost #SQL Benutzer Passwort eingeben
- MYSQL_DATABASE=nextcloud-mindboost #Datenbank Name
- MYSQL_USER=mindboostcloud #SQL Nutzername
- MYSQL_INITDB_SKIP_TZINFO=1
- MARIADB_AUTO_UPGRADE=1
nextcloud-redis:
image: redis:alpine
container_name: ${INFRASTRUCTURE_LABEL:-default}-nextcloud-redis-${ENVIRONMENT:-development}
profiles: ["all", "tools", "nextcloud"]
hostname: nextcloud-redis
restart: unless-stopped
command: redis-server --requirepass redis-mindboost-passwort # Redis Passwort eingeben
cloud:
image: nextcloud
container_name: ${INFRASTRUCTURE_LABEL:-default}-nextcloud-app-${ENVIRONMENT:-development}
profiles: ["all", "tools", "nextcloud"]
restart: unless-stopped
depends_on:
- nextcloud-db
- nextcloud-redis
environment:
TRUSTED_PROXIES: 172.16.255.254/16
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://${CLOUD_DOMAIN:-cloud}
OVERWRITEHOST: ${CLOUD_DOMAIN:-cloud}
REDIS_HOST: nextcloud-redis
REDIS_HOST_PASSWORD: redis-mindboost-passwort # Redis Passwort von oben wieder eingeben
volumes:
- ../../volumes/tools/${INFRASTRUCTURE_LABEL:-default}_cloudapp/:/var/www/html/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_cloud.entrypoints=websecure"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_cloud.rule=Host(`${CLOUD_DOMAIN}`)"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_cloud.tls=true"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_cloud.tls.certresolver=http_resolver"
- 'traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_cloud.service=cloud'
- "traefik.http.services.cloud.loadbalancer.server.port=80"
- "traefik.docker.network=${TRAEFIK_NETWORK:-default}"
- "traefik.http.routers.${INFRASTRUCTURE_LABEL:-default}_cloud.middlewares=nextcloud-dav,default@file"
- "traefik.http.middlewares.nextcloud-dav.replacepathregex.regex=^/.well-known/ca(l|rd)dav"
- "traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement=/remote.php/dav/"
networks:
- ${TRAEFIK_NETWORK}
networks:
nextcloud:
name: ${INFRASTRUCTURE_LABEL:-default}_nextcloud

View File

@@ -0,0 +1,29 @@
services:
kirbycms:
build:
context: ./kirby
dockerfile: Dockerfile
image: kirbycms
container_name: ${INFRASTRUCTURE_LABEL:-default}-kirbycms-${ENVIRONMENT:-development}
profiles: ["website","kirbycms","all"]
volumes:
- kirbycms_data:/var/www/html:rw # Persistente Daten
restart: unless-stopped
ports:
- 0:80
networks:
- ${TRAEFIK_NETWORK:-default}
labels:
- "traefik.enable=${TRAEFIK_ENABLE:-false}"
- "traefik.docker.network=${TRAEFIK_NETWORK:-default}"
- "traefik.http.routers.kirbycms.service=kirbycms"
- "traefik.http.routers.kirbycms.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-default}"
- "traefik.http.routers.kirbycms.tls.domains[0].main=`${WEBSITE_DOMAIN:-kirby.local}`"
- "traefik.http.routers.kirbycms.rule=Host(`${WEBSITE_DOMAIN:-kirby.local}`)"
- "traefik.http.routers.kirbycms.entrypoints=${TRAEFIK_ENTRYPOINT:-default}"
- "traefik.http.routers.kirbycms.tls=true"
- "traefik.http.services.kirbycms.loadbalancer.server.port=80"
volumes:
kirbycms_data:
driver: local

View File

@@ -0,0 +1,49 @@
# Use latest offical ubuntu image
FROM ubuntu:latest
# Set timezone
ENV TZ=Europe/Berlin
# Set geographic area using above variable
# This is necessary, otherwise building the image doesn't work
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Remove annoying messages during package installation
ARG DEBIAN_FRONTEND=noninteractive
# Install packages: web server & PHP plus extensions
RUN apt-get update && apt-get install -y \
apache2 \
apache2-utils \
ca-certificates \
php \
libapache2-mod-php \
php-curl \
php-dom \
php-gd \
php-intl \
php-json \
php-mbstring \
php-xml \
php-zip && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# Copy virtual host configuration from current path onto existing 000-default.conf
COPY default.conf /etc/apache2/sites-available/000-default.conf
# Remove default content (existing index.html)
RUN rm /var/www/html/*
# Activate Apache modules headers & rewrite
RUN a2enmod headers rewrite
# Ensure Group Ownership for www-data every member of kirbygroup should edit files
RUN groupadd -g 1003 kirbygroup && usermod -aG kirbygroup www-data
RUN chown -R www-data:kirbygroup /var/www/html
RUN chmod -R g+rw /var/www/html && find /var/www/html -type d -exec chmod g+xs {} \;
# Tell container to listen to port 80 at runtime
EXPOSE 80
# Start Apache web server
CMD [ "/usr/sbin/apache2ctl", "-DFOREGROUND" ]

View File

@@ -0,0 +1,9 @@
<VirtualHost *:80>
ServerName localhost
# Set the document root
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
# Allow overriding the default configuration via `.htaccess`
AllowOverride All
</Directory>
</VirtualHost>

View File

@@ -0,0 +1,7 @@
#!/bin/bash
set -e -u
[[ $USERID ]] && usermod --uid "${USERID}" www-data
exec "$@"

View File

@@ -0,0 +1 @@
USERID=${USERID:-0}

View File

@@ -1,49 +0,0 @@
# ----------------------------------
# Datenbank (MariaDB)
# ----------------------------------
MARIADB_USER=mindboost
MARIADB_DATABASE=mindboost
MARIADB_PASSWORD=1stronges-mindboostdb-passwort
MARIADB_ROOT_PASSWORD=1stronges-passwort-fuer-diedb
# ----------------------------------
# Redis
# ----------------------------------
REDIS_PASSWORD=laravel-redis-passwort
REDIS_PORT=6379
# ----------------------------------
# Vue Frontend (Nuxt.js)
# ----------------------------------
VUE_APP_BACKEND_HOST_ADDRESS=https://dev.b.mindboost.team
VUE_FRONTEND_PORT=3001
VUE_INTERNAL_PORT=3000
VUE_FRONTEND_DOMAIN_1=app.mindboost.team
VUE_FRONTEND_DOMAIN_2=mindboost.app
# ----------------------------------
# Laravel Backend
# ----------------------------------
BACKEND_NETWORK=backend
APP_NAME="mindboost backend - Compose Deployment"
APP_URL=https://b.mindboost.team
LARAVEL_PORT=8000
LARAVEL_VITE_PORT=5173
DB_HOST=mariadb
DB_PORT=3306
DB_PASSWORD=1stronges-mindboostdb-passwort
DB_USERNAME=mindboost
DB_DATABASE=mindboost
LARAVEL_DOMAIN=b.mindboost.team
JWT_SECRET=zMtO8sgsnc4UixWSsYWE1pK9EdpNLzxNSoIPlUpTe6dDlarM3bu4cwM80tH3jA0F
# ----------------------------------
# Traefik
# ----------------------------------
TRAEFIK_CERT_RESOLVER=http_resolver
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_NETWORK=proxy
# ----------------------------------
# Adminer
# ----------------------------------
ADMINER_PORT=8080

View File

@@ -1 +0,0 @@
DB_HOST= BLALBLAB

View File

@@ -1 +0,0 @@
DB_HOST= BLALBLAB

View File

@@ -1,24 +0,0 @@
# ----------------------------------
# Traefik
# ----------------------------------
# Allgemein
TRAEFIK_ENABLE=true
TRAEFIK_NETWORK=proxy
# Backend
TRAEFIK_ROUTER_BACKEND_ENTRYPOINT=websecure
TRAEFIK_ROUTER_BACKEND_RULE=Host(`b.mindboost.team`)
TRAEFIK_ROUTER_BACKEND_TLS=true
TRAEFIK_ROUTER_BACKEND_CERTRESOLVER=http_resolver
TRAEFIK_ROUTER_BACKEND_TLS_DOMAIN_MAIN=b.mindboost.team
TRAEFIK_SERVICE_BACKEND_PORT=8000
# Frontend
TRAEFIK_ROUTER_FRONTEND_ENTRYPOINT=websecure
TRAEFIK_ROUTER_FRONTEND_RULE=Host(`app.mindboost.team` || `mindboost.app`)
TRAEFIK_ROUTER_FRONTEND_TLS=true
TRAEFIK_ROUTER_FRONTEND_CERTRESOLVER=http_resolver
TRAEFIK_ROUTER_FRONTEND_TLS_DOMAIN_MAIN=app.mindboost.team
TRAEFIK_ROUTER_FRONTEND_TLS_DOMAIN_SANS=mindboost.app
TRAEFIK_SERVICE_FRONTEND_PORT=3000

114
dev-fpm.docker-compose.yml Normal file
View File

@@ -0,0 +1,114 @@
version: "3.8"
services:
mariadb_webapp_dev:
image: docker.io/bitnami/mariadb:11.1
container_name: ${DEV_COMPOSE_PREFIX:-dev}-mariadb
hostname: ${DEV_DB_HOST:-mariadb-webapp-dev}
environment:
MARIADB_USER: ${MARIADB_USER}
MARIADB_DATABASE: ${MARIADB_DATABASE}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
networks:
- dev_backend
volumes:
- mindboost_mariadb_data_dev:/var/lib/mysql
laravel-redis-dev:
image: redis:alpine
container_name: ${DEV_COMPOSE_PREFIX:-dev}-redis
hostname: ${DEV_REDIS_HOST:-laravel-redis-dev}
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
networks:
- dev_backend
restart: unless-stopped
volumes:
- ./data/redis-dev:/data
laravel_backend_dev:
image: ${BACKEND_IMAGE}
container_name: ${DEV_COMPOSE_PREFIX:-dev}-backend
environment:
APP_ENV: ${APP_ENV:-production}
APP_NAME: ${APP_NAME:-Mindboost Backend Dev}
APP_URL: https://${DEV_BACKEND_DOMAIN}
FRONTEND_URL: https://${DEV_FRONTEND_DOMAIN}
DB_CONNECTION: mysql
DB_HOST: ${DEV_DB_HOST:-mariadb-webapp-dev}
DB_PORT: ${DB_PORT:-3306}
DB_DATABASE: ${MARIADB_DATABASE}
DB_USERNAME: ${MARIADB_USER}
DB_PASSWORD: ${MARIADB_PASSWORD}
REDIS_HOST: ${DEV_REDIS_HOST:-laravel-redis-dev}
REDIS_PASSWORD: ${REDIS_PASSWORD}
REDIS_PORT: ${REDIS_PORT:-6379}
CACHE_DRIVER: redis
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
volumes:
- ${BACKEND_CODE_PATH:-./apps/backend/src}:/app
- ${BACKEND_PUBLIC_PATH:-./apps/backend/src/public}:/var/www/public
- ${BACKEND_ENV_FILE:-./env/development/.env.backend}:/var/www/.env
- ./logs/backend-dev:/var/www/storage/logs
depends_on:
- mariadb_webapp_dev
- laravel-redis-dev
networks:
- dev_backend
laravel-nginx-dev:
image: nginx:alpine
container_name: ${DEV_COMPOSE_PREFIX:-dev}-nginx
volumes:
- ./nginx:/etc/nginx/conf.d:ro
- ${BACKEND_PUBLIC_PATH:-./apps/backend/src/public}:/var/www/public:ro
depends_on:
- laravel_backend_dev
labels:
- "traefik.enable=true"
- "traefik.http.routers.dev_backend_http.entrypoints=web"
- "traefik.http.routers.dev_backend_http.rule=Host(`${DEV_BACKEND_DOMAIN}`)"
- "traefik.http.routers.dev_backend_http.middlewares=traefik-https-redirect"
- "traefik.http.routers.dev_backend_https.entrypoints=websecure"
- "traefik.http.routers.dev_backend_https.rule=Host(`${DEV_BACKEND_DOMAIN}`)"
- "traefik.http.routers.dev_backend_https.tls=true"
- "traefik.http.routers.dev_backend_https.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.routers.dev_backend_https.service=dev_backend_service"
- "traefik.http.services.dev_backend_service.loadbalancer.server.port=80"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
- dev_backend
- proxy
nuxt_frontend_dev:
image: ${NUXT_IMAGE}
container_name: ${DEV_COMPOSE_PREFIX:-dev}-frontend
environment:
VUE_APP_BACKEND_HOST_ADDRESS: https://${DEV_BACKEND_DOMAIN}
NUXT_PUBLIC_BACKEND_URL: https://${DEV_BACKEND_DOMAIN}
networks:
- dev_backend
- proxy
depends_on:
- laravel_backend_dev
labels:
- "traefik.enable=true"
- "traefik.http.routers.dev_frontend_http.entrypoints=web"
- "traefik.http.routers.dev_frontend_http.rule=Host(`${DEV_FRONTEND_DOMAIN}`)"
- "traefik.http.routers.dev_frontend_http.middlewares=traefik-https-redirect"
- "traefik.http.routers.dev_frontend_https.entrypoints=websecure"
- "traefik.http.routers.dev_frontend_https.rule=Host(`${DEV_FRONTEND_DOMAIN}`)"
- "traefik.http.routers.dev_frontend_https.tls=true"
- "traefik.http.routers.dev_frontend_https.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
- "traefik.http.services.dev_frontend_https.loadbalancer.server.port=${VUE_INTERNAL_PORT}"
- "traefik.docker.network=${TRAEFIK_NETWORK}"
networks:
dev_backend:
driver: bridge
proxy:
external: true
volumes:
mindboost_mariadb_data_dev:

29
docs/infra.md Normal file
View File

@@ -0,0 +1,29 @@
Infrastructure v2
Goals
- Modular stacks you can pick individually (Nextcloud, etc.)
- Unified reverse proxy (Traefik) with automatic TLS
- Clear env layering and gitignored secrets
- Simple Make targets for a smooth DX
Layout
- infra/core/traefik: Traefik compose + static/dynamic config
- infra/apps/<service>: Selfcontained compose stacks and .env.example
- infra/env/<env>/common.env: Shared environment defaults per environment
- infra/secrets: Local secret files (ignored)
- scripts/infra/bootstrap.sh: Creates proxy network and ACME storage
Usage
1. cp infra/env/development/common.env infra/env/development/common.env (adjust values)
2. make bootstrap
3. make proxy-up
4. make app-up APP=nextcloud
Security
- Do not commit real secrets. Place them in local `.env` files or secret managers.
- Optionally protect Traefik dashboard with basic auth via `TRAEFIK_BASIC_AUTH_USERS`.

39
env/.env.all vendored Normal file
View File

@@ -0,0 +1,39 @@
##
## Einstellung die für das gesamte Projekt gelten. Also der Name und der Admin
## Das Environment muss "production","staging" oder "development" heißen
INFRASTRUCTURE_LABEL=mindboost
ENVIRONMENT=development
ADMIN_USER=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
ADMIN_PASSWORD_HASH='$2y$05$U7noO29Ru/4VB5x8TpZo3.b4VjH6AAnhufJJUG2Vs7qHCM2Cd8yIK' # for development = admin
SERVER_IP=127.0.0.1
#################################################################################################
# 🔧 ENVIRONMENT VARIABLES 🔧 #
#################################################################################################
#
# This file contains **default (fallback) values** for environment variables.
# These values ensure that services run with sane defaults if no other configuration is provided.
#
# 📌 **ENVIRONMENT VARIABLE PRIORITY ORDER (Lowest to Highest)**
# 1⃣ **Fallback Values in the File** (Used only if no other source provides a value)
# 2⃣ **Global Defaults in `.env.all`** (Shared settings across all services)
# 3⃣ **Service-Specific `.env` Files** (Overrides per service group, e.g., `.env.backend`, `.env.proxy`)
# 4⃣ **Preloaded Shell Environment** (`export VAR=value` before running `docker compose`)
# 5⃣ **CLI Overrides** (`docker compose --env-file` or `-e VAR=value` → Highest Priority)
#
# 🔄 **Overwriting Behavior**
# - Variables defined in **`.env.all`** override values in this file.
# - Variables defined in **`.env.<service>`** (e.g., `.env.backend`) override `.env.all`.
# - Variables explicitly **exported in the shell** take priority over all `.env` files.
# - Variables passed via **CLI (`--env-file` or `-e VAR=value`)** have the **highest priority**.
#
# 🚀 **Key Takeaways**
# ✅ Use `.env.all` for common values across environments.
# ✅ Use `.env.<service>` for service-specific configurations.
# ✅ If needed, manually override variables in the shell or CLI.
#
#################################################################################################

50
env/README.md vendored Normal file
View File

@@ -0,0 +1,50 @@
# 🔧 Environment Configuration Guide
## 🌍 Overview
This project uses **environment variables** to manage configuration across different environments (development, staging, production, etc.). These variables are loaded from `.env` files and can be overridden at multiple levels.
---
## 📌 **Environment Variable Priority (Lowest to Highest)**
| 🔢 Priority | 📄 Source | 🔍 Description |
|------------|-----------------------------|------------------------------------------------|
| 1**Fallback Values** | hardcoded defaults | Used only if no other configuration is provided |
| 2**Global Defaults** | `.env.all` | Shared settings for all services |
| 3**Service-Specific Overrides** | `.env.backend`, `.env.proxy`, etc. | Overrides `.env.all` with service-specific values |
| 4**Shell Environment Variables** | `export VAR=value` before running | Takes precedence over `.env` files |
| 5**CLI Overrides** | `docker compose --env-file` or `-e VAR=value` | **Highest priority** (for temporary overrides) |
---
## 🔄 **Overwriting Behavior**
- 🏗 **Variables defined in `.env.all`** override fallback values.
- 🏗 **Variables defined in `.env.<service>`** (e.g., `.env.backend`) override `.env.all`.
- 🔧 **Manually exported environment variables** in the shell take priority over `.env` files.
- 🚀 **Variables passed via CLI (`--env-file` or `-e VAR=value`)** override everything.
---
## 🚀 **Best Practices**
✔️ **Use `.env.all` for global configurations** (e.g., `ENVIRONMENT=development`, `INFRASTRUCTURE_LABEL=myinfra`).
✔️ **Use `.env.<service>` for service-specific configurations** (e.g., `.env.backend` for Laravel, `.env.database` for MariaDB).
✔️ **If needed, manually override variables in the shell** using `export VAR=value`.
✔️ **Use CLI `--env-file` for temporary overrides** in testing/debugging scenarios.
---
## 🏗 **Example File Structure**
```sh
/env/
├── .env.all # Global default variables
├── development/
│ ├── .env.backend # Backend service config for development
│ ├── .env.database # Database config for development
│ ├── .env.proxy # Proxy config for development
├── staging/
│ ├── .env.backend # Backend service config for staging
│ ├── .env.database # Database config for staging
├── production/
│ ├── .env.backend # Backend service config for production
│ ├── .env.database # Database config for production

7
env/development/.env.administration vendored Normal file
View File

@@ -0,0 +1,7 @@
# ----------------------------------
# Portainer
# ----------------------------------
PORTAINER_IMAGE=portainer/portainer-ce:latest
PORTAINER_DATA_PATH=../../../volumes/administration/portainer/data

31
env/development/.env.backend vendored Normal file
View File

@@ -0,0 +1,31 @@
# ----------------------------------
# Redis
# ----------------------------------
REDIS_PASSWORD=laravel-redis-passwort
REDIS_PORT=6379
SERVER_IP=${SERVER_IP:-localhost}
# ----------------------------------
# Laravel Backend
# ----------------------------------
BACKEND_NETWORK=backend
APP_ENV=${ENVIRONMENT-local}
APP_NAME="mindboost backend - Compose Deployment"
APP_URL=https://backend.local
LARAVEL_PORT=8000
LARAVEL_VITE_PORT=5173
JWT_SECRET=zMtO8sgsnc4UixWSsYWE1pK9EdpNLzxNSoIPlUpTe6dDlarM3bu4cwM80tH3jA0F
# ----------------------------------
# Datenbank Zugriff - ! MUSS MIT .env.database übereinstimmen
# ----------------------------------
DB_HOST=database
DB_PORT=3306
DB_PASSWORD=1stronges-mindboostdb-passwort
DB_USERNAME=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
DB_DATABASE=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}

9
env/development/.env.database vendored Normal file
View File

@@ -0,0 +1,9 @@
# ----------------------------------
# Datenbank (MariaDB)
# ----------------------------------
MARIADB_USER=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_DATABASE=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_ROOT_PASSWORD_FILE=/run/secrets/mariadb_root
MARIADB_PASSWORD=1stronges-mindboostdb-passwort
MARIADB_PORT=3306
MARIADB_HOST=database

25
env/development/.env.develop vendored Normal file
View File

@@ -0,0 +1,25 @@
# ----------------------------------
# GITEA
# ----------------------------------
USER_UID=1000
USER_GID=1000
GITEA_VOLUME_PATH=../../../volumes/develop/gitea/gitea
GITEA_DATABASE_VOLUME_PATH=../../../volumes/develop/gitea/gitea_db
GITEA_MYSQL_ROOT_PASSWORD=very-difficult-passwort-gitea
GITEA_MYSQL_USER=gitea
GITEA_MYSQL_PASSWORD=very-difficult-gitea
GITEA_MYSQL_DATABASE=gitea
GITEA_MYSQL_ALLOW_EMPTY_PASSWORD=true
# ----------------------------------
# GITEA DB
# ----------------------------------
DB_HOST=gitea_db:3306
DB_NAME=gitea
DB_PASSWD=very-difficult-gitea
DB_TYPE=mysql
DB_USER=gitea

4
env/development/.env.frontend vendored Normal file
View File

@@ -0,0 +1,4 @@
# ----------------------------------
# VUE APP
# ----------------------------------
BACKEND_URL="backend.local"

51
env/development/.env.proxy vendored Normal file
View File

@@ -0,0 +1,51 @@
# ----------------------------------
# TRAEFIK
# ----------------------------------
TRAEFIK_ENABLE=true
TRAEFIK_NETWORK=proxy
TRAEFIK_BASIC_AUTH_USERS=${ADMIN_USER}:${ADMIN_PASSWORD_HASH}
TRAEFIK_CERT_RESOLVER=
## Domains when TRAEFIK is ENABLED
PORTAINER_DOMAIN=portainer.local
FRONTEND_DOMAIN=frontend.local
FRONTEND_DOMAIN_2=app.frontend.local
BACKEND_DOMAIN=backend.local
WEBSITE_DOMAIN=web.local
ADMINER_DOMAIN=adminer.local
GITEA_DOMAIN=gitea.local
LIMESURVEY_DOMAIN=survey.local
LINKSTACK_DOMAIN=linkstack.local
TRAEFIK_DOMAIN=traefik.local
CLOUD_DOMAIN=cloud.local
KILLBILL_DOMAIN=killbill.local
### TLS for Domains
PORTAINER_TLS_DOMAIN_MAIN=${PORTAINER_DOMAIN}
FRONTEND_TLS_DOMAIN_MAIN=${FRONTEND_DOMAIN}
FRONTEND_TLS_DOMAIN_SANS=${FRONTEND_DOMAIN_2}
BACKEND_TLS_DOMAIN_MAIN=${BACKEND_DOMAIN}
WEBSITE_TLS_DOMAIN_MAIN=${WEBSITE_DOMAIN}
GITEA_TLS_DOMAIN_MAIN=${GITEA_DOMAIN}
LIMESURVEY_TLS_DOMAIN_MAIN=${LIMESURVEY_DOMAIN}
LINKSTACK_TLS_DOMAIN_MAIN=${LINKSTACK_DOMAIN}
TRAEFIK_TLS_DOMAIN_MAIN=${TRAEFIK_DOMAIN}
CLOUD_TLS_DOMAIN_MAIN=${CLOUD_DOMAIN}
KILLBILL_TLS_DOMAIN_MAIN=${KILLBILL_DOMAIN}
## MIDDLEWARES
TRAEFIK_HTTPS_REDIRECT_MIDDLEWARE=${INFRASTRUCTURE_LABEL:-default}-https-redirect
TRAEFIK_BASIC_AUTH_MIDDLEWARE=${INFRASTRUCTURE_LABEL:-default}-basic-auth
## ENTRYPOINTS
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_ENTRYPOINT_HTTP=web

29
env/development/.env.tools vendored Normal file
View File

@@ -0,0 +1,29 @@
# ----------------------------------
# NEXTCLOUD DB
# ----------------------------------
MYSQL_ROOT_PASSWORD=headpiece-constant1-denim-mindboost #SQL root Passwort eingeben
MYSQL_PASSWORD=idealist9-frayed-murkiness-mindboost #SQL Benutzer Passwort eingeben
MYSQL_DATABASE=nextcloud-mindboost #Datenbank Name
MYSQL_USER=mindboostcloud #SQL Nutzername
MYSQL_INITDB_SKIP_TZINFO=1
MARIADB_AUTO_UPGRADE=1
# ----------------------------------
# NEXTCLOUD CLOUD
# ----------------------------------
TRUSTED_PROXIES=172.16.255.254/16
OVERWRITEPROTOCOL=https
OVERWRITECLIURL=https://${CLOUD_DOMAIN:-cloud}
OVERWRITEHOST=${CLOUD_DOMAIN:-cloud}
REDIS_HOST=nextcloud-redis
REDIS_HOST_PASSWORD=redis-mindboost-passwort
# ----------------------------------
# KILLBILL PAYMENT
# ----------------------------------
KILLBILL_DAO_URL=jdbc:mysql://db:3306/killbill
KILLBILL_DAO_USER=${ADMIN_USER:-root}
KILLBILL_DAO_PASSWORD=${ADMIN_PASSWORD_HASH}

5
env/development/.env.website vendored Normal file
View File

@@ -0,0 +1,5 @@
# ----------------------------------
# KIRBY CMS
# ----------------------------------
USER_ID=0

31
env/development/portainer/backend.env vendored Normal file
View File

@@ -0,0 +1,31 @@
# ----------------------------------
# Redis
# ----------------------------------
REDIS_PASSWORD=laravel-redis-passwort
REDIS_PORT=6379
SERVER_IP=${SERVER_IP:-localhost}
# ----------------------------------
# Laravel Backend
# ----------------------------------
BACKEND_NETWORK=backend
APP_ENV=${ENVIRONMENT-local}
APP_NAME="mindboost backend - Compose Deployment"
APP_URL=https://backend.local
LARAVEL_PORT=8000
LARAVEL_VITE_PORT=5173
JWT_SECRET=zMtO8sgsnc4UixWSsYWE1pK9EdpNLzxNSoIPlUpTe6dDlarM3bu4cwM80tH3jA0F
# ----------------------------------
# Datenbank Zugriff - ! MUSS MIT .env.database übereinstimmen
# ----------------------------------
DB_HOST=database
DB_PORT=3306
DB_PASSWORD=1stronges-mindboostdb-passwort
DB_USERNAME=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
DB_DATABASE=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}

0
env/production/.env.administration vendored Normal file
View File

1
env/production/.env.backend vendored Normal file
View File

@@ -0,0 +1 @@
${REDIS_PASSWORD}

View File

@@ -1,8 +1,7 @@
# ---------------------------------- # ----------------------------------
# Datenbank (MariaDB) # Datenbank (MariaDB)
# ---------------------------------- # ----------------------------------
MARIADB_USER=mindboost MARIADB_USER=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_DATABASE=mindboost MARIADB_DATABASE=${INFRASTRUCTURE_LABEL:-default}_${ENVIRONMENT:-development}
MARIADB_PASSWORD=1stronges-mindboostdb-passwort MARIADB_PASSWORD=1stronges-mindboostdb-passwort
MARIADB_ROOT_PASSWORD=1stronges-passwort-fuer-diedb MARIADB_ROOT_PASSWORD=1stronges-passwort-fuer-diedb
ADMINER_PORT=8000

1
env/production/.env.develop vendored Normal file
View File

@@ -0,0 +1 @@
ADMINER_PORT=8000

0
env/production/.env.frontend vendored Normal file
View File

3
env/production/.env.portainer vendored Normal file
View File

@@ -0,0 +1,3 @@
PORTAINER_IMAGE=portainer/portainer-ce:latest
PORTAINER_DATA_PATH=/opt/containers/portainer/data
PORTAINER_DOMAIN=portainer.yourdomain.com

32
env/production/.env.proxy vendored Normal file
View File

@@ -0,0 +1,32 @@
TRAEFIK_HTTPS_REDIRECT_MIDDLEWARE=${INFRASTRUCTURE_LABEL:-default}-https-redirect
TRAEFIK_BASIC_AUTH_MIDDLEWARE=${INFRASTRUCTURE_LABEL:-default}-basic-auth
TRAEFIK_BASIC_AUTH_USERS=${ADMIN_USER}:${ADMIN_PASSWORD_HASH}
# Service Crowdsec
SERVICES_CROWDSEC_CONTAINER_NAME=crowdsec
SERVICES_CROWDSEC_HOSTNAME=crowdsec
SERVICES_CROWDSEC_IMAGE=crowdsecurity/crowdsec
SERVICES_CROWDSEC_IMAGE_VERSION=latest
SERVICES_CROWDSEC_NETWORKS_CROWDSEC_IPV4=172.31.254.254
# Service Traefik
SERVICES_TRAEFIK_CONTAINER_NAME=${INFRASTRUCTURE_LABEL:-default}-traefik
SERVICES_TRAEFIK_HOSTNAME=${INFRASTRUCTURE_LABEL:-default}-traefik
SERVICES_TRAEFIK_IMAGE=traefik
SERVICES_TRAEFIK_IMAGE_VERSION=2.11
SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST=`traefik.haslach2025.de`
SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4=172.31.254.253
SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4=172.30.255.254
# Service Traefik Crowdsec Bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_CONTAINER_NAME=traefik_crowdsec_bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME=traefik-crowdsec-bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE=fbonalair/traefik-crowdsec-bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE_VERSION=latest
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4=172.31.254.252
# Netzwerkeinstellungen
NETWORKS_PROXY_NAME=proxy
NETWORKS_PROXY_SUBNET_IPV4=172.30.0.0/16
NETWORKS_CROWDSEC_NAME=crowdsec
NETWORKS_CROWDSEC_SUBNET_IPV4=172.31.0.0/16

0
env/production/.env.tools vendored Normal file
View File

0
env/production/.env.website vendored Normal file
View File

6
env/staging/.env.administration vendored Normal file
View File

@@ -0,0 +1,6 @@
# ----------------------------------
# Portainer
# ----------------------------------

15
env/staging/.env.backend vendored Normal file
View File

@@ -0,0 +1,15 @@
# ----------------------------------
# Redis
# ----------------------------------
# ----------------------------------
# Laravel Backend
# ----------------------------------
# ----------------------------------
# Adminer
# ----------------------------------

3
env/staging/.env.database vendored Normal file
View File

@@ -0,0 +1,3 @@
# ----------------------------------
# Datenbank (MariaDB)
# ----------------------------------

9
env/staging/.env.develop vendored Normal file
View File

@@ -0,0 +1,9 @@
# ----------------------------------
# GITEA
# ----------------------------------
# ----------------------------------
# GITEA DB
# ----------------------------------

3
env/staging/.env.frontend vendored Normal file
View File

@@ -0,0 +1,3 @@
# ----------------------------------
# VUE APP
# ----------------------------------

4
env/staging/.env.proxy vendored Normal file
View File

@@ -0,0 +1,4 @@
# ----------------------------------
# TRAEFIK
# ----------------------------------

9
env/staging/.env.tools vendored Normal file
View File

@@ -0,0 +1,9 @@
# ----------------------------------
# NEXTCLOUD DB
# ----------------------------------
# ----------------------------------
# NEXTCLOUD CLOUD
# ----------------------------------

4
env/staging/.env.website vendored Normal file
View File

@@ -0,0 +1,4 @@
# ----------------------------------
# KIRBY CMS
# ----------------------------------

View File

@@ -0,0 +1,14 @@
# Nextcloud stack configuration
NEXTCLOUD_DOMAIN=cloud.example.com
# Database
NEXTCLOUD_DB_NAME=nextcloud
NEXTCLOUD_DB_USER=nextcloud
NEXTCLOUD_DB_PASSWORD=changeMe
NEXTCLOUD_DB_ROOT_PASSWORD=changeMeRoot
# PHP tuning
NEXTCLOUD_PHP_MEMORY_LIMIT=512M
NEXTCLOUD_PHP_UPLOAD_LIMIT=1024M

View File

@@ -0,0 +1,13 @@
Nextcloud Stack
Env vars required (copy .env.example to .env and adjust):
- NEXTCLOUD_DOMAIN: public domain for Nextcloud
- NEXTCLOUD_DB_NAME, NEXTCLOUD_DB_USER, NEXTCLOUD_DB_PASSWORD, NEXTCLOUD_DB_ROOT_PASSWORD
- Optional: NEXTCLOUD_PHP_MEMORY_LIMIT, NEXTCLOUD_PHP_UPLOAD_LIMIT
Usage
- Ensure the Traefik proxy stack is up and the external `${TRAEFIK_NETWORK}` network exists.
- Run: `docker compose --env-file ../../env/${ENV}/common.env --env-file ./.env -f docker-compose.yml up -d`

View File

@@ -0,0 +1,70 @@
services:
nextcloud:
image: nextcloud:28-apache
container_name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud
restart: unless-stopped
depends_on:
- db
- redis
environment:
- NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_DOMAIN}
- OVERWRITEHOST=${NEXTCLOUD_DOMAIN}
- OVERWRITEPROTOCOL=https
- OVERWRITECLIURL=https://${NEXTCLOUD_DOMAIN}
- REDIS_HOST=redis
- MYSQL_HOST=db
- MYSQL_DATABASE=${NEXTCLOUD_DB_NAME:-nextcloud}
- MYSQL_USER=${NEXTCLOUD_DB_USER:-nextcloud}
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
- PHP_MEMORY_LIMIT=${NEXTCLOUD_PHP_MEMORY_LIMIT:-512M}
- PHP_UPLOAD_LIMIT=${NEXTCLOUD_PHP_UPLOAD_LIMIT:-1024M}
volumes:
- nextcloud_data:/var/www/html
networks:
- nextcloud
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.nextcloud.rule=Host(`${NEXTCLOUD_DOMAIN}`)
- traefik.http.routers.nextcloud.entrypoints=websecure
- traefik.http.routers.nextcloud.tls=true
- traefik.http.routers.nextcloud.tls.certresolver=letsencrypt
- traefik.http.services.nextcloud.loadbalancer.server.port=80
- traefik.http.routers.nextcloud.middlewares=security-headers@file
- traefik.docker.network=${TRAEFIK_NETWORK:-proxy}
db:
image: mariadb:11
container_name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${NEXTCLOUD_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${NEXTCLOUD_DB_NAME:-nextcloud}
- MYSQL_USER=${NEXTCLOUD_DB_USER:-nextcloud}
- MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- nextcloud
redis:
image: redis:7-alpine
container_name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- nextcloud
volumes:
nextcloud_data:
db_data:
redis_data:
networks:
proxy:
external: true
nextcloud:
name: ${INFRASTRUCTURE_LABEL:-stack}-nextcloud

View File

@@ -0,0 +1,47 @@
services:
traefik:
image: traefik:v2.11
container_name: ${INFRASTRUCTURE_LABEL:-stack}-traefik
restart: unless-stopped
command:
- --providers.file.directory=/etc/traefik/dynamic
- --providers.file.watch=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=${TRAEFIK_NETWORK:-proxy}
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --api.dashboard=true
- --log.level=${TRAEFIK_LOG_LEVEL:-INFO}
- --accesslog=true
ports:
- ${TRAEFIK_HTTP_PORT:-80}:80
- ${TRAEFIK_HTTPS_PORT:-443}:443
environment:
- TZ=${TZ:-UTC}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./data:/letsencrypt
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DASHBOARD_DOMAIN}`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls=true
- traefik.http.routers.traefik.tls.certresolver=letsencrypt
- traefik.http.routers.traefik.service=api@internal
- traefik.docker.network=${TRAEFIK_NETWORK:-proxy}
# Optional: protect dashboard with basic auth if TRAEFIK_BASIC_AUTH_USERS is set
- traefik.http.routers.traefik.middlewares=dashboard-basicauth@file
networks:
proxy:
external: true

View File

@@ -0,0 +1,25 @@
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: true
security-headers:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: no-referrer-when-downgrade
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
dashboard-basicauth:
basicAuth:
users:
# Provide users via env TRAEFIK_BASIC_AUTH_USERS, format: user:hashedpassword
# Example to generate: htpasswd -nbB admin 'yourpassword'
# If env is empty, you can comment this middleware out from labels
- ${TRAEFIK_BASIC_AUTH_USERS:-}

View File

@@ -0,0 +1,6 @@
tls:
options:
default:
minVersion: VersionTLS12
sniStrict: true

View File

@@ -0,0 +1,30 @@
api:
dashboard: true
providers:
docker:
exposedByDefault: false
network: ${TRAEFIK_NETWORK:-proxy}
file:
directory: /etc/traefik/dynamic
watch: true
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
log:
level: ${TRAEFIK_LOG_LEVEL:-INFO}
accessLog: {}
certificatesResolvers:
letsencrypt:
acme:
email: ${ACME_EMAIL}
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web

14
infra/env/common.env.example vendored Normal file
View File

@@ -0,0 +1,14 @@
# Global/defaults
INFRASTRUCTURE_LABEL=mindboost
TZ=UTC
# Traefik / proxy
TRAEFIK_NETWORK=proxy
TRAEFIK_HTTP_PORT=80
TRAEFIK_HTTPS_PORT=443
TRAEFIK_LOG_LEVEL=INFO
ACME_EMAIL=you@example.com
TRAEFIK_DASHBOARD_DOMAIN=traefik.example.com
# Optional basic auth users for dashboard (format: user:hashed)
#TRAEFIK_BASIC_AUTH_USERS=admin:$2y$05$...

11
infra/env/development/common.env vendored Normal file
View File

@@ -0,0 +1,11 @@
# Development defaults (copy to production and adjust as needed)
INFRASTRUCTURE_LABEL=dev
TZ=UTC
TRAEFIK_NETWORK=proxy
TRAEFIK_HTTP_PORT=80
TRAEFIK_HTTPS_PORT=443
TRAEFIK_LOG_LEVEL=INFO
ACME_EMAIL=dev@example.com
TRAEFIK_DASHBOARD_DOMAIN=traefik.local

27
nginx/dev-backend.conf Normal file
View File

@@ -0,0 +1,27 @@
server {
listen 80;
server_name _;
root /var/www/public;
index index.php index.html;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass laravel_backend_dev:9000;
fastcgi_index index.php;
}
location ~ /\.(?!well-known).* {
deny all;
}
}

View File

@@ -0,0 +1,3 @@
source ./../setup/set-global-env.sh
chmod +x ./../../apps/backend/src/entrypoint.sh
docker compose -f ./../../apps/backend/docker-compose.overwrite.yml --env-file ./../../env/${ENVIRONMENT}/.env.database --env-file ./../../env/${ENVIRONMENT}/.env.backend --profile backend up

View File

@@ -0,0 +1,3 @@
source ./../setup/set-global-env.sh
source ./../setup/set-frontend-env.sh
docker compose -f ./../../apps/frontend/docker-compose.overwrite.yml --env-file ./../../env/${ENVIRONMENT}/.env.frontend --profile frontend up

View File

@@ -1,22 +0,0 @@
#!/bin/bash
set -e
echo "Prüfe, ob Traefik läuft..."
if ! docker ps --format '{{.Names}}' | grep -q 'traefik'; then
echo "Traefik läuft nicht."
read -p "Möchtest du die lokale Version zum Debuggen (docker-compose.overwrite.yml) starten? (y/n): " answer
if [[ "$answer" =~ ^[Yy]$ ]]; then
echo "Starte lokale Version..."
docker compose -f ../apps/docker-compose.overwrite.yml up -d
else
echo "Deployment abgebrochen."
exit 1
fi
else
echo "Traefik läuft."
echo "Starte Deployment mit docker-compose.prod.yml..."
docker compose -f ../apps/docker-compose.prod.yml up -d
fi
echo "Deployment abgeschlossen."

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
# Create external proxy network if it doesn't exist and prepare Traefik state
NETWORK_NAME=${TRAEFIK_NETWORK:-proxy}
ACME_FILE="infra/core/traefik/data/acme.json"
echo "[bootstrap] Ensuring external network '${NETWORK_NAME}' exists..."
if ! docker network ls --format '{{.Name}}' | grep -qx "${NETWORK_NAME}"; then
docker network create "${NETWORK_NAME}"
echo "[bootstrap] Created network '${NETWORK_NAME}'."
else
echo "[bootstrap] Network '${NETWORK_NAME}' already exists."
fi
echo "[bootstrap] Ensuring ACME storage exists with correct permissions..."
mkdir -p "$(dirname "${ACME_FILE}")"
touch "${ACME_FILE}"
chmod 600 "${ACME_FILE}"
echo "[bootstrap] ACME storage ready at ${ACME_FILE}."
echo "[bootstrap] Done."

View File

@@ -0,0 +1,87 @@
#!/bin/bash
# 🚀 Script to Generate Secure Secrets for Deployment
# Define root directory relative to the script location
# Stelle sicher, dass ROOT_DIR gesetzt ist
if [ -z "$ROOT_DIR" ]; then
echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..."
source ./set-project-root.sh
fi
SECRET_FILE="$ROOT_DIR/env/secrets.env"
GITIGNORE_FILE="$ROOT_DIR/.gitignore"
# ✅ Function to check if a command is installed
check_dependency() {
command -v "$1" >/dev/null 2>&1
}
# 🔍 Check for OpenSSL, and prompt user to install if missing
if ! check_dependency "openssl"; then
echo "⚠️ OpenSSL is not installed. It is required to generate secure secrets."
echo "Would you like to install OpenSSL now? (yes/no)"
read -r install_choice
if [[ "$install_choice" == "yes" ]]; then
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
sudo apt update && sudo apt install -y openssl
elif [[ "$OSTYPE" == "darwin"* ]]; then
brew install openssl
else
echo "❌ Unsupported OS. Please install OpenSSL manually."
exit 1
fi
else
echo "❌ OpenSSL is required but was not installed. Exiting."
exit 1
fi
fi
# ✅ Securely generate random values
generate_secret() {
openssl rand -base64 32
}
# 🔄 Check if the secret file already exists
if [ -f "$SECRET_FILE" ]; then
echo "⚠️ $SECRET_FILE already exists. Overwrite? (yes/no)"
read -r response
if [[ "$response" != "yes" ]]; then
echo "❌ Secret file creation canceled."
exit 1
fi
fi
# ✏️ Write secrets to file
echo "🔐 Generating $SECRET_FILE ..."
mkdir -p "$(dirname "$SECRET_FILE")" # Ensure the env directory exists
> "$SECRET_FILE" # Clear file if it exists
# 🔑 Define and write secrets
echo "ADMIN_PASSWORD_HASH=$(openssl passwd -6 admin)" >> "$SECRET_FILE"
echo "JWT_SECRET=$(generate_secret)" >> "$SECRET_FILE"
echo "MARIADB_PASSWORD=$(generate_secret)" >> "$SECRET_FILE"
echo "MARIADB_ROOT_PASSWORD=$(generate_secret)" >> "$SECRET_FILE"
echo "REDIS_HOST_PASSWORD=$(generate_secret)" >> "$SECRET_FILE"
echo "TRAEFIK_BASIC_AUTH_USERS=admin:$(openssl passwd -6 traefikpass)" >> "$SECRET_FILE"
echo "GITEA_MYSQL_PASSWORD=$(generate_secret)" >> "$SECRET_FILE"
echo "NEXTCLOUD_ADMIN_PASSWORD=$(generate_secret)" >> "$SECRET_FILE"
echo "MAIL_PASSWORD=$(generate_secret)" >> "$SECRET_FILE"
# 🛑 Ensure secrets.env is ignored by Git **without overwriting last line**
if [ -f "$SECRET_FILE" ]; then
# Check if the last line is missing a newline and fix it
if [ -s "$GITIGNORE_FILE" ] && [ "$(tail -c1 "$GITIGNORE_FILE")" != "" ]; then
echo "" >> "$GITIGNORE_FILE"
fi
# Append 'env/secrets.env' only if it's not already in .gitignore
if ! grep -q "^env/secrets.env$" "$GITIGNORE_FILE"; then
echo "env/secrets.env" >> "$GITIGNORE_FILE"
echo "✅ Added 'env/secrets.env' to .gitignore"
fi
fi
echo "✅ Secrets have been generated and stored in $SECRET_FILE."
echo "⚠️ Keep this file secure and do NOT commit it to Git!"

View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Stelle sicher, dass ROOT_DIR gesetzt ist
if [ -z "$ROOT_DIR" ]; then
echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..."
# Bestimme das Root-Verzeichnis des Git-Repos
ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null)
# Falls das Repository nicht gefunden wurde, abbrechen
if [ -z "$ROOT_DIR" ]; then
echo "❌ Fehler: Kein Git-Repository gefunden!"
exit 1
fi
# Setze die Variable für die aktuelle Shell-Sitzung
export ROOT_DIR
echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR"
fi
# Setze den Pfad zur .env.all Datei relativ zum Projekt-Root
ENV_FILE="$ROOT_DIR/env/development/.env.frontend"
# Prüfen, ob die Datei existiert
if [ ! -f "$ENV_FILE" ]; then
echo "❌ Fehler: Die Datei $ENV_FILE existiert nicht!"
exit 1
fi
echo "✅ ENV-Datei vorhanden: $ENV_FILE"
# Funktion: Alle Variablen exportieren
export_env_vars() {
while IFS='=' read -r key value; do
# Entferne führende und nachfolgende Leerzeichen
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# Falls die Zeile ein Kommentar oder leer ist, überspringen
if [[ -z "$key" || "$key" =~ ^# || -z "$value" ]]; then
continue
fi
# Exportiere die Variable
export "$key=$value"
done < "$ENV_FILE"
}
# Alle Variablen exportieren
export_env_vars
echo "🔹 Geladene Variablen:"
grep -o '^[^#]*' "$ENV_FILE" | cut -d '=' -f1 | while read -r var; do
echo "$var=${!var}" # Gibt die gesetzten Variablen mit ihrem Wert aus
done

65
scripts/setup/set-global-env.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/bash
# Stelle sicher, dass ROOT_DIR gesetzt ist
if [ -z "$ROOT_DIR" ]; then
echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..."
# Bestimme das Root-Verzeichnis des Git-Repos
ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null)
# Falls das Repository nicht gefunden wurde, abbrechen
if [ -z "$ROOT_DIR" ]; then
echo "❌ Fehler: Kein Git-Repository gefunden!"
exit 1
fi
# Setze die Variable für die aktuelle Shell-Sitzung
export ROOT_DIR
echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR"
fi
# Setze den Pfad zur .env.all Datei relativ zum Projekt-Root
ENV_FILE="$ROOT_DIR/env/.env.all"
# Prüfen, ob die Datei existiert
if [ ! -f "$ENV_FILE" ]; then
echo "❌ Fehler: Die Datei $ENV_FILE existiert nicht!"
exit 1
fi
echo "✅ ENV-Datei vorhanden: $ENV_FILE"
# Funktion: Alle Variablen exportieren
export_env_vars() {
while IFS='=' read -r key value; do
# Entferne führende und nachfolgende Leerzeichen
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# Falls die Zeile ein Kommentar oder leer ist, überspringen
if [[ -z "$key" || "$key" =~ ^# || -z "$value" ]]; then
continue
fi
# Entferne umschließende Anführungszeichen, falls vorhanden
value=$(echo "$value" | sed -E 's/^"(.*)"$/\1/')
# Exportiere die Variable
export "$key=$value"
done < "$ENV_FILE"
}
# Alle Variablen exportieren
export_env_vars
export SERVER_IP=$(curl -s https://api.ipify.org)
echo "🔹 Geladene Variablen:"
grep -o '^[^#]*' "$ENV_FILE" | cut -d '=' -f1 | while read -r var; do
echo "$var=${!var}" # Gibt die gesetzten Variablen mit ihrem Wert aus
done

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Bestimme das Root-Verzeichnis des Git-Repos
ROOT_DIR=$(git rev-parse --show-toplevel 2>/dev/null)
# Falls das Repository nicht gefunden wurde, abbrechen
if [ -z "$ROOT_DIR" ]; then
echo "❌ Fehler: Kein Git-Repository gefunden!"
exit 1
fi
# Setze die Variable für die aktuelle Shell-Sitzung
export ROOT_DIR
echo "✅ ROOT_DIR gesetzt auf: $ROOT_DIR"

View File

@@ -0,0 +1,60 @@
#!/bin/bash
# Stelle sicher, dass ROOT_DIR gesetzt ist
if [ -z "$ROOT_DIR" ]; then
echo "❌ WARN: ROOT_DIR ist nicht gesetzt! Setze ROOT_DIR..."
source ./set-project-root.sh
fi
# Stelle sicher, dass ENVIRONMENT gesetzt ist
if [ -z "$ENVIRONMENT" ]; then
echo "❌ WARN: ENVIRONMENT ist nicht gesetzt! Setze ENVIRONMENT..."
source ./set-global-env.sh
fi
# Setze den Pfad zur .env.all Datei relativ zum Projekt-Root
ENV_FILE="$ROOT_DIR/env/${ENVIRONMENT}/.env.proxy"
# Prüfen, ob die Datei existiert
if [ ! -f "$ENV_FILE" ]; then
echo "❌ Fehler: Die Datei $ENV_FILE existiert nicht!"
exit 1
fi
echo "✅ ENV-Datei vorhanden: $ENV_FILE"
# Funktion: Alle Variablen exportieren
export_env_vars() {
while IFS='=' read -r key value; do
# Entferne führende und nachfolgende Leerzeichen
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# Falls die Zeile ein Kommentar oder leer ist, überspringen
if [[ -z "$key" || "$key" =~ ^# || -z "$value" ]]; then
continue
fi
# Entferne umschließende Anführungszeichen, falls vorhanden
value=$(echo "$value" | sed -E 's/^"(.*)"$/\1/')
# Exportiere die Variable
export "$key=$value"
done < "$ENV_FILE"
}
# Alle Variablen exportieren
export_env_vars
export SERVER_IP=$(curl -s https://api.ipify.org)
echo "🔹 Geladene Variablen:"
grep -o '^[^#]*' "$ENV_FILE" | cut -d '=' -f1 | while read -r var; do
echo "$var=${!var}" # Gibt die gesetzten Variablen mit ihrem Wert aus
done

View File

@@ -0,0 +1,75 @@
#!/bin/bash
# Pfad zur .env.all Datei
ENV_FILE="../../env/.env.all"
# Funktion zum Überprüfen der Existenz einer Datei
check_file_exists() {
if [ ! -f "$1" ]; then
echo "Fehler: Die Datei $1 existiert nicht."
return 1
fi
}
# Überprüfe die Existenz von .env.all
check_file_exists "../../env/.env.all"
# Funktion zum Auslesen von Variablen aus der .env.all Datei
get_env_var() {
grep "^$1=" "$ENV_FILE" | cut -d '=' -f2
}
# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen
INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL")
ENVIRONMENT=$(get_env_var "ENVIRONMENT")
# Load environment variables from the .env files
set -o allexport
source ../../env/.env.all
source ../../env/${ENVIRONMENT:-development}/.env.administration
set +o allexport
# Liste Stacks
STACKS=("administration")
# Liste aller Environments
ENVIRONMENTS=("development" "staging" "production")
# Überprüfe die Existenz aller Stack-spezifischen .env Dateien
missing_files=0
for stack in "${STACKS[@]}"; do
env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}"
if ! check_file_exists "$env_file"; then
missing_files=$((missing_files + 1))
fi
done
if [ $missing_files -eq 0 ]; then
echo "Alle erforderlichen .env Dateien für das ${ENVIRONMENT:-development}-Environment sind vorhanden."
else
echo "Warnung: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren."
fi
# Überprüfe die Existenz aller Stack-spezifischen .env Dateien für alle Environments
for env in "${ENVIRONMENTS[@]}"; do
if [ "$env" != "$ENVIRONMENT" ]; then
for stack in "${STACKS[@]}"; do
env_file="../../env/${env}/.env.${stack}"
if ! check_file_exists "$env_file"; then
echo "Warnung: Die Datei $env_file fehlt für das Environment $env."
fi
done
fi
done
# Ausgabe der Variablen
echo " "
echo "Deploying to:"
echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}"
echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}"
echo "-----------------------------------"
# Ausführen des Docker Compose Befehls
docker compose -f ../apps/docker-compose.all.yml --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile administration up --remove-orphans

109
scripts/start/deploy-all.sh Executable file
View File

@@ -0,0 +1,109 @@
#!/bin/bash
source ../setup/set-project-root.sh
source ../setup/set-global-env.sh
source ../setup/set-proxy-env.sh
source ../setup/generate-secrets.sh
# Pfad zur .env.all Datei
ENV_FILE="../../env/.env.all"
# Funktion zum Auslesen von Variablen aus der .env.all Datei
get_env_var() {
grep "^$1=" "$ENV_FILE" | cut -d '=' -f2
}
# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen
INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL")
ENVIRONMENT=$(get_env_var "ENVIRONMENT")
SERVER_IP=$(curl -s https://api.ipify.org)
# Liste aller Stacks
STACKS=("administration" "frontend" "develop" "database" "proxy" "tools" "website" "backend")
# Liste aller Environments
ENVIRONMENTS=("development" "staging" "production")
# Funktion zum Überprüfen der Existenz einer Datei
check_file_exists() {
if [ ! -f "$1" ]; then
echo "Fehler: Die Datei $1 existiert nicht."
return 1
fi
}
# Prüfe, ob das Skript nur in der Entwicklungsumgebung ausgeführt wird
if [ "$ENVIRONMENT" == "development" ]; then
# Sicherstellen, dass acme_letsencrypt.json existiert und korrekte Berechtigungen hat
ACME_FILE="../apps/proxy/traefik/acme_letsencrypt.json"
if [ ! -f "$ACME_FILE" ]; then
echo "🔹 Erstelle $ACME_FILE..."
touch "$ACME_FILE"
fi
echo "🔹 Setze Berechtigungen für $ACME_FILE auf 600..."
chmod 600 "$ACME_FILE"
echo "🔹 ENVIRONMENT ist 'development' Hosts aus .env.proxy werden hinzugefügt und Container gestartet."
# Pfad zur Proxy-Env-Datei
ENV_PROXY_FILE="../../env/development/.env.proxy"
# Hosts-Datei Pfad (Linux/macOS)
HOSTS_FILE="/etc/hosts"
# Prüfe, ob die Env-Datei existiert
if [ ! -f "$ENV_PROXY_FILE" ]; then
echo "❌ Fehler: Die Datei $ENV_PROXY_FILE existiert nicht!"
exit 1
fi
# Lese alle Zeilen, die auf *_DOMAIN= enden und extrahiere die Werte
DOMAINS=($(grep -E '^[A-Z_]+_DOMAIN=' "$ENV_PROXY_FILE" | cut -d '=' -f2))
# Füge jede Domain zur /etc/hosts Datei hinzu, falls sie fehlt
for DOMAIN in "${DOMAINS[@]}"; do
if ! grep -q "$DOMAIN" "$HOSTS_FILE"; then
echo "127.0.0.1 $DOMAIN" | sudo tee -a "$HOSTS_FILE" > /dev/null
echo "$DOMAIN zu /etc/hosts hinzugefügt"
else
echo "$DOMAIN ist bereits in /etc/hosts vorhanden"
fi
done
else
echo "❌ ENVIRONMENT ist nicht 'development' Routing über externen DNS erwartet"
fi
# Überprüfe die Existenz von .env.all
check_file_exists "../../env/.env.all"
# Überprüfe die Existenz aller Stack-spezifischen .env Dateien
missing_files=0
for stack in "${STACKS[@]}"; do
env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}"
if ! check_file_exists "$env_file"; then
missing_files=$((missing_files + 1))
fi
done
if [ $missing_files -eq 0 ]; then
echo "Alle erforderlichen .env Dateien sind vorhanden."
else
echo "WARNUNG: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren."
fi
# Ausgabe der Variablen
echo "Deploying to:"
echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}"
echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}"
echo "-----------------------------------"
# Check for the --build argument
BUILD_OPTION=""
if [[ "$1" == "--build" ]]; then
BUILD_OPTION="--build"
fi
# Ausführen des Docker Compose Befehls
docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --env-file ../../env/.env.all --env-file ../../env/${ENVIRONMENT:-development}/.env.proxy --profile backend up --remove-orphans $BUILD_OPTION

65
scripts/start/deploy-app.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/bash
source ../setup/set-project-root.sh
source ../setup/set-global-env.sh
source ../setup/set-proxy-env.sh
source ../setup/generate-secrets.sh
# Pfad zur .env.all Datei
ENV_FILE="../../env/.env.all"
# Funktion zum Auslesen von Variablen aus der .env.all Datei
get_env_var() {
grep "^$1=" "$ENV_FILE" | cut -d '=' -f2
}
# Auslesen der INFRASTRUCTURE und ENVIRONMENT Variablen
INFRASTRUCTURE=$(get_env_var "INFRASTRUCTURE_LABEL")
ENVIRONMENT=$(get_env_var "ENVIRONMENT")
SERVER_IP=$(curl -s https://api.ipify.org)
# Liste aller Stacks
STACKS=("proxy" "frontend" "database" "backend")
# Liste aller Environments
ENVIRONMENTS=("development" "staging" "production")
# Funktion zum Überprüfen der Existenz einer Datei
check_file_exists() {
if [ ! -f "$1" ]; then
echo "Fehler: Die Datei $1 existiert nicht."
return 1
fi
}
# Überprüfe die Existenz von .env.all
check_file_exists "../../env/.env.all"
# Überprüfe die Existenz aller Stack-spezifischen .env Dateien
missing_files=0
for stack in "${STACKS[@]}"; do
env_file="../../env/${ENVIRONMENT:-development}/.env.${stack}"
if ! check_file_exists "$env_file"; then
missing_files=$((missing_files + 1))
fi
done
if [ $missing_files -eq 0 ]; then
echo "Alle erforderlichen .env Dateien sind vorhanden."
else
echo "WARNUNG: $missing_files .env Datei(en) fehlen. Einige Stacks könnten nicht korrekt funktionieren."
fi
# Ausgabe der Variablen
echo "Deploying to"
echo "INFRASTRUCTURE: ${INFRASTRUCTURE:-Not set}"
echo "ENVIRONMENT: ${ENVIRONMENT:-Not set}"
echo "-----------------------------------"
# Check for the --build argument
BUILD_OPTION=""
if [[ "$1" == "--build" ]]; then
BUILD_OPTION="--build"
fi
# Ausführen des Docker Compose Befehls
docker compose -f ../../apps/docker-compose.all.yml -p ${INFRASTRUCTURE:-my} --profile app up --remove-orphans $BUILD_OPTION

Some files were not shown because too many files have changed in this diff Show More