diff --git a/AGENTS.md b/AGENTS.md index bfc5fcc..56a3c6b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,6 +76,10 @@ live in `backend/src/main/resources/db/migration/`. Naming: `V__descript To reset: `docker compose down -v && docker compose up -d`. +Flyway schema migrations live in `db/migration/`; dev-only seeds (test users, +sample orders) are in `db/dev-migration/` and run only without the `prod` profile. +Production admin is created from `ADMIN_EMAIL` / `ADMIN_PASSWORD` on first boot. + --- ## Project Structure diff --git a/README.md b/README.md index 81f8e5e..0a40cc7 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,8 @@ No CORS configuration needed in development — the browser never calls the back | Profile | Datasource | Use | |---------|-----------|-----| | default | H2 in-memory | Local IDE dev (`./gradlew :backend:bootRun`) | -| `docker` | PostgreSQL via `docker-compose.yml` | Docker Compose dev | -| `prod` | PostgreSQL (production config) | Deploy (`docker-compose.prod.yml`) | +| `docker` | PostgreSQL + dev Flyway seeds | Docker Compose dev / CI | +| `docker,prod` | PostgreSQL, schema only, admin bootstrap | Deploy (`docker-compose.prod.yml`) | --- @@ -91,6 +91,89 @@ Copy `.env.example` to `.env` and fill in: | `STRIPE_SECRET_KEY` | Stripe secret key | | `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | | `STRIPE_PRICE_ID` | Stripe price ID for single letter | +| `SWISH_NUMBER` | Swish number for payment instructions | +| `ADMIN_EMAIL` | Production admin login (e.g. `admin@bilhej.se`) | +| `ADMIN_PASSWORD` | Strong production admin password (not `test1234`) | + +**Dev-only accounts** (from `db/dev-migration`, not used in production): + +| Email | Password | Role | +|-------|----------|------| +| `test@bilhej.se` | `test1234` | user (e2e / local) | +| `admin@bilhalsning.se` | `test1234` | admin (local docker only) | + +--- + +## Database access + +PostgreSQL runs in Docker. Any GUI client works (IntelliJ IDEA, DBeaver, TablePlus, +pgAdmin) — same idea as a normal remote database. + +### Local dev (`docker compose up`) + +| Setting | Value | +|---------|--------| +| Host | `localhost` | +| Port | `5432` | +| Database | from `.env` → `POSTGRES_DB` (default `bilhej`) | +| User / password | `POSTGRES_USER` / `POSTGRES_PASSWORD` | + +**IntelliJ IDEA:** Database tool window → `+` → Data Source → PostgreSQL → fill in above → +Test Connection → OK. + +**CLI:** + +```bash +docker exec -it bilhej-postgres psql -U bilhej -d bilhej +``` + +### Production (server) + +Postgres is bound to **localhost only** on the server (`127.0.0.1:5433`) so it is not +exposed to the internet. Use an **SSH tunnel** from your laptop, then point IntelliJ (or +DBeaver) at `localhost`. + +1. On the server, recreate the stack once so the port mapping is active (after deploy). + +2. From your laptop: + +```bash +ssh -N -L 5433:127.0.0.1:5433 you@srvr.nu +``` + +3. In IntelliJ / DBeaver: + +| Setting | Value | +|---------|--------| +| Host | `localhost` | +| Port | `5433` | +| Database | prod `POSTGRES_DB` | +| User / password | prod secrets | + +**CLI on the server** (no GUI): + +```bash +docker exec -it bilhej-postgres-prod psql -U bilhej -d bilhej +``` + +### Manual prod cleanup (keep data, remove dev seeds) + +```sql +DELETE FROM orders +WHERE user_id = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; + +DELETE FROM users +WHERE email IN ('test@bilhalsning.se', 'test@bilhej.se', 'admin@bilhalsning.se'); +``` + +Then deploy with `ADMIN_EMAIL` / `ADMIN_PASSWORD` set — the app creates the production +admin on startup. No need to insert a password hash by hand. + +To generate a bcrypt hash manually (optional): + +```bash +./gradlew hashPassword -Ppassword='your-strong-password' +``` --- @@ -187,6 +270,15 @@ Before the first deploy, complete these steps on the production server (`srvr.nu | `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | | `STRIPE_PRICE_ID` | Stripe price ID for single letter | | `SWISH_NUMBER` | Swish phone number for payment instructions | + | `ADMIN_EMAIL` | Production admin email (e.g. `admin@bilhej.se`) | + | `ADMIN_PASSWORD` | Strong unique admin password (password manager) | + + Production does **not** seed `test@bilhej.se` or demo orders. On first start, the + backend creates one admin from `ADMIN_EMAIL` / `ADMIN_PASSWORD` if no admin exists. + + If prod already has dev seed users, clean them with SQL (see [Database access](#database-access)) + instead of wiping the volume. Then redeploy with the new secrets so bootstrap can create + `ADMIN_EMAIL`. 2. **Point DNS** @@ -225,7 +317,7 @@ Before the first deploy, complete these steps on the production server (`srvr.nu | Tag | Git tag `v0.1.0` is created and pushed | | Build | Production backend JAR and frontend bundle are built | | Images | Multi-stage Docker images are built locally on the server | -| Start | `docker compose -f docker-compose.prod.yml up -d` | +| Start | `docker compose -f docker-compose.prod.yml up -d` (`SPRING_PROFILES_ACTIVE=docker,prod`) | | Verify | Health checks confirm backend API and frontend are responding | ### Architecture on Server