From d5a0e4b2ab0ff74fa7dd5842c19e51efeeeb5de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Jasi=C5=84ski?= Date: Mon, 1 Jun 2026 19:24:37 +0200 Subject: [PATCH] feat(infra): create staging docker-compose and env template --- .env.stage.template | 42 ++++++++++++++++ .gitignore | 1 + docker-compose.stage.yml | 102 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 .env.stage.template create mode 100644 docker-compose.stage.yml diff --git a/.env.stage.template b/.env.stage.template new file mode 100644 index 0000000..a6447fb --- /dev/null +++ b/.env.stage.template @@ -0,0 +1,42 @@ +# =================================================================== +# NexusReader — Staging (Stage) Environment Variables +# =================================================================== +# Copy this file to `.env.stage` and fill in the values before deployment: +# cp .env.stage.template .env.stage +# +# Then deploy with: +# docker compose -f docker-compose.stage.yml --env-file .env.stage up -d --build +# =================================================================== + +# === PostgreSQL === +POSTGRES_USER=nexus_user_stage +POSTGRES_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD +POSTGRES_DB=nexus_stage_db +POSTGRES_PORT=5438 + +# === Neo4j === +NEO4J_USERNAME=neo4j +NEO4J_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD + +# === Qdrant (leave empty to disable API key auth in staging) === +QDRANT_API_KEY= + +# === Web App === +WEB_PORT=5080 + +# === Google OAuth (Staging credentials) === +GOOGLE_CLIENT_ID=placeholder_google_client_id_stage +GOOGLE_CLIENT_SECRET=placeholder_google_client_secret_stage + +# === Gemini AI === +GOOGLE_AI_API_KEY=placeholder_gemini_api_key_stage + +# === Secure Admin Seed Password (MANDATORY in Staging environment) === +# This password is used by DbInitializer during startup. Cannot be empty or 'Admin123!'. +NEXUS_ADMIN_PASSWORD=CHANGE_ME_TO_SECURE_ADMIN_PASSWORD + +# === Non-standard ports for auxiliary services === +QDRANT_HTTP_PORT=6383 +QDRANT_GRPC_PORT=6384 +NEO4J_HTTP_PORT=7488 +NEO4J_BOLT_PORT=7688 diff --git a/.gitignore b/.gitignore index 7fc6051..1816458 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ Thumbs.db .fake .env +.env.stage src/NexusReader.Web/nexus.db src/NexusReader.Web/wwwroot/covers/ src/NexusReader.Web/wwwroot/uploads/ diff --git a/docker-compose.stage.yml b/docker-compose.stage.yml new file mode 100644 index 0000000..cac5b43 --- /dev/null +++ b/docker-compose.stage.yml @@ -0,0 +1,102 @@ +services: + db: + image: pgvector/pgvector:pg17 + container_name: nexus-db-stage + environment: + POSTGRES_USER: ${POSTGRES_USER:-nexus_user_stage} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} + POSTGRES_DB: ${POSTGRES_DB:-nexus_stage_db} + ports: + - "${POSTGRES_PORT:-5438}:5432" + volumes: + - pgdata_stage:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-nexus_user_stage} -d $${POSTGRES_DB:-nexus_stage_db}"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - nexus-stage + restart: unless-stopped + + web: + build: + context: . + dockerfile: Dockerfile + container_name: nexus-web-stage + ports: + - "${WEB_PORT:-5080}:5000" + environment: + - ASPNETCORE_ENVIRONMENT=Staging + - ConnectionStrings__PostgresConnection=Host=db;Database=${POSTGRES_DB:-nexus_stage_db};Username=${POSTGRES_USER:-nexus_user_stage};Password=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} + - ConnectionStrings__QdrantConnection=http://qdrant:6334 + - ConnectionStrings__Neo4jConnection=bolt://neo4j:7687 + - Neo4j__Username=${NEO4J_USERNAME:-neo4j} + - Neo4j__Password=${NEO4J_PASSWORD:?NEO4J_PASSWORD is required} + - Authentication__Google__ClientId=${GOOGLE_CLIENT_ID:-placeholder_google_client_id_stage} + - Authentication__Google__ClientSecret=${GOOGLE_CLIENT_SECRET:-placeholder_google_client_secret_stage} + - Ai__Google__ApiKey=${GOOGLE_AI_API_KEY:-placeholder_gemini_api_key_stage} + - NEXUS_ADMIN_PASSWORD=${NEXUS_ADMIN_PASSWORD:?NEXUS_ADMIN_PASSWORD is required} + depends_on: + db: + condition: service_healthy + qdrant: + condition: service_healthy + neo4j: + condition: service_healthy + volumes: + - stage_www_uploads:/app/wwwroot/uploads + - stage_www_covers:/app/wwwroot/covers + networks: + - nexus-stage + restart: unless-stopped + + qdrant: + image: qdrant/qdrant:latest + container_name: nexus-qdrant-stage + environment: + - QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY:-} + ports: + - "${QDRANT_HTTP_PORT:-6383}:6333" + - "${QDRANT_GRPC_PORT:-6384}:6334" + volumes: + - qdrant_stage_data:/qdrant/storage + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:6333/healthz || exit 1"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - nexus-stage + restart: unless-stopped + + neo4j: + image: neo4j:5-community + container_name: nexus-neo4j-stage + environment: + - NEO4J_AUTH=${NEO4J_USERNAME:-neo4j}/${NEO4J_PASSWORD:?NEO4J_PASSWORD is required} + ports: + - "${NEO4J_HTTP_PORT:-7488}:7474" + - "${NEO4J_BOLT_PORT:-7688}:7687" + volumes: + - neo4j_stage_data:/data + healthcheck: + test: ["CMD-SHELL", "wget -qO- http://localhost:7474 || exit 1"] + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s + networks: + - nexus-stage + restart: unless-stopped + +volumes: + pgdata_stage: + qdrant_stage_data: + neo4j_stage_data: + stage_www_uploads: + stage_www_covers: + +networks: + nexus-stage: + driver: bridge