Task 1.1 — Project scaffold

Phase 1 task 1.1 lands. Directus 11.17.4 boots locally end-to-end
against a TimescaleDB+PostGIS container; admin UI serves at :8055,
admin bootstrap from env vars works, named volumes preserve data
across down/up cycles.

Scaffold:
- Dockerfile — FROM directus/directus:11.17.4. Pre-installs
  postgresql16-client (ahead of task 1.2's db-init runner needing psql).
  Bakes in /directus/snapshots, /directus/db-init, /directus/scripts,
  /directus/extensions, /directus/entrypoint.sh.
- compose.dev.yaml — db (timescale/timescaledb-ha:pg16.6-ts2.17.2-all)
  + directus (local build), healthchecks, named volumes
  directus-pg-data + directus-uploads.
- entrypoint.sh — placeholder using upstream's actual flow
  (node cli.js bootstrap && pm2-runtime start ecosystem.config.cjs);
  the real db-init -> schema apply -> start wrapper lands in task 1.7.
- package.json — scripts-only (dev, dev:down, dev:reset,
  schema:snapshot, schema:apply, db:init), no runtime deps.
- .env.example — sectioned, fully documented, KEY/SECRET marked
  required with generation hints.
- .gitignore, .dockerignore — match the processor service conventions.
- snapshots/, db-init/, scripts/, extensions/ — empty with .gitkeep,
  filled by later Phase 1 tasks (1.3, 1.6) and Phase 5.

Lessons locked in (against the empirical pnpm dev boot):
- timescale/timescaledb-ha:pg16-latest does NOT exist on Docker Hub.
  Pin a concrete version (we used pg16.6-ts2.17.2-all).
- This image's data directory is /home/postgres/pgdata/data, not
  /pgdata or /var/lib/postgresql/data. PGDATA env var and the volume
  mount must both target it.
- The -all variant bundles PostGIS binaries but the extension is not
  auto-created on the directus database; CREATE EXTENSION lands in
  Phase 2 alongside the geofences/SLZs/waypoints collections.
- The upstream image's CMD is bootstrap + pm2-runtime, not a simple
  cli.js start. Bypassing pm2 would lose crash recovery.

These corrections folded into 01-project-scaffold.md (deliverable line
+ Done section), 08-gitea-ci-dryrun.md (CI service tag), and the
inline comments in compose.dev.yaml so future implementers don't
re-discover them.

Status: ROADMAP marks 1.1 done, Phase 1 in progress, 1.2 next.
This commit is contained in:
2026-05-01 21:29:00 +02:00
parent a8e808e71c
commit 387c3c4cfa
16 changed files with 389 additions and 8 deletions
+120
View File
@@ -0,0 +1,120 @@
# Local development compose — builds the Directus image from this repo's source
# tree and runs it alongside a TimescaleDB container.
#
# Use this file to verify Dockerfile changes, db-init migrations, and snapshot
# apply behaviour locally before pushing. For day-to-day admin UI work you can
# run `docker compose -f compose.dev.yaml up --build` and hit localhost:8055.
#
# For STAGE and PRODUCTION deployment, use the multi-service compose in the
# sibling `deploy/` repo (https://git.dev.microservices.al/trm/deploy), which
# references this service by its registry image tag instead of building locally.
#
# Usage:
# docker compose -f compose.dev.yaml up --build
# docker compose -f compose.dev.yaml down # preserves volumes
# docker compose -f compose.dev.yaml down -v # wipes volumes (clean slate)
#
# See also: pnpm dev | pnpm dev:down | pnpm dev:reset
name: directus-dev
services:
# ---------------------------------------------------------------------------
# db — PostgreSQL 16 + TimescaleDB + PostGIS (via timescaledb-ha)
#
# The "-all" suffix variant bundles all extensions (TimescaleDB, PostGIS,
# postgis_raster, etc.). Note: the binaries are present in the image, but
# extensions still need `CREATE EXTENSION` on each database that uses them
# — that's what db-init/*.sql does (Phase 1 task 1.3 + Phase 2).
#
# Pin a specific TS+PG version (not :pg16-latest, which doesn't exist on
# Docker Hub). Bump the pin explicitly when upgrading.
#
# PGDATA: this image initialises Postgres at /home/postgres/pgdata/data,
# not the upstream-standard /var/lib/postgresql/data and not /pgdata.
# The volume mount must match exactly or initdb fails with permission errors.
# ---------------------------------------------------------------------------
db:
image: timescale/timescaledb-ha:pg16.6-ts2.17.2-all
environment:
POSTGRES_USER: ${DB_USER:-directus}
POSTGRES_PASSWORD: ${DB_PASSWORD:-directus}
POSTGRES_DB: ${DB_DATABASE:-directus}
PGDATA: /home/postgres/pgdata/data
volumes:
- directus-pg-data:/home/postgres/pgdata/data
expose:
- '5432'
restart: unless-stopped
healthcheck:
# pg_isready reads PGUSER / PGDATABASE from env; compose sets those from
# the environment block above, so no hard-coding is needed here.
test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB']
interval: 10s
timeout: 5s
start_period: 30s
retries: 5
# ---------------------------------------------------------------------------
# directus — built from local Dockerfile
#
# All env vars are read from .env (copy .env.example → .env to get started).
# The service waits for db to pass its healthcheck before starting.
# ---------------------------------------------------------------------------
directus:
build:
context: .
dockerfile: Dockerfile
depends_on:
db:
condition: service_healthy
ports:
- '8055:8055'
environment:
# Database connection
DB_CLIENT: ${DB_CLIENT:-pg}
DB_HOST: db
DB_PORT: ${DB_PORT:-5432}
DB_DATABASE: ${DB_DATABASE:-directus}
DB_USER: ${DB_USER:-directus}
DB_PASSWORD: ${DB_PASSWORD:-directus}
# Instance security — REQUIRED; set non-placeholder values in .env
KEY: ${KEY}
SECRET: ${SECRET}
# Admin bootstrap — only applied on first init when the users table is empty
ADMIN_EMAIL: ${ADMIN_EMAIL}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
# Public URL used in emails and OAuth redirects
PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8055}
# Logging
LOG_LEVEL: ${LOG_LEVEL:-info}
LOG_STYLE: ${LOG_STYLE:-pretty}
# Optional: cache, CORS, WebSockets (inherit from .env if set)
CACHE_ENABLED: ${CACHE_ENABLED:-false}
CORS_ENABLED: ${CORS_ENABLED:-false}
CORS_ORIGIN: ${CORS_ORIGIN:-false}
WEBSOCKETS_ENABLED: ${WEBSOCKETS_ENABLED:-true}
volumes:
# Persist Directus file uploads across container restarts.
# No uploads use-case in Phase 1, but the volume is cheap to declare now.
- directus-uploads:/directus/uploads
restart: unless-stopped
healthcheck:
test: ['CMD-SHELL', 'wget -qO- http://localhost:8055/server/health || exit 1']
interval: 30s
timeout: 10s
start_period: 60s
retries: 3
volumes:
# Named volume so `down` preserves data and `down -v` wipes it.
directus-pg-data:
directus-uploads: