feat: Sprint 1 remainder — Postgres store, Docker client, warm pool, sessions route
Mirror to GitHub / mirror (push) Canceled after 0s

- PostgresUserStore (Queryable) + migration + env-gated wiring
- DockerodeClient behind injected DockerEngine
- ImageManager, WarmPool, Dockerfile.wordpress, docker-compose
- POST /sessions spins up sandbox (503 when unconfigured)
- ADRs 0014-0015; 68 unit tests green
This commit is contained in:
SinachPat
2026-08-15 20:18:34 +01:00
parent 97f31315bd
commit df2315087d
23 changed files with 1119 additions and 11 deletions
+26
View File
@@ -0,0 +1,26 @@
# 14. Postgres user store via a Queryable boundary; schema in SQL migrations
- **Status:** Accepted
- **Date:** 2026-08-15
## Context
ADR 0012 deferred Postgres behind the `UserStore` interface. Sprint 1 now ships the real implementation. Postgres is not available in the dev environment, so the store had to be testable without a database.
## Decision
`PostgresUserStore` uses `node-postgres` (`pg`) but depends on a minimal `Queryable` interface (`query(text, values)`) instead of `pg.Pool` directly. `UserStore` methods became async. The `users` table lives in `api/migrations/001_init.sql`. `index.ts` selects Postgres when `DATABASE_URL` is set, else the in-memory store.
### Options considered
- ORM (Prisma/Drizzle) with migrations.
- Raw `pg` behind a `Queryable` interface (chosen).
### Rejected
- ORM — adds tooling and a codegen step for a two-statement surface; the raw SQL is reviewable and the interface keeps tests database-free.
## Consequences
- `InMemoryUserStore` and `PostgresUserStore` share the same async `UserStore` contract; swapping is env-driven.
- Auth data is durable when `DATABASE_URL` is configured; the migration must be applied before first use.
@@ -0,0 +1,27 @@
# 15. Docker daemon client via dockerode behind an injected engine; sandbox gated by env
- **Status:** Accepted
- **Date:** 2026-08-15
## Context
ADR 0013 deferred the real Docker client. Sprint 1 now ships it. There is still no Docker daemon in the dev environment, so the client had to be testable without one.
## Decision
`DockerodeClient` implements `DockerClient` using `dockerode`, but depends on an injected `DockerEngine` (a minimal `createContainer`/`getContainer` surface) instead of `dockerode` directly. The API enables it via `WUR_ENABLE_SANDBOX=1`; when off, `POST /sessions` returns 503. `index.ts` constructs `new Docker()` (dockerode auto-detects `DOCKER_HOST` or the unix socket).
### Options considered
- Raw Docker Engine HTTP over the unix socket.
- dockerode behind an injected engine (chosen).
### Rejected
- Raw HTTP — dockerode already handles unix sockets, TLS, and API version negotiation; reimplementing it is pure waste.
## Consequences
- `DockerodeClient` is unit-tested with a fake engine; only the daemon wiring remains to be verified on a Docker host.
- Sandbox spin-up is opt-in, so local dev without Docker still boots and `auth`/`sessions` behave predictably (503 on sessions).
- Port mapping is a Sprint 1 placeholder (single fixed host port); dynamic port allocation and preview proxying are follow-ups.
+2
View File
@@ -27,6 +27,8 @@ Each ADR is a single file following the [Nygard format](https://cognitect.com/bl
| [0011](0011-fastify-react-stack.md) | Fastify is the API server; React + Vite is the web shell | Accepted |
| [0012](0012-in-memory-user-store.md) | In-memory user store behind a UserStore interface; Postgres deferred | Accepted |
| [0013](0013-docker-boundary-mock.md) | Sandbox orchestration mocks the Docker boundary; real daemon client deferred | Accepted |
| [0014](0014-postgres-queryable.md) | Postgres user store via a Queryable boundary; schema in SQL migrations | Accepted |
| [0015](0015-dockerode-engine-gating.md) | Docker daemon client via dockerode behind an injected engine; sandbox gated by env | Accepted |
## How to add one