Commit Graph

732 Commits

Author SHA1 Message Date
jubnl 2944cd32a1 docs: tick permissions and auditLog in migrate.md and refresh the migration graph 2026-07-27 22:09:08 +02:00
jubnl 6ff936c613 chore: move auditLog backend to proper DI 2026-07-27 22:09:08 +02:00
jubnl 1d56de3897 chore: move permissions backend to proper DI 2026-07-27 22:08:37 +02:00
jubnl 55cb2148d4 fix(server): fix the quirks preserved by the day DI migration 2026-07-27 20:58:03 +02:00
jubnl 2d1eec76c3 chore: move day backend to proper DI 2026-07-27 20:57:01 +02:00
jubnl e62628bf93 feat(server): adopt zod DTOs for the day and accommodation body contracts 2026-07-27 20:56:45 +02:00
jubnl d952eaf4d4 fix(server): fix the quirks preserved by the reservations DI migration
Two verified defects carried through the parity fold:

- The create-path accommodation metadata sync gated on the raw
  accommodation_id instead of the resolved one, so a hotel whose
  accommodation was just auto-created never received its metadata
  check-in/out times or confirmation. Now keyed off resolvedAccommodationId
  (RESV-SVC-006 re-pins the fixed behavior).
- The multi-statement writes ran outside transactions: create (accommodation
  insert + reservation insert + endpoint save + metadata sync), update
  (accommodation upsert + update + endpoint replace + sync), remove (the
  3-delete cascade), setReservationTravelers (delete + inserts) and
  resyncReservationDays now run in db.transaction() — a mid-write failure no
  longer leaves partial state (RESV-FIX-001/002 pin the rollbacks).

Left as-is on purpose: the truthy updatePositions dayId check, the
empty-string COALESCE keeps on title/status/type, the TEXT accommodation_id
normalization, the optional day_plan_position wire tolerance and the
swallowed notifyBookingChange catches — all contract or intentional
behavior, not defects.
2026-07-27 19:52:00 +02:00
jubnl 203d67c45f chore: move reservations backend to proper DI
Folds the 626-line services/reservationService.ts into the DI-native
ReservationsService — byte-identical SQL, statuses, bodies, error strings
and broadcasts; the legacy file is deleted.

- reservations.mcp.ts: the 5 legacy tools (create/update/delete_reservation,
  reorder_reservations, link_hotel_accommodation) + the trip-reservations
  resource move to the decorator registry (identical names, schemas,
  annotations, error strings, broadcasts); the canWrite/canRead registration
  gates become declarative access markers. Legacy registrar and resource
  entries removed.
- reservations.bridge.ts: 9 legacy-named exports + 3 type re-exports for the
  outside-container consumers (tripService, airtrail import/sync, the
  still-legacy transit + transports registrars, transitItineraryService's
  type-only import).
- notifyBookingChange folds to the legacy (tripId, actorId) signature; the
  controller passes user.id and the plugin-host factory drops its last
  plain-function reservation import for a private method over the injected
  service (constructor unchanged).
- TripsService and BookingImportService inject ReservationsService instead of
  importing legacy functions.
- Tests: reservations.service.test.ts rewritten as a real-SQL suite
  (RESV-SVC-001..030, RES-TRAV-001..005 moved verbatim,
  RESV-BRIDGE-001..009 pinning the bridge); both e2e suites converted to the
  DI-native pattern; path mocks repointed to the bridge or constructor stubs;
  tools-reservations kept and extended with the moved resource cases.

Quirks preserved on purpose (parity first — see the follow-up fix commit):
the raw-accommodation_id metadata-sync gate, the un-transactioned
multi-statement writes, the truthy updatePositions dayId check, the
empty-string COALESCE keeps, and the TEXT accommodation_id normalization.
2026-07-27 19:47:01 +02:00
jubnl c27fc99220 feat(server): adopt zod DTOs for the reservation body contracts
ReservationsController's 4 grandfathered @Body() params now validate through
the global ZodValidationPipe (createZodDto over the existing @trek/shared
schemas); their allow-list entries are removed (the boot gate rejects stale
entries). The bespoke 400 bodies (Title is required / positions must be an
array / user_ids must be an array) become the pipe's field envelope, and
non-numeric traveler ids now 400 instead of being silently dropped.

reservationPositionsRequestSchema is loosened to the real wire contract:
day_plan_position is optional (the legacy route never validated position
items and an absent value binds NULL) — pinned by RESV-006.

AccommodationsController stays grandfathered until the dayService migration.
2026-07-27 19:45:41 +02:00
jubnl fbc773386f fix(server): fix the quirks preserved by the vacay DI migration 2026-07-27 18:39:53 +02:00
jubnl 588457710c chore: move vacay backend to proper DI 2026-07-27 18:39:52 +02:00
jubnl 29e89a5774 feat(server): adopt zod DTOs for the vacay body contracts 2026-07-27 18:39:47 +02:00
jubnl 07b8f1234c fix(server): fix the quirks preserved by the collab DI migration 2026-07-27 00:27:12 +02:00
jubnl 49197c5370 feat(server): adopt zod DTOs for the collab body contracts 2026-07-27 00:27:11 +02:00
jubnl c08c595b7e chore: move collab backend to proper DI 2026-07-27 00:27:11 +02:00
jubnl f69731b72b docs(server): chronicle the fileService DI migration 2026-07-26 23:19:32 +02:00
jubnl f3e5be78c9 chore: move files backend to proper DI
Folds the legacy services/fileService.ts into the DI-native FilesService
(injected DatabaseService, byte-identical SQL). Load-time constants move to
files.constants.ts; files.bridge.ts survives with a single export for the
request-time getAllowedExtensions read the module-scope multer configs need
outside DI. TripsService and PluginHostDepsFactory inject FilesService.

Also rides the nestjs-zod ratchet (FileUploadDto/FileUpdateDto/FileLinkDto
over the @trek/shared contracts, FilesController entries dropped from the
body-contract allow-list) and fixes the verified legacy quirks: file-link
insert failures now surface instead of being swallowed, updateFile coerces
an empty-string description to NULL like createFile, and formatFile's
misleading optional trip_id annotation is gone.
2026-07-26 23:19:26 +02:00
jubnl 4a7d9cd0be refactor(plugins): route injectable-class SQL through the injected DatabaseService
PluginRuntimeService's operatorEgressHosts/pruneErrorLog and PluginsService's
egressHostCount were module-level helpers on the raw db proxy; they fold into
their classes as methods over the injected service. (PluginHostDepsFactory's
equivalent conversion rode the settings commit — same files.) No injectable
class under src/nest imports the db global anymore; the remaining importers
are the documented module-level seams (bridges, platform shell,
plugin-host-state, signature-status, the bare plugin user-setting readers).
2026-07-26 22:24:24 +02:00
jubnl e0530680cb feat(server): adopt zod DTOs for the settings body contracts
SettingUpsertDto/SettingsBulkDto wrap the existing @trek/shared schemas; both
SettingsController entries drop off the body-contract allow-list (boot
ratchet). Per the todo precedent the dead bespoke 400 guards go: missing/empty
key and a non-record settings body now answer with the pipe envelope
({ error: 'key: …' } / { error: 'settings: …' }) instead of 'Key is required' /
'Settings object is required', and arrays for settings are rejected. The local
masked-value literal now imports MASKED_SETTING_VALUE from @trek/shared.
The settings e2e registers ZodValidationPipe and pins the DTO 400s plus the
bulk masked-sentinel skip.
2026-07-26 22:24:14 +02:00
jubnl cece6509b9 chore: move settings backend to proper DI
Folds the legacy services/settingsService SQL into the Nest SettingsService
(byte-identical statements; BEGIN/COMMIT blocks become db.transaction()).
AdminService and ShareService inject it; llm-config.resolver becomes the
injectable LlmConfigResolver (SettingsService + DatabaseService), injected by
LlmParseService and PluginHostDepsFactory. No MCP surface, no bridge.

Whole-file commit, so this also carries the post-migration quirk fixes that
live in the same files: null serializes as '' (a stored "null" leaked out of
getDecryptedUserSetting as the literal string), bulk upserts skip the masked
sentinel (mirroring the single-upsert no-op) and return the written count.
Unit suite moved to tests/unit/nest with SET-SVC-001..026 preserved,
027..030 pin the fixes; settings/admin e2e converted to DI-native.
2026-07-26 22:23:57 +02:00
jubnl c9a9df5310 fix(server): harden share links — transactional writes, TTL renewal on update, scoped shared queries 2026-07-26 21:25:47 +02:00
jubnl d572b1ec8d feat(server): adopt zod DTOs for the share-link body contract 2026-07-26 21:25:34 +02:00
jubnl 72c3edda1a chore: move share backend to proper DI 2026-07-26 21:24:57 +02:00
jubnl efd0e7fa98 fix(server): import MCP session revocation from sessionManager, not the mcp barrel 2026-07-26 20:34:21 +02:00
jubnl 41b51849b7 feat(server): adopt zod DTOs for the assignment body contracts 2026-07-26 20:34:06 +02:00
jubnl 164f5152be chore: move assignments backend to proper DI and harden its writes 2026-07-26 20:33:31 +02:00
jubnl 9650ad4597 feat(server): adopt zod DTOs for the trip-invite body contracts
New tripInviteLinkCreateRequestSchema in @trek/shared (single source of
truth), a createZodDto wrapper on the controller's raw @Body(), and the
TripInviteLinkController.create entry dropped from the body-contract
allow-list (the boot ratchet enforces it).

expires_in_days accepts number | digits-only string | null | absent — the
string branch is digits-only so garbage like "7abc" is a 400 instead of
silently parseInt-ing to 7; the empty string (blank form input, meaning no
expiry) stays accepted. The e2e registers ZodValidationPipe and pins both
the bounded-expiry and 400 paths.
2026-07-26 18:21:18 +02:00
jubnl cb0cd3300d chore: move trip-invite backend to proper DI
Fold the legacy tripInviteService SQL into the Nest TripInviteService over
the injected DatabaseService (byte-identical statements; the JS-side expiry
check and non-positive-means-no-expiry coercion preserved) and delete the
legacy module. No bridge, no MCP port and no plugin-host change needed —
nothing outside the container consumed this domain.

Also wraps the rotation's probe + write + re-select in db.transaction()
per the repo rule that multi-statement writes are never bare.

Unit suite moved to tests/unit/nest preserving TRIP-INVITE-001..006 and
adding 007 (expiry pinning); the e2e converted to the DI-native pattern
(real SQL against temp trips/trip_invite_tokens tables).
2026-07-26 18:21:04 +02:00
jubnl 3762d3306c feat(server): adopt zod DTOs for the day-note body contracts 2026-07-26 17:44:25 +02:00
jubnl 4e49e9c21e chore: move day-notes backend to proper DI 2026-07-26 17:44:12 +02:00
jubnl 7242812b27 fix(trips): scope the offline bundle's packing items to the viewer (#858) 2026-07-26 16:57:02 +02:00
jubnl 934848e114 fix(packing): correct legacy quirks kept during the DI migration 2026-07-26 16:56:18 +02:00
jubnl 5da2abf4f9 feat(server): adopt zod DTOs for the packing body contracts 2026-07-26 16:55:53 +02:00
jubnl f1960dc364 chore: move packing backend to proper DI 2026-07-26 16:55:20 +02:00
jubnl 67b3005d36 docs(plugins): record Option A as implemented, correct bridge/next-up drift 2026-07-26 15:29:00 +02:00
jubnl 811230fa5d refactor(plugins): wire PluginHostDepsFactory through DI, drop tags/categories bridges 2026-07-26 15:28:55 +02:00
jubnl 167db04d69 refactor(plugins): split create-rpc-host into an injectable factory + host-state module 2026-07-26 15:28:39 +02:00
jubnl 6c583c2f27 feat(server): adopt nestjs-zod with a fail-closed body-contract boot gate
- One global ZodValidationPipe (APP_PIPE, createZodValidationPipe) validates
  any @Body() typed with a createZodDto class; the custom exception factory
  keeps TREK's { error: 'field: message; ...' } envelope byte-identical.
