From cf73eec2e4c44d88b68aa8a34dd85c4bf4f1fd0e Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Thu, 29 Oct 2026 12:21:07 -0400 Subject: [PATCH] docs(readme): sync quickstart compose block to docker-compose.yml verbatim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'copy this wholesale' docker-compose.yml block in the README had drifted from the real make-of-record file: it hardcoded 5432/8765 (missing the DB_PORT/SERVER_PORT configurability from de8f71b2), lost the loopback-only db publishing (b03ed9a2), carried its own hand-tailored db-init service with inline SQL instead of the z_rls_role.sh script version shipped in 313268a7, and still contained the vestigial ./uploads mount on the db service (dropped in the previous commit). Replace the whole yaml block with a byte-for-byte copy of docker-compose.yml so the quickstart can no longer drift — the documented optional envs, loopback bind, db-init script path, and env_file wiring all now match what actually ships. Verified by diffing the fenced block against the file. --- README.md | 104 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 53d10de1..416fcf66 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ mkdir uploads ```yaml services: + # PostgreSQL Database db: image: postgres:15-alpine container_name: bookhoard_db @@ -43,17 +44,27 @@ services: POSTGRES_DB: bookhoard POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DBPASS} - PGPORT: 5432 + # PGPORT makes Postgres listen on DB_PORT (kept in sync with the host mapping + app's DATABASE_PORT) + PGPORT: ${DB_PORT:-5432} volumes: - postgres_data:/var/lib/postgresql/data - - ./uploads:/app/uploads + - ./database/schema:/docker-entrypoint-initdb.d + ports: + - "127.0.0.1:${DB_PORT:-5432}:${DB_PORT:-5432}" + - "[::1]:${DB_PORT:-5432}:${DB_PORT:-5432}" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 30s timeout: 5s retries: 3 start_period: 10s + env_file: + - .env + # One-shot bootstrap: creates the bookhoard_srv runtime role and + # transfers object ownership to it (RLS prerequisite). Idempotent — + # safe on every startup; the app waits for its success. Passwords + # accept inline values or .env, like the rest of this file. db-init: image: postgres:15-alpine container_name: bookhoard_db_init @@ -61,61 +72,65 @@ services: POSTGRES_USER: postgres POSTGRES_DB: bookhoard PGHOST: db - PGUSER: postgres - PGPASSWORD: ${DBPASS} + DBPASS: ${DBPASS} DBSRVPASS: ${DBSRVPASS} - entrypoint: ["bash", "-c"] - command: - - | - set -e - : "${DBSRVPASS:?DBSRVPASS required in .env}" - psql -v ON_ERROR_STOP=1 -v role_pass="$DBSRVPASS" <<'EOSQL' - DO $$$$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'bookhoard_srv') THEN - CREATE ROLE bookhoard_srv LOGIN; - END IF; - END $$$$; - ALTER ROLE bookhoard_srv LOGIN PASSWORD :'role_pass'; - DO $$$$ - DECLARE r RECORD; - BEGIN - FOR r IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP - EXECUTE format('ALTER TABLE public.%I OWNER TO bookhoard_srv', r.tablename); - END LOOP; - FOR r IN SELECT sequencename FROM pg_sequences WHERE schemaname = 'public' LOOP - EXECUTE format('ALTER SEQUENCE public.%I OWNER TO bookhoard_srv', r.sequencename); - END LOOP; - FOR r IN SELECT p.oid::regprocedure AS fn FROM pg_proc p - JOIN pg_namespace n ON p.pronamespace = n.oid - WHERE n.nspname = 'public' AND p.prokind = 'f' LOOP - EXECUTE format('ALTER FUNCTION %s OWNER TO bookhoard_srv', r.fn); - END LOOP; - END $$$$; - ALTER SCHEMA public OWNER TO bookhoard_srv; - ALTER DATABASE bookhoard OWNER TO bookhoard_srv; - EOSQL + volumes: + - ./database/schema/z_rls_role.sh:/z_rls_role.sh:ro + command: bash /z_rls_role.sh depends_on: db: condition: service_healthy restart: "no" + # Bookhoard Application + # In production this image is pulled from the Gitea container registry. + # Override IMAGE_TAG in .env to pin or rollback a specific version (defaults to "latest"). app: image: git.linuxhg.com/bookhoard/bookhoard:${IMAGE_TAG:-latest} container_name: bookhoard restart: unless-stopped environment: + # Database Configuration + # Runtime role = bookhoard_srv (non-superuser OWNER, subject to RLS + # via FORCE). POSTGRES_USER above is the bootstrap account ONLY — + # the app must NEVER connect as it (superusers bypass RLS). DATABASE_HOST: db - DATABASE_PORT: 5432 - # Runtime role: bookhoard_srv (created above, confirmed by db-init). - # NEVER connect the app as the bootstrap superuser — it is subject - # to row-level security (per-user library visibility) by design. + DATABASE_PORT: ${DB_PORT:-5432} DATABASE_USER: bookhoard_srv DATABASE_PASSWORD: ${DBSRVPASS} + DATABASE_NAME: bookhoard + + # Application Configuration JWT_SECRET: ${JWT_SECRET} - SERVER_PORT: 8765 + SERVER_PORT: ${SERVER_PORT:-8765} + # IMPORTANT: Device sync requires full URL with protocol + # Local: http://localhost:8765 + # Local network: http://192.168.1.X:8765 + # Domain: https://bookhoard.example.com + BASE_URL: ${BASE_URL:-http://localhost:8765} + # Mark session cookies Secure; set true behind a TLS-terminating reverse proxy (Caddy/nginx/traefik) + COOKIE_SECURE: ${COOKIE_SECURE:-false} + + # Rate Limiting Configuration + TEST_MODE: ${TEST_MODE:-false} + RATE_LIMIT_ENABLED: ${RATE_LIMIT_ENABLED:-true} + REQUESTS_PER_MINUTE: ${REQUESTS_PER_MINUTE:-10} + + # Conversion Service Configuration + BOOKHOARD_CONVERSION_CACHE_DIR: ${BOOKHOARD_CONVERSION_CACHE_DIR:-/app/cache/kepub} + BOOKHOARD_CONVERSION_TOOL: ${BOOKHOARD_CONVERSION_TOOL:-/usr/bin/kepubify} + BOOKHOARD_CONVERSION_CACHE_TTL: ${BOOKHOARD_CONVERSION_CACHE_TTL:-24h} + + # Library Maintenance + # Days an archived item (file missing from disk for 2+ scans) is kept, + # with its reading history, before library scans purge it for good. + # Set 0 to keep archived items until purged manually on the library admin page. + ARCHIVE_RETENTION_DAYS: ${ARCHIVE_RETENTION_DAYS:-90} + + # System timezone (fallback for server-side time operations) + TZ: ${TZ:-UTC} ports: - - "8765:8765" + - "${SERVER_PORT:-8765}:${SERVER_PORT:-8765}" depends_on: db: condition: service_healthy @@ -124,7 +139,14 @@ services: volumes: - ./uploads:/app/uploads - bookhoard_conversion_cache:/app/cache/kepub + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:${SERVER_PORT:-8765}/health || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s +# Named Volumes volumes: postgres_data: bookhoard_conversion_cache: