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-016dc36d15cc40a5completed after local preflight and read-only verification. - Existing
booking,finance,ingestionandpublicobjects 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_multipliercondon.open_entitlement_periodcondon.create_usage_recordcondon.create_usage_record_v2condon.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:
{
"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_HOSTDB_PORTDB_USERDB_PASSWORDDB_NAME=booking_test
Optional variables and safe placeholders are documented in .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
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:
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. Do not run it after business data has been imported. The previous legacy rollback remains 002_legacy_import_and_bookings.down.sql, and the original empty-schema rollback remains 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.