- The 7 piped controllers (collections, places, vacay, todo, health,
  airtrail x2) now type bodies with colocated <domain>.dto.ts wrappers —
  the Zod schemas in @trek/shared stay the single source of truth.
- OpenAPI: the hand-rolled api-zod.ts enricher is replaced by nestjs-zod's
  cleanupOpenApiDoc; request bodies surface as $refs to named DTO schemas.
- validateBodyContracts() runs in buildApp() after init and refuses to boot
  when any POST/PUT/PATCH @Body() (including @Body('field') sub-reads) lacks
  a ZodDto metatype. The 175 pre-existing legacy handlers are grandfathered
  in body-contract-allow-list.ts; the list is ratchet-only — the gate also
  throws on stale entries, so migrating a domain forces their removal.
2026-07-26 14:55:13 +02:00
jubnl ad7d349c85 fix(todo): wrap category-assignee replacement in a transaction
updateCategoryAssignees ran its DELETE + INSERT OR IGNORE loop unwrapped, so
a mid-loop failure could leave a category half-cleared. Also widens the item
params to the nullable shapes the wire contract actually sends (prep for the
todo body validation).
2026-07-26 14:54:38 +02:00
jubnl 79946cc711 chore: move todo backend to proper DI 2026-07-26 14:09:54 +02:00
jubnl 2ce40724d7 chore: move category backend to proper DI 2026-07-26 12:36:30 +02:00
jubnl 8f3ee53841 feat(mcp): Create nest wrapper for MCP for proper DI (#1708) 2026-07-26 11:42:47 +02:00
jubnl 53e884ad55 refactor(nest): migrate tagService into TagsService DI (pilot)
Move the tag SQL from src/services/tagService.ts into TagsService over an
injected DatabaseService (identical statements, #10b981 default, COALESCE
semantics). Non-Nest consumers (MCP tools, plugin RPC host) go through the
new tags.bridge.ts, which exports the legacy function names 1:1 from a
module-level instance over the shared db Proxy — the settled pattern for
the remaining legacy-service migrations (recipe documented in
src/nest/README.md). Tests are DI-native: direct service construction in
unit (with bridge delegation cases for the coverage gate) and
DatabaseModule + real SQL in e2e.
2026-07-25 18:36:17 +02:00
jubnl dd2c2d6668 refactor(nest): inject DatabaseService in db-using controllers 2026-07-25 18:15:15 +02:00
jubnl fbe200424d refactor(nest): inject DatabaseService in db-using services 2026-07-25 18:15:04 +02:00
jubnl def6b84938 refactor(nest): inject DatabaseService in access-check services and controllers 2026-07-25 18:14:53 +02:00
jubnl 718022c7ba feat(db): provide better-sqlite3 connection via DI token and complete DatabaseService surface 2026-07-25 18:14:37 +02:00
jubnl e162da6dbe fix(server): resolve zh-TW default language, close lint gap on bracket env access 2026-07-25 16:19:15 +02:00
jubnl 0f3a6f80b7 chore(server): forbid direct process.env access outside app-config 2026-07-25 16:05:26 +02:00
jubnl 3b31f4256b refactor(server): move language/session parsing into app-config, config.ts becomes keys module 2026-07-25 16:01:42 +02:00