diff --git a/docs/web/architecture.md b/docs/web/architecture.md index ee389bd5..0d3e6c64 100644 --- a/docs/web/architecture.md +++ b/docs/web/architecture.md @@ -565,11 +565,12 @@ are not seeded either. **Environments**: -| Environment | Compose file | -| ----------- | -------------------------- | -| Development | `docker-compose.dev.yaml` | -| Testing | `docker-compose.test.yaml` | -| Production | `docker-compose.prod.yaml` | +| Environment | Compose file | +| --------------- | ------------------------------- | +| Development | `docker-compose.dev.yaml` | +| Testing | `docker-compose.test.yaml` | +| Production | `docker-compose.prod.yaml` | +| Production seed | `docker-compose.prod.seed.yaml` | For details, see [Docker Guide](docker.md). diff --git a/docs/web/diagrams/deployment.md b/docs/web/diagrams/deployment.md index 38d80f20..e5190802 100644 --- a/docs/web/diagrams/deployment.md +++ b/docs/web/diagrams/deployment.md @@ -41,7 +41,7 @@ no data flowing over them. | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Dev network** (`devnet`, bridge) | Internal DNS resolves service names such as `sqlserver` and `api.core`. Volumes: `sqlserverdata-dev`, `nuget-cache-dev`, `seaweedfsdata-dev`. Startup order: SQL Server (health check) → migrations (run-once) → seed (run-once, depends on `seaweedfs`) → API (long-running, depends on seed). | | **Test network** (`testnet`, bridge) | All test components are isolated; fresh DB instance each run (`CLEAR_DATABASE=true`, ephemeral `sqlserverdata-test` / `seaweedfsdata-test` volumes). `api.specs`, `unit.tests`, and `frontend.tests` all run as separate containers, in parallel once seeding completes; results are aggregated. | -| **Prod network** (`prodnet`, bridge) | Isolated from dev/test networks. Volumes: `biergartendata-prod` and `seaweedfsdata-prod` (both external, so `down -v` keeps them), plus `nuget-cache-prod`. Startup order matches dev: SQL Server (health check) → migrations → seed → API → frontend, with `frontend` the only service publishing a host port. | +| **Prod network** (`prodnet`, bridge, named `biergarten-prodnet`) | Isolated from dev/test networks. Volumes: `biergartendata-prod` and `seaweedfsdata-prod` (both external, so `down -v` keeps them), plus `nuget-cache-prod`. Startup order: SQL Server (health check) → migrations → API → frontend, with `frontend` the only service publishing a host port. Seeding runs on demand from `docker-compose.prod.seed.yaml`, as its own Compose project joining `biergarten-prodnet`. | | **Database-only environment** | Same devnet volumes/images as `docker-compose.dev.yaml`, minus `api.core`, `frontend`, and `mailpit` — for working against the database (and object storage, since `seaweedfs` is still included) without running the API or website. | | **Minimal environment** | Database + migrations + seed + `seaweedfs` + mailpit, no API or frontend container — for frontend development against an `API.Core` process run outside Docker. | | `unit.tests` | Builds `Dockerfile.tests`, which restores/builds against `Biergarten.API.slnx` and then runs `dotnet test` for every `Features/*.Tests` project in turn (`Features.Users.Tests`, `Features.Breweries.Tests`, `Features.Emails.Tests`, `Features.PhotoUpload.Tests`), mocking each handler's repository/service dependencies with Moq — no real database needed. | diff --git a/docs/web/diagrams/deployment/prod.mmd b/docs/web/diagrams/deployment/prod.mmd index 6c267b92..7f094561 100644 --- a/docs/web/diagrams/deployment/prod.mmd +++ b/docs/web/diagrams/deployment/prod.mmd @@ -5,7 +5,8 @@ sequenceDiagram participant ProdAPI as API Container
ASP.NET Core 10
:8080 HTTP / :8081 HTTPS end - ProdMig-->>ProdDB: depends_on + ProdMig-->>ProdDB: depends_on: sqlserver healthy ProdMig->>ProdDB: Run migrations - ProdAPI-->>ProdDB: depends_on: sql-server healthy (not migrations/seed) + ProdAPI-->>ProdMig: depends_on: migrations completed ProdAPI->>ProdDB: Connect and serve + Note over ProdDB: Seeding is a separate run-once job
(docker-compose.prod.seed.yaml) diff --git a/docs/web/docker.md b/docs/web/docker.md index 7c5a9b1a..c19d8e5d 100644 --- a/docs/web/docker.md +++ b/docs/web/docker.md @@ -139,16 +139,17 @@ containers, which would end the run prematurely. See - Security hardening - Only `frontend` publishes a port to the host, bound to `127.0.0.1` so a reverse proxy can sit in front of it; `sqlserver`, `api.core` and `seaweedfs` are - reachable only from other containers on `prodnet` -- Seeded catalogue, with its AI-generated origin disclosed in the app. See - [Production data](#production-data) below. + reachable only from other containers on `prodnet` (named `biergarten-prodnet` + so the seed job can join it from its own Compose project) +- Seeding is a separate, explicitly run compose file + (`docker-compose.prod.seed.yaml`), so an `up` or a restart leaves the existing + catalogue as it is. See [Production data](#production-data) below. **Services**: ```yaml sqlserver # Production SQL Server (internal only) database.migrations # Schema updates only -database.seed # Loads the catalogue, runs once api.core # Production API (internal only, no published ports) seaweedfs # S3-compatible object storage (internal only) frontend # Production website (127.0.0.1:5656, the only host binding) @@ -169,10 +170,27 @@ Docker seeds each volume with the ownership the image expects and labels it for SELinux, so the host needs no `chown` and no `:z`/`:Z` mount options on enforcing distributions such as RHEL, AlmaLinux and Fedora. +#### Seeding production (`docker-compose.prod.seed.yaml`) + +`database.seed` lives in its own compose file, run on demand once the prod stack +is up and `sqlserver` is healthy: + +```bash +docker compose --env-file web/.env.prod -f web/docker-compose.prod.seed.yaml \ + run --rm --build database.seed +``` + +The job runs as the `biergarten-prod-seed` Compose project and attaches to the +external `biergarten-prodnet` network, which the main prod stack creates, so it +reaches `sqlserver` and `seaweedfs` under the same hostnames the API uses. It +exits when seeding finishes; `--rm` removes the container. Bring the prod stack +up first — the seed project has no visibility into the other project's health +checks, so a run started before SQL Server is accepting connections fails. + #### Production data -Production runs `database.seed`, so the catalogue it serves is the AI-generated -fixture data produced by the pipeline. Per +A production deployment seeded this way serves the AI-generated fixture data +produced by the pipeline. Per [Pipeline ethics, bias, and known issues](../pipeline/ETHICS-AND-KNOWN-ISSUES.md), anyone interacting with an application seeded from it has to be told upfront that the content is AI-generated. @@ -184,8 +202,9 @@ on. It is part of the server-rendered HTML rather than mounted after hydration, the disclosure is on screen with the first paint. Acknowledging it hides the dialog for the remainder of that page load; a new load shows it again. -Keep `database.seed` and the dialog together. A production deployment that seeds -the catalogue without the disclosure does not satisfy the ethics documentation. +Keep the seed job and the dialog together: any deployment that runs +`docker-compose.prod.seed.yaml` needs the disclosure in place to satisfy the +ethics documentation. ### 4. Database only (`docker-compose.db.yaml`) diff --git a/docs/web/environment-variables.md b/docs/web/environment-variables.md index d47afe53..656d0cc1 100644 --- a/docs/web/environment-variables.md +++ b/docs/web/environment-variables.md @@ -317,6 +317,7 @@ from the template in place; edit them manually if you go that route. - `web/docker-compose.dev.yaml` → `web/.env.dev` - `web/docker-compose.test.yaml` → `web/.env.test` - `web/docker-compose.prod.yaml` → `web/.env.prod` +- `web/docker-compose.prod.seed.yaml` → `web/.env.prod` ## Variable reference table