146 lines
6.8 KiB
Markdown
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.
|