diff --git a/Services/Website/README.md b/Services/Website/README.md index f2325ac..dd240ae 100644 --- a/Services/Website/README.md +++ b/Services/Website/README.md @@ -10,7 +10,7 @@ The service: - Returns a custom not-found (404) HTML page, localized like the landing page, for any request that matches neither a route nor a static file. - Compresses responses (gzip/deflate) above a configurable size when the client advertises support. - Stamps a hardened set of security headers on every response. -- Persists data through [Fluent](https://github.com/hummingbird-project/hummingbird-fluent), against either an ephemeral in-memory SQLite database (the default — no external infrastructure) or a MySQL/MariaDB server, selected by a single configuration key. +- Persists data through [Fluent](https://github.com/hummingbird-project/hummingbird-fluent), against either an ephemeral in-memory SQLite database (the default — no external infrastructure) or a PostgreSQL server, selected by a single configuration key. ## Requirements - Swift 6.3 toolchain (`swift-tools-version:6.3`). @@ -55,7 +55,7 @@ the following sources, **highest precedence first**: 5. Built-in defaults The two files play different roles: -- **`.env`** (git-ignored) holds your deployment values — it is the file the Makefile and Compose read for the `${VAR}` placeholders, and typically selects the MySQL/MariaDB backend. +- **`.env`** (git-ignored) holds your deployment values — it is the file the Makefile and Compose read for the `${VAR}` placeholders, and typically selects the PostgreSQL backend. - **`.env.local`** (tracked) holds the local development values — the in-memory database and `debug` logging, plus the image/deployment placeholders the Makefile falls back to when no `.env` exists. Because it sits *above* `.env`, a direct launch (`swift run` or a debugger) runs against the local values even when `.env` points at a deployment, the same way `docker-compose.override.yml` overrides the base Compose file. Compose itself never reads it, and the production image does not ship it — only the executable, its resources, and the static files are staged into the final stage. ### Environment variable naming @@ -92,10 +92,10 @@ A dotted config key maps to an environment variable by upper-casing, splitting c ### Persistence | Config key | Environment variable | Default | Description | | --- | --- | --- | --- | -| `database.driver` | `DATABASE_DRIVER` | `inMemory` | Backend: `inMemory` (ephemeral SQLite, no infrastructure) or `mysql` (MySQL/MariaDB). | +| `database.driver` | `DATABASE_DRIVER` | `inMemory` | Backend: `inMemory` (ephemeral SQLite, no infrastructure) or `postgres` (PostgreSQL). | | `database.migrate` | `DATABASE_MIGRATE` (flag `--database-migrate`) | `false` | When set, run the migrations and exit instead of serving. | -| `database.host` | `DATABASE_HOST` | `localhost` | MySQL/MariaDB host. Ignored for `inMemory`. | -| `database.port` | `DATABASE_PORT` | `3306` | MySQL/MariaDB port. Ignored for `inMemory`. | +| `database.host` | `DATABASE_HOST` | `localhost` | PostgreSQL host. Ignored for `inMemory`. | +| `database.port` | `DATABASE_PORT` | `5432` | PostgreSQL port. Ignored for `inMemory`. | | `database.name` | `DATABASE_NAME` | `loud` | Database name. Ignored for `inMemory`. | | `database.username` | `DATABASE_USERNAME` | `loud` | Database username. Ignored for `inMemory`. | | `database.password` | `DATABASE_PASSWORD` | _(empty)_ | Database password. Provide via the environment/a secret — never commit it. | @@ -136,7 +136,7 @@ swift run Website # binds to Hummingbird's default 127.0.0.1:8080 swift run Website --http-host 0.0.0.0 --http-port 9000 --log-level debug ``` -A direct run picks up the local development overrides from `.env.local` (in-memory database, `debug` logging) over whatever `.env` configures. To run against another backend, override per launch — e.g. `DATABASE_DRIVER=mysql swift run Website` — since process environment variables outrank both files. +A direct run picks up the local development overrides from `.env.local` (in-memory database, `debug` logging) over whatever `.env` configures. To run against another backend, override per launch — e.g. `DATABASE_DRIVER=postgres swift run Website` — since process environment variables outrank both files. Or via the Makefile / Docker (uses `docker-compose.override.yml`, which builds from source and sets `LOG_LEVEL=debug`): ```sh @@ -154,8 +154,8 @@ The service persists data through Fluent and selects its backend at runtime with ### In-memory (default) With no configuration, the service uses an ephemeral in-memory SQLite database. It is created and **migrated on startup** every launch, so `swift run Website` and `docker compose up` work with no external database — ideal for local development and tests. -### MySQL / MariaDB -Set `DATABASE_DRIVER=mysql` and the connection values (`DATABASE_HOST`, `DATABASE_NAME`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, …). Unlike the in-memory backend, a MySQL/MariaDB database is **not** migrated on boot — a shared database is migrated out of band so multiple instances never race: +### PostgreSQL +Set `DATABASE_DRIVER=postgres` and the connection values (`DATABASE_HOST`, `DATABASE_NAME`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, …). Unlike the in-memory backend, a PostgreSQL database is **not** migrated on boot — a shared database is migrated out of band so multiple instances never race: ```sh # Run the registered migrations against the configured database, then exit. swift run Website --database-migrate @@ -163,14 +163,14 @@ swift run Website --database-migrate docker compose -f docker-compose.yml run --rm website --database-migrate ``` -A local MariaDB for development lives behind the `database` Compose profile (so a plain `docker compose up` still runs in-memory). Its data directory is bind-mounted to `Tests/DB` (git-ignored), so the database survives `db-unmount` and container restarts: +A local PostgreSQL for development lives behind the `database` Compose profile (so a plain `docker compose up` still runs in-memory). Its data directory is bind-mounted to `Tests/DB` (git-ignored), so the database survives `db-unmount` and container restarts: ```sh -make db-mount # start MariaDB (docker compose --profile database up --wait mariadb) +make db-mount # start PostgreSQL (docker compose --profile database up --wait postgres) make db-migrate # run migrations against it -make db-shell # open a SQL shell on it +make db-shell # open a SQL shell on it (psql) make db-unmount # stop and remove the container (data kept in Tests/DB) make db-reset # stop and remove the container -DATABASE_DRIVER=mysql make site-mount # run the site against MariaDB +DATABASE_DRIVER=postgres make site-mount # run the site against PostgreSQL ``` > **Note:** because the data lives in the bind-mounted `Tests/DB` folder rather than a named volume, `db-reset`'s `--volumes` flag does **not** clear it. To start from an empty database, delete `Tests/DB` by hand. @@ -187,11 +187,11 @@ make pkg-test Tests use the [Swift Testing](https://developer.apple.com/documentation/testing/) framework. The `Tests/Website.xctestplan` covers the service's two targets — `WebsiteTests` (the executable/integration tests) and `WebsiteLibraryTests` (the library unit tests) — plus the local packages' suites: `InfrastructureTests`, `PersistenceTests`, `LocalizationTests`, and `UtilityTests`. -The `Persistence` package has its own suite (run it from `Packages/Persistence`). Its tests run against the in-memory backend by default; the MySQL integration test is skipped unless a database is pointed at via `MYSQL_TEST_HOST` (with optional `MYSQL_TEST_PORT`/`NAME`/`USERNAME`/`PASSWORD`), so `swift test` stays runnable with no database: +The `Persistence` package has its own suite (run it from `Packages/Persistence`). Its tests run against the in-memory backend by default; the PostgreSQL integration test is skipped unless a database is pointed at via `POSTGRES_TEST_HOST` (with optional `POSTGRES_TEST_PORT`/`NAME`/`USERNAME`/`PASSWORD`), so `swift test` stays runnable with no database: ```sh cd ../../Packages/Persistence && swift test # in-memory only -# With the local MariaDB up (make db-mount): -cd ../../Packages/Persistence && MYSQL_TEST_HOST=127.0.0.1 swift test +# With the local PostgreSQL up (make db-mount): +cd ../../Packages/Persistence && POSTGRES_TEST_HOST=127.0.0.1 swift test ``` ## Deployment @@ -262,8 +262,8 @@ The Makefile and Compose files read these from a `.env` file (or the environment | `LOG_LEVEL` | Runtime log level (default `info`). | | `HTTP_SERVER_NAME` | Runtime server name (default `LoudWebsite`). | | `SECURITY_STRICT_TRANSPORT_SECURITY` | HSTS header value (default `max-age=31536000; includeSubDomains`). | -| `DATABASE_DRIVER` | `inMemory` or `mysql`. The production Compose file defaults it to `mysql`; the local override defaults back to the in-memory backend. | -| `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USERNAME`, `DATABASE_PASSWORD` | MySQL/MariaDB connection (when `DATABASE_DRIVER=mysql`). Provide the password via a secret. | +| `DATABASE_DRIVER` | `inMemory` or `postgres`. The production Compose file defaults it to `postgres`; the local override defaults back to the in-memory backend. | +| `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USERNAME`, `DATABASE_PASSWORD` | PostgreSQL connection (when `DATABASE_DRIVER=postgres`). Provide the password via a secret. | | `DATABASE_TLS` | TLS posture when connecting: `off`, `prefer`, or `require` (default `prefer` in production — set `require` when the database enforces TLS, so a stripped connection fails instead of silently downgrading to plaintext). | Run the migrations against the production database once before (or during) rollout: `docker compose -f docker-compose.yml run --rm website --database-migrate`.