build: add shared Postgres and Valkey to compose

- postgres replaces keycloak-db and holds a keycloak and a mancala database,
  each with its own user, created by apps/postgres/init
- valkey with an append-only file for live game state and caches
- backend gets DATABASE_URL and VALKEY_URL
This commit is contained in:
Halit Aksoy 2026-10-16 00:05:21 +03:00
parent d0004742f5
commit 29fa443b93
5 changed files with 70 additions and 10 deletions

View File

@ -12,6 +12,23 @@ yarn build:packages # from the repo root
After changing a package, rebuild it (or run `yarn dev` in it to rebuild on change); the apps pick up the new `dist`. After changing a package, rebuild it (or run `yarn dev` in it to rebuild on change); the apps pick up the new `dist`.
Postgres (games and Keycloak), Valkey (live games and caches) and Keycloak run with Docker Compose. Create
`apps/.env` once, then start them:
```sh
cd apps
cat > .env <<EOF
POSTGRES_PASSWORD=postgres
KEYCLOAK_DB_PASSWORD=keycloak
KEYCLOAK_ADMIN_PASSWORD=admin
MANCALA_DB_PASSWORD=mancala
EOF
docker compose up -d --build postgres valkey keycloak
```
Postgres is on `localhost:5433` and Valkey on `localhost:6379`; the backend's defaults point there. Then run
`yarn dev` in `apps/backend` (port 5005) and in `apps/frontend` (port 1234).
Docker images for the backend and frontend are built from the repo root: Docker images for the backend and frontend are built from the repo root:
```sh ```sh

View File

@ -11,8 +11,13 @@ services:
KEYCLOAK_JWKS_URL: http://keycloak:8080/realms/mancala/protocol/openid-connect/certs KEYCLOAK_JWKS_URL: http://keycloak:8080/realms/mancala/protocol/openid-connect/certs
KEYCLOAK_CLIENT_IDS: mancala-web KEYCLOAK_CLIENT_IDS: mancala-web
LEGACY_USERKEY_AUTH: ${LEGACY_USERKEY_AUTH:-true} LEGACY_USERKEY_AUTH: ${LEGACY_USERKEY_AUTH:-true}
DATABASE_URL: postgres://mancala:${MANCALA_DB_PASSWORD}@postgres:5432/mancala
VALKEY_URL: redis://valkey:6379
ports: ports:
- "5000:6000" - "5000:6000"
depends_on:
- postgres
- valkey
frontend: frontend:
image: jhalitaksoy/mancala-frontend:latest image: jhalitaksoy/mancala-frontend:latest
build: build:
@ -22,16 +27,33 @@ services:
KEYCLOAK_URL: ${KEYCLOAK_HOSTNAME:-http://localhost:8080} KEYCLOAK_URL: ${KEYCLOAK_HOSTNAME:-http://localhost:8080}
ports: ports:
- "3000:3000" - "3000:3000"
keycloak-db: # Shared by Keycloak and the game, each with its own database and user (see postgres/init).
postgres:
image: postgres:17.6 image: postgres:17.6
environment: environment:
POSTGRES_DB: keycloak POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in apps/.env}
POSTGRES_USER: keycloak KEYCLOAK_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD:?set KEYCLOAK_DB_PASSWORD in apps/.env}
POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD:?set KEYCLOAK_DB_PASSWORD in apps/.env} MANCALA_DB_PASSWORD: ${MANCALA_DB_PASSWORD:?set MANCALA_DB_PASSWORD in apps/.env}
volumes: volumes:
- keycloak-db:/var/lib/postgresql/data - postgres-data:/var/lib/postgresql/data
- ./postgres/init:/docker-entrypoint-initdb.d:ro
ports:
- "127.0.0.1:5433:5432"
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"] test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
# Live game state and caches; the append-only file keeps live games across restarts.
valkey:
image: valkey/valkey:9.1.2
command: ["valkey-server", "--appendonly", "yes"]
volumes:
- valkey-data:/data
ports:
- "127.0.0.1:6379:6379"
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 5s interval: 5s
timeout: 5s timeout: 5s
retries: 10 retries: 10
@ -39,7 +61,7 @@ services:
image: jhalitaksoy/mancala-keycloak:latest image: jhalitaksoy/mancala-keycloak:latest
build: ./keycloak build: ./keycloak
environment: environment:
KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD} KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
# Public URL of Keycloak; https://auth.playmancala.app in production # Public URL of Keycloak; https://auth.playmancala.app in production
@ -52,8 +74,9 @@ services:
ports: ports:
- "127.0.0.1:8080:8080" - "127.0.0.1:8080:8080"
depends_on: depends_on:
keycloak-db: postgres:
condition: service_healthy condition: service_healthy
volumes: volumes:
keycloak-db: postgres-data:
valkey-data:

