README: replace migration section with pre-launch recreate-the-schema policy
The project is pre-launch: there is no production database and all dev starts from a fresh volume recreated from init-scripts/init-script.sql. The obsolete 'Database migrations' section (ALTER statements and apply-before-deploy notes for existing deployments) is replaced with the no-ALTER policy — schema changes are edited directly into the CREATE statements, no migration-managed delta exists, and the diff on the next recreate IS the migration.
This commit is contained in:
@@ -105,71 +105,14 @@ The heavier static analyzers (`golangci-lint`, `staticcheck`, `gosec`) are **not
|
||||
|
||||
CI caches Go modules (`~/go/pkg/mod`) and npm dependencies (`~/.npm`, `node_modules`) via `actions/cache` — keyed on `go.sum` and `package-lock.json` respectively. Cache is served by Gitea's built-in cache server at `git.popertots.com`. First run downloads everything (~3m35s), subsequent runs restore from cache in seconds.
|
||||
|
||||
### Database migrations
|
||||
### Database schema policy (pre-launch — no ALTERs)
|
||||
|
||||
The schema lives in `init-scripts/init-script.sql` and is applied automatically on a **fresh** volume via `docker-entrypoint-initdb.d`. Existing deployments must apply the payment-system delta manually (the schema is not migration-managed):
|
||||
The schema is single-source in `init-scripts/init-script.sql`, applied automatically on a **fresh** volume via `docker-entrypoint-initdb.d`. This project is **pre-launch**: there is no production database, and all dev work starts from a fresh DB recreation. Therefore:
|
||||
|
||||
```sql
|
||||
-- Refund system columns (refunds table)
|
||||
ALTER TABLE refunds ADD COLUMN IF NOT EXISTS refund_attempts INT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE refunds ADD COLUMN IF NOT EXISTS origin VARCHAR(16) NOT NULL DEFAULT 'manual';
|
||||
ALTER TABLE refunds ADD COLUMN IF NOT EXISTS idempotency_key VARCHAR(64) UNIQUE;
|
||||
|
||||
-- refund_failed notification reason (admin_notification_reason enum)
|
||||
-- NOTE: ALTER TYPE ... ADD VALUE cannot run inside a transaction block; run on a connection with autocommit.
|
||||
ALTER TYPE admin_notification_reason ADD VALUE IF NOT EXISTS 'refund_failed';
|
||||
|
||||
-- Refunds may now reference non-booking payments (gift-card purchase refunds)
|
||||
ALTER TABLE refunds ALTER COLUMN booking_id DROP NOT NULL;
|
||||
|
||||
-- Terminal checkout in-flight guard + payment_type passthrough (terminal_checkouts table)
|
||||
-- NOTE: CREATE TABLE is a fresh addition, not a column change. Apply before deploying
|
||||
-- the terminal-payment changes or CreateTerminalPayment/GetCheckoutStatus fail at runtime.
|
||||
CREATE TABLE IF NOT EXISTS terminal_checkouts (
|
||||
checkout_id VARCHAR(64) PRIMARY KEY,
|
||||
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
|
||||
payment_type payment_type NOT NULL DEFAULT 'full',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||||
amount NUMERIC(10,2) NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_terminal_checkouts_booking ON terminal_checkouts(booking_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_terminal_checkouts_status ON terminal_checkouts(status);
|
||||
|
||||
-- Square webhook dedup (square_webhook_events table)
|
||||
-- NOTE: CREATE TABLE is a fresh addition, not a column change. Apply before deploying
|
||||
-- the webhook handler changes or the dedup INSERT fails at runtime.
|
||||
-- Required for the Square webhook dedup; without it webhooks fail closed 503.
|
||||
CREATE TABLE IF NOT EXISTS square_webhook_events (event_id TEXT PRIMARY KEY, received_at TIMESTAMPTZ NOT NULL DEFAULT NOW());
|
||||
```
|
||||
|
||||
`backend/handlers/payments/refunds.go` casts `'refund_failed'::admin_notification_reason` (the sweep job in `internal/jobs/cleanup.go` only registers the handler), so an un-migrated DB fails at runtime — apply these before deploying the payment changes.
|
||||
|
||||
#### Saved-card per-user uniqueness + Square customer provisioning (P14)
|
||||
|
||||
The `user_saved_cards.square_card_id` UNIQUE constraint is now scoped **per user** (`UNIQUE (user_id, square_card_id)`), so the same physical card saved by two users produces two independent rows instead of user B mutating user A's saved-card row. Existing deployments must swap the constraint (the auto-generated constraint name is `user_saved_cards_square_card_id_key`):
|
||||
|
||||
```sql
|
||||
ALTER TABLE user_saved_cards DROP CONSTRAINT user_saved_cards_square_card_id_key;
|
||||
ALTER TABLE user_saved_cards ADD CONSTRAINT user_saved_cards_user_id_square_card_id_key UNIQUE (user_id, square_card_id);
|
||||
```
|
||||
|
||||
`square_customer_id TEXT` (nullable) was also added to `user_saved_cards` — populated the first time a user saves a card (Square customer provisioning, P14) and reused thereafter:
|
||||
|
||||
```sql
|
||||
ALTER TABLE user_saved_cards ADD COLUMN IF NOT EXISTS square_customer_id TEXT;
|
||||
```
|
||||
|
||||
#### Saved-card Square references scrubbing (GDPR account anonymization)
|
||||
|
||||
Account anonymization (`anonymize_user`, `delete_guest_user`, `AnonymizeStaleGuestAccounts`) now NULLs `square_card_id` / `square_customer_id` on `user_saved_cards` so external Square references are removed on erasure (Feature Catalog §9.2). This requires `square_card_id` to be nullable — existing deployments must apply:
|
||||
|
||||
```sql
|
||||
ALTER TABLE user_saved_cards ALTER COLUMN square_card_id DROP NOT NULL;
|
||||
```
|
||||
|
||||
Fresh installs get the nullable column from `init-scripts/init-script.sql`; the schema is not migration-managed, so this one-liner is required for existing DBs before deploying the anonymization changes.
|
||||
- **No `ALTER TABLE` / `ALTER TYPE` / `ADD VALUE` statements anywhere** — not in `init-script.sql`, not in tests, not in code.
|
||||
- Any schema change is edited **directly into the `CREATE` statements** in `init-script.sql`.
|
||||
- There is **no migration-managed delta** and no "apply before deploying" step. If a local dev DB needs updating, drop and recreate it (`docker compose down -v && docker compose up --build -d`), or apply the change by hand locally — never commit ALTERs.
|
||||
- Do not document changes as migration snippets; the schema diff on the next recreate is the migration.
|
||||
|
||||
## Full Documentation
|
||||
|
||||
|
||||
Reference in New Issue
Block a user