Files
wyndham-Condon/backend/README.md
2026-08-03 16:43:57 +08:00

146 lines
6.8 KiB
Markdown

# 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.