View File

@ -38,12 +38,17 @@ console too, or delete the realm and restart Keycloak.
```sh ```sh
cd apps cd apps
cat > .env <<EOF cat > .env <<EOF
POSTGRES_PASSWORD=change-me
KEYCLOAK_DB_PASSWORD=change-me KEYCLOAK_DB_PASSWORD=change-me
KEYCLOAK_ADMIN_PASSWORD=change-me KEYCLOAK_ADMIN_PASSWORD=change-me
MANCALA_DB_PASSWORD=change-me
EOF EOF
docker compose up -d --build keycloak docker compose up -d --build keycloak
``` ```
Keycloak uses the `keycloak` database of the shared `postgres` service; `apps/postgres/init` creates it the first time
the Postgres volume is initialised.
Keycloak is then at http://localhost:8080 and the admin console at http://localhost:8080/admin. Keycloak is then at http://localhost:8080 and the admin console at http://localhost:8080/admin.
In production set `KEYCLOAK_HOSTNAME=https://auth.playmancala.app`; nginx proxies that host to port 8080. In production set `KEYCLOAK_HOSTNAME=https://auth.playmancala.app`; nginx proxies that host to port 8080.

View File

@ -0,0 +1,15 @@
#!/bin/bash
# Runs once, when the Postgres data directory is created: one database and user each for Keycloak and the game.
set -euo pipefail
: "${KEYCLOAK_DB_PASSWORD:?KEYCLOAK_DB_PASSWORD is not set}"
: "${MANCALA_DB_PASSWORD:?MANCALA_DB_PASSWORD is not set}"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" \
-v keycloak_password="$KEYCLOAK_DB_PASSWORD" \
-v mancala_password="$MANCALA_DB_PASSWORD" <<'EOSQL'
CREATE ROLE keycloak LOGIN PASSWORD :'keycloak_password';
CREATE DATABASE keycloak OWNER keycloak;
CREATE ROLE mancala LOGIN PASSWORD :'mancala_password';
CREATE DATABASE mancala OWNER mancala;
EOSQL

View File

@ -2,7 +2,7 @@
Work through the tasks in order; each ends with its tests passing and a commit. Tick a task when it is committed. Work through the tasks in order; each ends with its tests passing and a commit. Tick a task when it is committed.
- [ ] 1. **Infrastructure**: `postgres` service with init script for the `keycloak` and `mancala` databases - [x] 1. **Infrastructure**: `postgres` service with init script for the `keycloak` and `mancala` databases
(replacing `keycloak-db`), `valkey` service, backend `DATABASE_URL` / `VALKEY_URL`, `.env` variables, README. (replacing `keycloak-db`), `valkey` service, backend `DATABASE_URL` / `VALKEY_URL`, `.env` variables, README.
- [ ] 2. **Core types and history helpers**: `PlayerSummary`, `MoveRecord`, summaries, `GameRecord`, - [ ] 2. **Core types and history helpers**: `PlayerSummary`, `MoveRecord`, summaries, `GameRecord`,
`TURN_TIMEOUT_MS`, new channels, `offlineSince`; `getInitialBoard`, `getBoardAfterMove`, `describeMove`, `TURN_TIMEOUT_MS`, new channels, `offlineSince`; `getInitialBoard`, `getBoardAfterMove`, `describeMove`,