# CONDO Backend TypeScript/Fastify API and PostgreSQL database layer for the current CONDO owner-account frontend. ## Current state - Database: existing `booking_test`. - Isolated schema: `condon`. - Migrations: `001_create_condon_schema`, `002_legacy_import_and_bookings`, `003_delete_usage_record`. - Reference data: 11 room types. - Imported latest `(2)` batch: 388 owners, 389 periods, 155 bookings, 157 usage records and 547 ledger rows. - Historical data import: `legacy-016dc36d15cc40a5` completed after local preflight and read-only verification. - Existing `booking`, `finance`, `ingestion` and `public` objects were not changed. - Operator access uses an expiring opaque server session and an HttpOnly cookie; credentials are verified only in the backend. ## Database objects | Object | Purpose | | --- | --- | | `condon.room_types` | Room codes, automatic tiers and AC2 manual-rule marker | | `condon.owner_accounts` | One independent account per owner room | | `condon.entitlement_periods` | Annual grant, carry-forward and the single authoritative balance | | `condon.bookings` | Unique Confirmation booking header; usage is 1:N | | `condon.usage_records` | Confirmation, stay dates, Night, multiplier, Use and post-use Balance | | `condon.entitlement_ledger` | Annual grant, carry-forward and usage balance movements | | `condon.schema_migrations` | Migrations belonging only to `condon` | The database functions are: - `condon.calculate_multiplier` - `condon.open_entitlement_period` - `condon.create_usage_record` - `condon.create_usage_record_v2` - `condon.delete_usage_record` `create_usage_record_v2` is the authoritative new-record write path. It calculates Night, Multiplier, Use and Balance, locks the entitlement period and writes the usage row, ledger row and new balance atomically. Historical rows use `legacy-source`, preserve source Use/Balance/Room/raw room type, and may have a null multiplier. ## API | Method | Path | Purpose | | --- | --- | --- | | POST | `/auth/login` | Verify operator credentials and create a 12-hour session | | GET | `/auth/session` | Check the current browser session without exposing the cookie | | POST | `/auth/logout` | Revoke the current session and expire its cookie | | GET | `/health` | Database/schema/migration health | | GET | `/room-types` | Room-type rule metadata | | GET | `/owner-accounts` | Searchable, paginated account list | | GET | `/owner-accounts/:id` | Account detail and period balance | | GET | `/usage-records` | Searchable, paginated usage history | | POST | `/usage-records` | Transactional usage deduction | | DELETE | `/usage-records/:id` | Transactional usage deletion and balance restoration | | GET | `/dashboard` | Annual totals and room-type/month aggregates | Runtime OpenAPI documentation is available at `/docs`. Only the three `/auth/*` routes and CORS preflight are public. Health, OpenAPI documentation and every business route require the `condon_session` cookie. Session tokens are cryptographically random, stored only as SHA-256 keys in backend memory, and expire after the configured TTL or logout. Example POST body: ```json { "ownerAccountId": "00000000-0000-4000-8000-000000000001", "confirmationNo": "26090001", "checkIn": "2026-09-01", "checkOut": "2026-09-04", "usedRoomType": "SU1", "manualMultiplier": null, "remark": "", "idempotencyKey": "00000000-0000-4000-8000-000000000002" } ``` The request does not accept Night, Use, Balance or an automatic multiplier. PostgreSQL derives them from the account, dates and room rules. ## Configuration Use runtime environment variables or a deployment secret manager. Do not commit live credentials. Required variables: - `DB_HOST` - `DB_PORT` - `DB_USER` - `DB_PASSWORD` - `DB_NAME=booking_test` Optional variables and safe placeholders are documented in [.env.example](./.env.example). The application refuses to start if `DB_NAME` is anything other than `booking_test`. Authentication defaults for this deployment are `wyndhamcondon / wyndhamcondon`. Override `AUTH_USERNAME`, `AUTH_PASSWORD`, `AUTH_SESSION_TTL_HOURS` and `AUTH_COOKIE_SECURE` through runtime secrets for another environment. Secure cookies default on automatically when `NODE_ENV=production`. ## Local commands ```bash npm install npm run typecheck npm test npm run build npm run preview:history:check npm run preview:history npm run dev ``` The API listens on `127.0.0.1:3000` by default. The default CORS allowlist accepts the existing frontend at `127.0.0.1:4173` and `localhost:4173`. `preview:history:check` validates the retained import source hash and the fixed 388 owner / 157 usage / 438 used / 5,394 remaining totals without opening a port. `preview:history` serves that exact snapshot through the normal Fastify authentication and API routes without connecting to PostgreSQL. It is intended for safe local preview when remote database secrets are not present; any new preview records and balance changes are memory-only and reset when the process stops. Database operator commands: ```bash npm run db:check-migrations npm run db:inventory npm run db:verify npm run db:test-integration npm run db:verify-import ``` `db:test-integration` is an empty-database migration test; it must not be run against the imported production data. The database commands read connection material from one non-echoed standard-input line. They do not read `.env` files or print credentials. ## Verification - Migration safety checks: 16/16. - API/auth/config/error tests: 12/12. - Live imported-database API smoke checks: 6/6. - Live transactional API write checks: 8/8. - Imported-batch read-only verification: 6/6. - Post-test deployment checks: 11/11. - npm vulnerability audit: 0. The database integration test covers all 100 automatic room-type combinations, both AC2 manual paths, annual grant/carry-forward, derived Night/Use/Balance, idempotency, duplicate confirmation, cross-year rejection, insufficient balance and concurrent overspend protection. Synthetic records are removed and the final four business tables must be empty. ## Rollback The latest migration rollback is [003_delete_usage_record.down.sql](./migrations/003_delete_usage_record.down.sql). Do not run it after business data has been imported. The previous legacy rollback remains [002_legacy_import_and_bookings.down.sql](./migrations/002_legacy_import_and_bookings.down.sql), and the original empty-schema rollback remains [001_create_condon_schema.down.sql](./migrations/001_create_condon_schema.down.sql). Do not run rollback after business data has been imported. Rollback is an explicit operator action and is not exposed as a normal npm command. ## Deliberately deferred - Importing a newer workbook without a new preflight batch. - Switching the static frontend to the API. - Multi-user identities, roles and backend-restart-persistent sessions. - Manual balance adjustment. - Cross-entitlement-year stays.