Add postgres service (todo §3); pin postgres:18.4-alpine3.23, one DB per Kratos/Keto/Hydra via init.sql
This commit is contained in:
@@ -443,6 +443,7 @@ src/menu-config.ts loadMenuConfig()/defineMenu(): read config/menu.ts (central
|
||||
views/ Core EJS templates (index = the app-shell People dashboard, 403/404/500, partials/ incl. app shell, nav tree, filter bar, data table, pagination, form field, auth card, menu/popover, theme switch, icon sprite)
|
||||
public/ Static assets under /public/ (css/styles.css + auth.css, favicon, robots.txt)
|
||||
config/menu.ts Central menu override + branding (optional; defaults apply if absent)
|
||||
ory/ Ory service config + storage init (postgres/init/init.sql: one DB per Kratos/Keto/Hydra)
|
||||
plugins/ Drop-in plugin folders (scanned at /app/plugins; bind-mount or bake in) (planned)
|
||||
docs/ Reference docs (plugin-contract.md — the authoritative plugin API)
|
||||
e2e/ Playwright visual + functional E2E (Dockerfile.e2e + compose.e2e.yml run it)
|
||||
|
||||
22
compose.yml
22
compose.yml
@@ -11,3 +11,25 @@ services:
|
||||
CACHE_TEMPLATES: "true"
|
||||
REQUIRE_SECURE_SECRETS: "true"
|
||||
restart: unless-stopped
|
||||
|
||||
# Ory's storage only (Kratos/Keto/Hydra) — the web app never connects here.
|
||||
# init/init.sql creates one database per service. Dev defaults below; supply
|
||||
# POSTGRES_USER/PASSWORD via env in production.
|
||||
postgres:
|
||||
image: postgres:18.4-alpine3.23
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-ory}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ory}
|
||||
POSTGRES_DB: ory
|
||||
volumes:
|
||||
- ./ory/postgres/init:/docker-entrypoint-initdb.d:ro
|
||||
- pgdata:/var/lib/postgresql # PG18+: mount the parent, not /data (version-subdir layout)
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-ory} -d ory"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
|
||||
6
ory/postgres/init/init.sql
Normal file
6
ory/postgres/init/init.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
-- Runs once on first boot (docker-entrypoint-initdb.d), as the POSTGRES_USER.
|
||||
-- One database per Ory service: each owns its schema and runs its own migrations,
|
||||
-- so they never collide. The web app never connects here (stateless — see README).
|
||||
CREATE DATABASE kratos;
|
||||
CREATE DATABASE keto;
|
||||
CREATE DATABASE hydra;
|
||||
23
src/postgres.test.ts
Normal file
23
src/postgres.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// Guards the Ory Postgres config (§3): image stays pinned to an exact version
|
||||
// (AGENTS.md rule) and each Ory service keeps its own database. Real container
|
||||
// behaviour is verified by booting postgres in CI/e2e; this catches edits.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const read = (p: string) => readFileSync(new URL(`../${p}`, import.meta.url), "utf8");
|
||||
const ORY_DATABASES = ["hydra", "keto", "kratos"]; // one DB per Ory service
|
||||
|
||||
test("compose pins the postgres image to an exact version", () => {
|
||||
const tag = read("compose.yml").match(/image:\s*postgres:(\S+)/)?.[1];
|
||||
assert.ok(tag, "compose.yml pins a postgres image");
|
||||
assert.match(tag, /^\d+\.\d+/, `${tag} pins major.minor`);
|
||||
assert.doesNotMatch(tag, /latest|[\^~*]/, `${tag} is exact, not floating`);
|
||||
});
|
||||
|
||||
test("init SQL gives each Ory service its own database", () => {
|
||||
const sql = read("ory/postgres/init/init.sql");
|
||||
for (const db of ORY_DATABASES) {
|
||||
assert.match(sql, new RegExp(`CREATE DATABASE ${db}\\b`, "i"), `creates ${db}`);
|
||||
}
|
||||
});
|
||||
2
todo.md
2
todo.md
@@ -57,7 +57,7 @@ everything via Docker.
|
||||
- [x] Go over all tests and combine/unify ones that cover the same stuff or are very related and could be combined in a good way. Remove tests that aren't helping, we only want tests that are actually helpful to us. → Reviewed all 24 test files. The suite already follows the deliberate per-module "matrix + edge" pattern from the §0/§1 merge (line 22), so most files carry no fat and force-merging distinct concerns would only hurt readability. Removed the genuine §2-era overlaps, all in `app.test.ts`: merged the two HTTP static tests into one (GET/HEAD + traversal/NUL→403), and dropped the standalone "renders the 403 error page" `ejs.renderFile` stopgap (its comment even said "403 has no first-party route yet") — the gated plugin route now exercises 403 over HTTP, so the template assertions (status + 403.ejs body + stylesheet link) moved there; also dropped the now-unused `ejs` import. Unified `view-resolver.test.ts`'s two `resolveViewPath` cases (resolve + reject) into one. 113 → 110 tests, zero coverage lost; typecheck + tests green.
|
||||
|
||||
## 3. Ory stack — compose + config
|
||||
- [ ] `postgres` service (pinned tag); separate DB/schema per Kratos/Keto/Hydra.
|
||||
- [x] `postgres` service (pinned tag); separate DB/schema per Kratos/Keto/Hydra. → `compose.yml` `postgres` service pinned to `postgres:18.4-alpine3.23` (verified latest stable PG + newest Alpine the official image ships); `ory/postgres/init/init.sql` (mounted at `docker-entrypoint-initdb.d`) creates one DB per service (`kratos`/`keto`/`hydra`) so each owns its schema + migrations. Dev defaults (`ory`/`ory`, env-overridable for prod), named `pgdata` volume mounted at `/var/lib/postgresql` (PG18+ version-subdir layout — not `/data`), `pg_isready` healthcheck. Web app never connects. Verified live: boots healthy, three DBs present, then torn down. `postgres.test.ts` guards the pin + DB-per-service. typecheck + 112 units green.
|
||||
- [ ] `kratos` service (pinned) + `migrate`; identity schema (traits: email, name).
|
||||
- [ ] Kratos self-service flows (login, registration, recovery, verification, settings) → return URLs at our themed pages.
|
||||
- [ ] Kratos OIDC/SSO providers (Google/Microsoft/SAML) config (secrets via env). **None enabled by default** — a clean clone runs password-only; a provider activates purely by supplying its env creds.
|
||||
|
||||
Reference in New Issue
Block a user