commit 91f4ba3dbf5fb7e48cb91c93364f9f8004157c14 Author: Lorentz Date: Sun Feb 15 19:31:34 2026 +0000 Initial infrastructure setup diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c994db7 --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# PostgreSQL +POSTGRES_PASSWORD=change-me-to-a-strong-password + +# Cloudflare DNS Challenge +CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-api-token-here +ACME_EMAIL=lorentz@wulfconsulting.com + +# Domains +DEV_DOMAIN=dev.quest.vorteq.wulf.cloud +TEST_DOMAIN=testing.vorteq.wulf.cloud +PROD_DOMAIN=quest.vorteq.wulf.cloud +TRAEFIK_DOMAIN=traefik.vorteq.wulf.cloud + +# Better-Auth Secrets (generate with: openssl rand -base64 32) +DEV_AUTH_SECRET=your-dev-secret-here +TEST_AUTH_SECRET=your-test-secret-here +PROD_AUTH_SECRET=your-prod-secret-here diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68901ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +traefik/acme.json +traefik/logs/ +*.log +node_modules/ diff --git a/create-files.sh b/create-files.sh new file mode 100755 index 0000000..59ec28a --- /dev/null +++ b/create-files.sh @@ -0,0 +1,403 @@ +#!/bin/bash +# Run this from /opt/stacks/vorteq on expvtcasp01 +# Creates all infrastructure files for the Vorteq Quest Portal + +set -e +echo "Creating infrastructure files..." + +# ============================================================ +# docker-compose.yml +# ============================================================ +cat > docker-compose.yml << 'DCOMPOSE' +version: '3.8' + +services: + # Traefik Reverse Proxy + traefik: + image: traefik:v3.0 + container_name: traefik + restart: unless-stopped + security_opt: + - no-new-privileges:true + networks: + - web + ports: + - "80:80" + - "443:443" + - "8080:8080" + environment: + - CF_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN} + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik/traefik.yml:/traefik.yml:ro + - ./traefik/acme.json:/acme.json + - ./traefik/config:/config:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.traefik.entrypoints=websecure" + - "traefik.http.routers.traefik.tls.certresolver=letsencrypt" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$8evjzm8w$$FU3G.qPxZRGlnUKzPJ6QB/" + + # PostgreSQL + postgres: + image: postgres:16-alpine + container_name: postgres + restart: unless-stopped + networks: + - web + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_MULTIPLE_DATABASES: vorteq_dev,vorteq_test,vorteq_prod + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis + redis: + image: redis:7-alpine + container_name: redis + restart: unless-stopped + networks: + - web + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Development Environment + app-dev: + image: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq:dev + container_name: vorteq-dev + restart: unless-stopped + networks: + - web + environment: + NODE_ENV: development + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/vorteq_dev + REDIS_URL: redis://redis:6379/0 + BETTER_AUTH_SECRET: ${DEV_AUTH_SECRET} + BETTER_AUTH_URL: https://${DEV_DOMAIN} + PORT: 3000 + labels: + - "traefik.enable=true" + - "traefik.http.routers.app-dev.rule=Host(`${DEV_DOMAIN}`)" + - "traefik.http.routers.app-dev.entrypoints=websecure" + - "traefik.http.routers.app-dev.tls.certresolver=letsencrypt" + - "traefik.http.services.app-dev.loadbalancer.server.port=3000" + - "traefik.http.routers.app-dev.middlewares=dev-headers" + - "traefik.http.middlewares.dev-headers.headers.customresponseheaders.X-Environment=development" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + # Testing Environment + app-test: + image: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq:test + container_name: vorteq-test + restart: unless-stopped + networks: + - web + environment: + NODE_ENV: test + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/vorteq_test + REDIS_URL: redis://redis:6379/1 + BETTER_AUTH_SECRET: ${TEST_AUTH_SECRET} + BETTER_AUTH_URL: https://${TEST_DOMAIN} + PORT: 3000 + labels: + - "traefik.enable=true" + - "traefik.http.routers.app-test.rule=Host(`${TEST_DOMAIN}`)" + - "traefik.http.routers.app-test.entrypoints=websecure" + - "traefik.http.routers.app-test.tls.certresolver=letsencrypt" + - "traefik.http.services.app-test.loadbalancer.server.port=3000" + - "traefik.http.routers.app-test.middlewares=test-headers" + - "traefik.http.middlewares.test-headers.headers.customresponseheaders.X-Environment=testing" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + # Production Environment + app-prod: + image: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq:latest + container_name: vorteq-prod + restart: unless-stopped + networks: + - web + environment: + NODE_ENV: production + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/vorteq_prod + REDIS_URL: redis://redis:6379/2 + BETTER_AUTH_SECRET: ${PROD_AUTH_SECRET} + BETTER_AUTH_URL: https://${PROD_DOMAIN} + PORT: 3000 + labels: + - "traefik.enable=true" + - "traefik.http.routers.app-prod.rule=Host(`${PROD_DOMAIN}`)" + - "traefik.http.routers.app-prod.entrypoints=websecure" + - "traefik.http.routers.app-prod.tls.certresolver=letsencrypt" + - "traefik.http.services.app-prod.loadbalancer.server.port=3000" + - "traefik.http.routers.app-prod.middlewares=prod-chain" + - "traefik.http.middlewares.prod-chain.chain.middlewares=security-headers,rate-limit,compress" + - "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000" + - "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true" + - "traefik.http.middlewares.security-headers.headers.stsPreload=true" + - "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true" + - "traefik.http.middlewares.security-headers.headers.frameDeny=true" + - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true" + - "traefik.http.middlewares.security-headers.headers.browserXssFilter=true" + - "traefik.http.middlewares.rate-limit.ratelimit.average=100" + - "traefik.http.middlewares.rate-limit.ratelimit.burst=50" + - "traefik.http.middlewares.compress.compress=true" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '1' + memory: 1G + +networks: + web: + external: true + +volumes: + postgres_data: + redis_data: +DCOMPOSE +echo " docker-compose.yml" + +# ============================================================ +# traefik/traefik.yml +# ============================================================ +mkdir -p traefik/{config,logs} + +cat > traefik/traefik.yml << 'TRAEFIK' +api: + dashboard: true + insecure: false + +entryPoints: + web: + address: ":80" + http: + redirections: + entryPoint: + to: websecure + scheme: https + websecure: + address: ":443" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: web + file: + directory: /config + watch: true + +certificatesResolvers: + letsencrypt: + acme: + email: lorentz@wulfconsulting.com + storage: acme.json + dnsChallenge: + provider: cloudflare + delayBeforeCheck: 0 + resolvers: + - "1.1.1.1:53" + - "8.8.8.8:53" + +log: + level: INFO + +accessLog: + filePath: /var/log/traefik/access.log + bufferingSize: 100 +TRAEFIK +echo " traefik/traefik.yml" + +# ============================================================ +# init-databases.sh +# ============================================================ +cat > init-databases.sh << 'INITDB' +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE DATABASE vorteq_dev; + GRANT ALL PRIVILEGES ON DATABASE vorteq_dev TO postgres; + + CREATE DATABASE vorteq_test; + GRANT ALL PRIVILEGES ON DATABASE vorteq_test TO postgres; + + CREATE DATABASE vorteq_prod; + GRANT ALL PRIVILEGES ON DATABASE vorteq_prod TO postgres; +EOSQL + +echo "Multiple databases created successfully!" +INITDB +chmod +x init-databases.sh +echo " init-databases.sh" + +# ============================================================ +# .env.example +# ============================================================ +cat > .env.example << 'ENVEX' +# PostgreSQL +POSTGRES_PASSWORD=change-me-to-a-strong-password + +# Cloudflare DNS Challenge +CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-api-token-here +ACME_EMAIL=lorentz@wulfconsulting.com + +# Domains +DEV_DOMAIN=dev.quest.vorteq.wulf.cloud +TEST_DOMAIN=testing.vorteq.wulf.cloud +PROD_DOMAIN=quest.vorteq.wulf.cloud +TRAEFIK_DOMAIN=traefik.vorteq.wulf.cloud + +# Better-Auth Secrets (generate with: openssl rand -base64 32) +DEV_AUTH_SECRET=your-dev-secret-here +TEST_AUTH_SECRET=your-test-secret-here +PROD_AUTH_SECRET=your-prod-secret-here +ENVEX +echo " .env.example" + +# ============================================================ +# setup.sh +# ============================================================ +cat > setup.sh << 'SETUP' +#!/bin/bash +set -e + +echo "Setting up Traefik Multi-Environment Stack" +echo "==============================================" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "Checking prerequisites..." + +command -v docker >/dev/null 2>&1 || { + echo -e "${RED}Docker is not installed${NC}" + exit 1 +} + +command -v docker compose >/dev/null 2>&1 || { + echo -e "${RED}Docker Compose is not installed${NC}" + exit 1 +} + +echo -e "${GREEN}Docker and Docker Compose are installed${NC}" + +echo "Creating directory structure..." +mkdir -p traefik/{config,logs} + +echo "Setting up SSL certificate storage..." +touch traefik/acme.json +chmod 600 traefik/acme.json + +chmod +x init-databases.sh + +echo "Creating Docker network..." +docker network create web 2>/dev/null || echo -e "${YELLOW}Network 'web' already exists${NC}" + +if [ ! -f .env ]; then + echo "Creating .env file..." + cp .env.example .env + echo -e "${YELLOW}Please edit .env file with your actual values!${NC}" +else + echo -e "${GREEN}.env file already exists${NC}" +fi + +SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || echo "YOUR_SERVER_IP") +echo "" +echo "Cloudflare DNS Configuration Check" +echo "====================================" +echo "Make sure these DNS records are configured in Cloudflare:" +echo " - quest.vorteq.wulf.cloud -> ${SERVER_IP}" +echo " - dev.quest.vorteq.wulf.cloud -> ${SERVER_IP}" +echo " - testing.vorteq.wulf.cloud -> ${SERVER_IP}" +echo " - traefik.vorteq.wulf.cloud -> ${SERVER_IP}" +echo "" +echo "Important: Set DNS records to 'DNS only' (grey cloud)" +echo "" + +# Generate auth secrets if needed +if grep -q "your-.*-secret-here" .env 2>/dev/null; then + echo -e "${YELLOW}Generating Better-Auth secrets...${NC}" + DEV_SECRET=$(openssl rand -base64 32) + TEST_SECRET=$(openssl rand -base64 32) + PROD_SECRET=$(openssl rand -base64 32) + + sed -i "s/your-dev-secret-here/$DEV_SECRET/" .env + sed -i "s/your-test-secret-here/$TEST_SECRET/" .env + sed -i "s/your-prod-secret-here/$PROD_SECRET/" .env + echo -e "${GREEN}Generated and saved auth secrets${NC}" +else + echo -e "${GREEN}Auth secrets already configured${NC}" +fi + +echo "" +echo "Pre-flight Checklist" +echo "======================" +echo "Before running docker compose up, ensure:" +echo " - DNS records are configured" +echo " - Firewall allows ports 80 and 443" +echo " - .env file is configured" +echo " - Docker images are built (or registry is accessible)" +echo "" + +read -p "Do you want to start the stack now? (y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Starting services..." + docker compose up -d + echo "" + echo -e "${GREEN}Stack is starting!${NC}" + echo "" + echo "View logs with: docker compose logs -f" + echo "SSL certificates may take a few minutes to provision" +else + echo "" + echo "Setup complete! Run 'docker compose up -d' when ready." +fi +SETUP +chmod +x setup.sh +echo " setup.sh" + +# ============================================================ +# Done +# ============================================================ +echo "" +echo "All files created. Directory listing:" +find . -not -path './.git/*' -not -path './.git' | sort +echo "" +echo "Next steps:" +echo " 1. cp .env.example .env" +echo " 2. nano .env (fill in your secrets)" +echo " 3. git add . && git commit -m 'Initial infrastructure setup' && git push -u origin main" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3f72b17 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,175 @@ +version: '3.8' + +services: + # Traefik Reverse Proxy + traefik: + image: traefik:v3.0 + container_name: traefik + restart: unless-stopped + security_opt: + - no-new-privileges:true + networks: + - web + ports: + - "80:80" + - "443:443" + - "8080:8080" + environment: + - CF_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN} + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik/traefik.yml:/traefik.yml:ro + - ./traefik/acme.json:/acme.json + - ./traefik/config:/config:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.traefik.entrypoints=websecure" + - "traefik.http.routers.traefik.tls.certresolver=letsencrypt" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$8evjzm8w$$FU3G.qPxZRGlnUKzPJ6QB/" + + # PostgreSQL + postgres: + image: postgres:16-alpine + container_name: postgres + restart: unless-stopped + networks: + - web + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_MULTIPLE_DATABASES: vorteq_dev,vorteq_test,vorteq_prod + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis + redis: + image: redis:7-alpine + container_name: redis + restart: unless-stopped + networks: + - web + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Development Environment + app-dev: + image: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq:dev + container_name: vorteq-dev + restart: unless-stopped + networks: + - web + environment: + NODE_ENV: development + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/vorteq_dev + REDIS_URL: redis://redis:6379/0 + BETTER_AUTH_SECRET: ${DEV_AUTH_SECRET} + BETTER_AUTH_URL: https://${DEV_DOMAIN} + PORT: 3000 + labels: + - "traefik.enable=true" + - "traefik.http.routers.app-dev.rule=Host(`${DEV_DOMAIN}`)" + - "traefik.http.routers.app-dev.entrypoints=websecure" + - "traefik.http.routers.app-dev.tls.certresolver=letsencrypt" + - "traefik.http.services.app-dev.loadbalancer.server.port=3000" + - "traefik.http.routers.app-dev.middlewares=dev-headers" + - "traefik.http.middlewares.dev-headers.headers.customresponseheaders.X-Environment=development" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + # Testing Environment + app-test: + image: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq:test + container_name: vorteq-test + restart: unless-stopped + networks: + - web + environment: + NODE_ENV: test + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/vorteq_test + REDIS_URL: redis://redis:6379/1 + BETTER_AUTH_SECRET: ${TEST_AUTH_SECRET} + BETTER_AUTH_URL: https://${TEST_DOMAIN} + PORT: 3000 + labels: + - "traefik.enable=true" + - "traefik.http.routers.app-test.rule=Host(`${TEST_DOMAIN}`)" + - "traefik.http.routers.app-test.entrypoints=websecure" + - "traefik.http.routers.app-test.tls.certresolver=letsencrypt" + - "traefik.http.services.app-test.loadbalancer.server.port=3000" + - "traefik.http.routers.app-test.middlewares=test-headers" + - "traefik.http.middlewares.test-headers.headers.customresponseheaders.X-Environment=testing" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + + # Production Environment + app-prod: + image: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq:latest + container_name: vorteq-prod + restart: unless-stopped + networks: + - web + environment: + NODE_ENV: production + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/vorteq_prod + REDIS_URL: redis://redis:6379/2 + BETTER_AUTH_SECRET: ${PROD_AUTH_SECRET} + BETTER_AUTH_URL: https://${PROD_DOMAIN} + PORT: 3000 + labels: + - "traefik.enable=true" + - "traefik.http.routers.app-prod.rule=Host(`${PROD_DOMAIN}`)" + - "traefik.http.routers.app-prod.entrypoints=websecure" + - "traefik.http.routers.app-prod.tls.certresolver=letsencrypt" + - "traefik.http.services.app-prod.loadbalancer.server.port=3000" + - "traefik.http.routers.app-prod.middlewares=prod-chain" + - "traefik.http.middlewares.prod-chain.chain.middlewares=security-headers,rate-limit,compress" + - "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000" + - "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true" + - "traefik.http.middlewares.security-headers.headers.stsPreload=true" + - "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true" + - "traefik.http.middlewares.security-headers.headers.frameDeny=true" + - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true" + - "traefik.http.middlewares.security-headers.headers.browserXssFilter=true" + - "traefik.http.middlewares.rate-limit.ratelimit.average=100" + - "traefik.http.middlewares.rate-limit.ratelimit.burst=50" + - "traefik.http.middlewares.compress.compress=true" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '1' + memory: 1G + +networks: + web: + external: true + +volumes: + postgres_data: + redis_data: diff --git a/init-databases.sh b/init-databases.sh new file mode 100755 index 0000000..9bdcd1f --- /dev/null +++ b/init-databases.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE DATABASE vorteq_dev; + GRANT ALL PRIVILEGES ON DATABASE vorteq_dev TO postgres; + + CREATE DATABASE vorteq_test; + GRANT ALL PRIVILEGES ON DATABASE vorteq_test TO postgres; + + CREATE DATABASE vorteq_prod; + GRANT ALL PRIVILEGES ON DATABASE vorteq_prod TO postgres; +EOSQL + +echo "Multiple databases created successfully!" diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..4e29c3e --- /dev/null +++ b/setup.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -e + +echo "Setting up Traefik Multi-Environment Stack" +echo "==============================================" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "Checking prerequisites..." + +command -v docker >/dev/null 2>&1 || { + echo -e "${RED}Docker is not installed${NC}" + exit 1 +} + +command -v docker compose >/dev/null 2>&1 || { + echo -e "${RED}Docker Compose is not installed${NC}" + exit 1 +} + +echo -e "${GREEN}Docker and Docker Compose are installed${NC}" + +echo "Creating directory structure..." +mkdir -p traefik/{config,logs} + +echo "Setting up SSL certificate storage..." +touch traefik/acme.json +chmod 600 traefik/acme.json + +chmod +x init-databases.sh + +echo "Creating Docker network..." +docker network create web 2>/dev/null || echo -e "${YELLOW}Network 'web' already exists${NC}" + +if [ ! -f .env ]; then + echo "Creating .env file..." + cp .env.example .env + echo -e "${YELLOW}Please edit .env file with your actual values!${NC}" +else + echo -e "${GREEN}.env file already exists${NC}" +fi + +SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || echo "YOUR_SERVER_IP") +echo "" +echo "Cloudflare DNS Configuration Check" +echo "====================================" +echo "Make sure these DNS records are configured in Cloudflare:" +echo " - quest.vorteq.wulf.cloud -> ${SERVER_IP}" +echo " - dev.quest.vorteq.wulf.cloud -> ${SERVER_IP}" +echo " - testing.vorteq.wulf.cloud -> ${SERVER_IP}" +echo " - traefik.vorteq.wulf.cloud -> ${SERVER_IP}" +echo "" +echo "Important: Set DNS records to 'DNS only' (grey cloud)" +echo "" + +# Generate auth secrets if needed +if grep -q "your-.*-secret-here" .env 2>/dev/null; then + echo -e "${YELLOW}Generating Better-Auth secrets...${NC}" + DEV_SECRET=$(openssl rand -base64 32) + TEST_SECRET=$(openssl rand -base64 32) + PROD_SECRET=$(openssl rand -base64 32) + + sed -i "s/your-dev-secret-here/$DEV_SECRET/" .env + sed -i "s/your-test-secret-here/$TEST_SECRET/" .env + sed -i "s/your-prod-secret-here/$PROD_SECRET/" .env + echo -e "${GREEN}Generated and saved auth secrets${NC}" +else + echo -e "${GREEN}Auth secrets already configured${NC}" +fi + +echo "" +echo "Pre-flight Checklist" +echo "======================" +echo "Before running docker compose up, ensure:" +echo " - DNS records are configured" +echo " - Firewall allows ports 80 and 443" +echo " - .env file is configured" +echo " - Docker images are built (or registry is accessible)" +echo "" + +read -p "Do you want to start the stack now? (y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Starting services..." + docker compose up -d + echo "" + echo -e "${GREEN}Stack is starting!${NC}" + echo "" + echo "View logs with: docker compose logs -f" + echo "SSL certificates may take a few minutes to provision" +else + echo "" + echo "Setup complete! Run 'docker compose up -d' when ready." +fi diff --git a/traefik/traefik.yml b/traefik/traefik.yml new file mode 100644 index 0000000..0928b36 --- /dev/null +++ b/traefik/traefik.yml @@ -0,0 +1,42 @@ +api: + dashboard: true + insecure: false + +entryPoints: + web: + address: ":80" + http: + redirections: + entryPoint: + to: websecure + scheme: https + websecure: + address: ":443" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: web + file: + directory: /config + watch: true + +certificatesResolvers: + letsencrypt: + acme: + email: lorentz@wulfconsulting.com + storage: acme.json + dnsChallenge: + provider: cloudflare + delayBeforeCheck: 0 + resolvers: + - "1.1.1.1:53" + - "8.8.8.8:53" + +log: + level: INFO + +accessLog: + filePath: /var/log/traefik/access.log + bufferingSize: 100