AzioneLab frontend placeholder
+The Angular application build will replace this page.
+diff --git a/.codex/project.md b/.codex/project.md index 8b6341e..4aca4ee 100644 --- a/.codex/project.md +++ b/.codex/project.md @@ -77,7 +77,7 @@ All tests MUST be executed inside Docker containers. Configure the canonical test command for this repository: ```bash -CHANGE_ME +docker compose --env-file .env.example -f infra/docker/compose.yml config ``` Examples: diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0ff479d --- /dev/null +++ b/.env.example @@ -0,0 +1,24 @@ +# AzioneLab Docker Compose example environment. +# Copy this file to .env and replace placeholder values before deployment. + +COMPOSE_PROJECT_NAME=azionelab + +NGINX_HTTP_PORT=8080 + +BACKEND_HOST=backend +BACKEND_PORT=8000 +FRONTEND_HOST=frontend +FRONTEND_PORT=8080 + +DJANGO_SECRET_KEY=change-me +DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 +DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:8080 +DJANGO_DEBUG=false + +POSTGRES_DB=azionelab +POSTGRES_USER=azionelab +POSTGRES_PASSWORD=change-me +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 + +DATABASE_URL=postgres://azionelab:change-me@postgres:5432/azionelab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2cef73e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +.env.* +!.env.example diff --git a/docs/adr/0007-use-docker-compose-for-deployment.md b/docs/adr/0007-use-docker-compose-for-deployment.md new file mode 100644 index 0000000..9d52271 --- /dev/null +++ b/docs/adr/0007-use-docker-compose-for-deployment.md @@ -0,0 +1,27 @@ +# ADR-0007: Use Docker Compose for Deployment + +Date: 2026-04-28 + +## Status + +Accepted + +## Context + +AzioneLab needs a simple production-oriented deployment for a small theatre company website. The initial runtime services are Django with gunicorn, an Angular frontend served by nginx, PostgreSQL, and an nginx reverse proxy. + +The project does not need Celery, Redis, a container orchestrator, or a more complex platform at this stage. + +## Decision + +Use Docker Compose as the initial deployment mechanism. + +The Compose setup will define explicit `backend`, `frontend`, `postgres`, and `nginx` services under `infra/docker/compose.yml`. Configuration is provided through `.env`, PostgreSQL data is stored in a named volume, and only the reverse proxy publishes a host port. + +## Consequences + +- The deployment remains easy to understand, run, and review. +- The same topology can support local infrastructure checks and small production deployments. +- PostgreSQL persistence is explicit through a named volume. +- The setup can be replaced later if hosting or scaling needs outgrow Docker Compose. +- Operators must manage `.env`, backups, TLS, and image updates carefully. diff --git a/docs/architecture.md b/docs/architecture.md index eec4195..30c0ada 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -137,10 +137,12 @@ The initial deployment uses Docker Compose with these services: - `nginx`: public reverse proxy and static frontend server; - `frontend`: Angular build stage or static asset build source; - `backend`: Django application served by gunicorn; -- `db`: PostgreSQL database. +- `postgres`: PostgreSQL database. Only nginx should be publicly exposed. The backend and database should be reachable only on the internal Compose network. +The initial Compose files live under `infra/docker/`. The backend and frontend images are placeholders until the Django and Angular applications are implemented. + ## Architectural Constraints - Keep the booking workflow synchronous and explicit. @@ -153,4 +155,8 @@ Only nginx should be publicly exposed. The backend and database should be reacha ## Relevant ADRs -No ADRs are recorded yet. The technology stack and initial constraints are documented here from the project request. +- [ADR-0001: Use Django Monolith](adr/0001-use-django-monolith.md) +- [ADR-0002: Do Not Add an Async Task Queue Yet](adr/0002-no-async-task-queue.md) +- [ADR-0003: Use Opaque Tokens in QR Codes](adr/0003-qr-code-token-strategy.md) +- [ADR-0004: Use Email Confirmation for Reservations](adr/0004-email-confirmation-flow.md) +- [ADR-0007: Use Docker Compose for Deployment](adr/0007-use-docker-compose-for-deployment.md) diff --git a/docs/deployment.md b/docs/deployment.md index 938e1b7..54f82c1 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -5,10 +5,12 @@ AzioneLab should deploy with a simple Docker Compose topology: - `nginx`: public reverse proxy and static frontend server; - `frontend`: Angular build source or build stage for static assets; - `backend`: Django 5.2 LTS application served by gunicorn; -- `db`: PostgreSQL database. +- `postgres`: PostgreSQL database. Only nginx should expose public ports. The backend and database should stay on the internal Compose network. +The initial Compose setup is located at `infra/docker/compose.yml`. + ## Services ### nginx @@ -37,6 +39,8 @@ Deployment options: The first option is preferred for a simple production deployment because nginx can serve immutable built assets without a long-running Node process. +At the infrastructure placeholder stage, the `frontend` service serves a static placeholder page with nginx. The Angular build will replace this placeholder later. + ### backend The backend is a Django application served by gunicorn. @@ -51,7 +55,9 @@ Responsibilities: The backend should run database migrations before or during deployment through an explicit operational command, not as hidden startup magic unless that choice is documented later. -### db +At the infrastructure placeholder stage, the `backend` service runs gunicorn against a minimal placeholder WSGI application. The real Django application will replace it later. + +### postgres PostgreSQL is the only database service. @@ -84,6 +90,8 @@ Generated QR codes may also be generated on demand instead of stored as files. I ## Configuration +Copy `.env.example` to `.env` and replace all placeholder values before running or deploying the stack. + Required backend configuration: - `DJANGO_SECRET_KEY`; @@ -129,21 +137,25 @@ The exact commands will be finalized when application code and Compose files are Expected production-style flow: ```bash -docker compose build -docker compose run --rm backend python manage.py migrate -docker compose run --rm backend python manage.py collectstatic --noinput -docker compose up -d +docker compose --env-file .env -f infra/docker/compose.yml build +docker compose --env-file .env -f infra/docker/compose.yml run --rm backend python manage.py migrate +docker compose --env-file .env -f infra/docker/compose.yml run --rm backend python manage.py collectstatic --noinput +docker compose --env-file .env -f infra/docker/compose.yml up -d ``` Expected validation commands: ```bash -docker compose config -docker compose run --rm backend python manage.py check --deploy -docker compose run --rm backend python manage.py test +docker compose --env-file .env.example -f infra/docker/compose.yml config +docker compose --env-file .env -f infra/docker/compose.yml run --rm backend python manage.py check --deploy +docker compose --env-file .env -f infra/docker/compose.yml run --rm backend python manage.py test ``` -The repository does not yet define the canonical Docker-based test command. +The canonical repository check for the current infrastructure stage is: + +```bash +docker compose --env-file .env.example -f infra/docker/compose.yml config +``` ## Rollback diff --git a/docs/security-notes.md b/docs/security-notes.md index ce7ea88..809a9ff 100644 --- a/docs/security-notes.md +++ b/docs/security-notes.md @@ -115,12 +115,15 @@ Expected secret configuration: Use environment variables, Docker secrets, or deployment-managed secret injection. Documentation and example configuration should use placeholders only. +For the Docker Compose setup, copy `.env.example` to `.env` and replace placeholder values outside version control. The repository ignores `.env` and `.env.*` files except `.env.example`. + ## Deployment Security Deployment should follow least privilege: - expose only nginx publicly; - keep backend and database on an internal Docker network; +- do not publish backend, frontend, or PostgreSQL ports to the host in production; - avoid privileged containers; - use explicit image tags rather than `latest`; - persist PostgreSQL data in a named volume; diff --git a/docs/testing.md b/docs/testing.md index 928ca48..f23e36d 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -7,7 +7,7 @@ All tests should run inside Docker containers. ## Canonical test command ```bash -CHANGE_ME +docker compose --env-file .env.example -f infra/docker/compose.yml config ``` ## Test categories diff --git a/infra/docker/backend/Dockerfile b/infra/docker/backend/Dockerfile new file mode 100644 index 0000000..763831f --- /dev/null +++ b/infra/docker/backend/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.13.4-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN pip install --no-cache-dir \ + "Django==5.2.3" \ + "djangorestframework==3.16.0" \ + "gunicorn==23.0.0" \ + "psycopg[binary]==3.2.9" + +RUN useradd --create-home --shell /usr/sbin/nologin appuser + +COPY placeholder_wsgi.py /app/placeholder_wsgi.py + +USER appuser + +EXPOSE 8000 + +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "placeholder_wsgi:application"] diff --git a/infra/docker/backend/placeholder_wsgi.py b/infra/docker/backend/placeholder_wsgi.py new file mode 100644 index 0000000..ac8f182 --- /dev/null +++ b/infra/docker/backend/placeholder_wsgi.py @@ -0,0 +1,10 @@ +def application(environ, start_response): + body = b"AzioneLab backend placeholder\n" + start_response( + "503 Service Unavailable", + [ + ("Content-Type", "text/plain; charset=utf-8"), + ("Content-Length", str(len(body))), + ], + ) + return [body] diff --git a/infra/docker/compose.yml b/infra/docker/compose.yml new file mode 100644 index 0000000..87e31a5 --- /dev/null +++ b/infra/docker/compose.yml @@ -0,0 +1,77 @@ +services: + backend: + build: + context: ./backend + image: azionelab-backend:local + environment: + DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY} + DJANGO_ALLOWED_HOSTS: ${DJANGO_ALLOWED_HOSTS} + DJANGO_CSRF_TRUSTED_ORIGINS: ${DJANGO_CSRF_TRUSTED_ORIGINS} + DJANGO_DEBUG: ${DJANGO_DEBUG:-false} + DATABASE_URL: ${DATABASE_URL} + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_HOST: ${POSTGRES_HOST:-postgres} + POSTGRES_PORT: ${POSTGRES_PORT:-5432} + expose: + - "${BACKEND_PORT:-8000}" + depends_on: + postgres: + condition: service_healthy + networks: + - internal + restart: unless-stopped + + frontend: + build: + context: ./frontend + image: azionelab-frontend:local + expose: + - "${FRONTEND_PORT:-8080}" + networks: + - internal + restart: unless-stopped + + postgres: + image: postgres:16.3-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 5 + networks: + - internal + restart: unless-stopped + + nginx: + image: nginx:1.27.0-alpine + ports: + - "${NGINX_HTTP_PORT:-8080}:80" + environment: + BACKEND_HOST: ${BACKEND_HOST:-backend} + BACKEND_PORT: ${BACKEND_PORT:-8000} + FRONTEND_HOST: ${FRONTEND_HOST:-frontend} + FRONTEND_PORT: ${FRONTEND_PORT:-8080} + NGINX_ENVSUBST_FILTER: "^(BACKEND_HOST|BACKEND_PORT|FRONTEND_HOST|FRONTEND_PORT)$" + volumes: + - ./nginx/templates:/etc/nginx/templates:ro + depends_on: + - backend + - frontend + networks: + - internal + restart: unless-stopped + +volumes: + postgres_data: + +networks: + internal: + driver: bridge diff --git a/infra/docker/frontend/Dockerfile b/infra/docker/frontend/Dockerfile new file mode 100644 index 0000000..0f494ea --- /dev/null +++ b/infra/docker/frontend/Dockerfile @@ -0,0 +1,6 @@ +FROM nginx:1.27.0-alpine + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY html/ /usr/share/nginx/html/ + +EXPOSE 8080 diff --git a/infra/docker/frontend/html/index.html b/infra/docker/frontend/html/index.html new file mode 100644 index 0000000..c8733e2 --- /dev/null +++ b/infra/docker/frontend/html/index.html @@ -0,0 +1,14 @@ + + +
+ + +The Angular application build will replace this page.
+