From ad312cdd437f9a6c7850d55dd50d249ae5a73cb1 Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 10 Aug 2026 09:13:33 +0200 Subject: [PATCH] Give day notes a domain of their own days/ carried two fachlichkeiten with a full file set each: days.controller, days.service, days.mcp, days.rpc, days.dto next to the five day-notes ones. The note files move out unchanged; because the new directory sits at the same depth, not one import path inside them changed. The two modules deliberately do not reference each other in either direction: dayExists is raw SQL on the days table in the note service, and DaysService reads day_notes the same way, so a module edge would buy nothing and cost a cycle. DayNotesModule has to be registered in PluginsRuntimeModule as well as AppModule - DayNotesRpc owns four of the 113 wire methods and the registry boots with requireTotalCoverage, so leaving it out would stop every test app that assembles the plugin runtime. The days e2e container assembles both modules now: the note routes still nest under the days prefix, and a container that builds only DaysModule would 404 on them while production served them fine. --- server/src/mcp/resources.ts | 2 +- server/src/nest/app.module.ts | 3 +- .../day-notes.controller.ts | 0 .../nest/{days => day-notes}/day-notes.dto.ts | 0 .../nest/{days => day-notes}/day-notes.mcp.ts | 0 server/src/nest/day-notes/day-notes.module.ts | 28 +++++++++++++++++++ .../nest/{days => day-notes}/day-notes.rpc.ts | 0 .../{days => day-notes}/day-notes.service.ts | 0 server/src/nest/days/days.module.ts | 24 +++++++--------- .../nest/plugins/plugins-runtime.module.ts | 3 +- server/tests/e2e/days.e2e.test.ts | 6 +++- server/tests/helpers/mcp-test-controllers.ts | 4 +-- server/tests/helpers/plugin-host.ts | 4 +-- server/tests/helpers/rpc-host-deps.ts | 2 +- server/tests/unit/days/day-notes.rpc.test.ts | 8 +++--- .../tests/unit/nest/day-notes.service.test.ts | 2 +- .../tests/unit/nest/days.controller.test.ts | 6 ++-- .../tests/unit/plugins/rpc-fallbacks.test.ts | 2 +- server/vitest.config.ts | 1 + 19 files changed, 63 insertions(+), 32 deletions(-) rename server/src/nest/{days => day-notes}/day-notes.controller.ts (100%) rename server/src/nest/{days => day-notes}/day-notes.dto.ts (100%) rename server/src/nest/{days => day-notes}/day-notes.mcp.ts (100%) create mode 100644 server/src/nest/day-notes/day-notes.module.ts rename server/src/nest/{days => day-notes}/day-notes.rpc.ts (100%) rename server/src/nest/{days => day-notes}/day-notes.service.ts (100%) diff --git a/server/src/mcp/resources.ts b/server/src/mcp/resources.ts index da8454523..03c165a78 100644 --- a/server/src/mcp/resources.ts +++ b/server/src/mcp/resources.ts @@ -60,7 +60,7 @@ export function registerResources(server: McpServer, userId: number, scopes: str // src/nest/reservations/reservations.mcp.ts (@ResourceTemplate). // The day-notes resource moved to the DI-discovered - // src/nest/days/day-notes.mcp.ts (@ResourceTemplate). + // src/nest/day-notes/day-notes.mcp.ts (@ResourceTemplate). // The trip-accommodations resource moved to the DI-discovered // src/nest/days/days.mcp.ts (@ResourceTemplate). diff --git a/server/src/nest/app.module.ts b/server/src/nest/app.module.ts index a91ccada9..db12f8d87 100644 --- a/server/src/nest/app.module.ts +++ b/server/src/nest/app.module.ts @@ -21,6 +21,7 @@ import { PackingModule } from './packing/packing.module'; import { BudgetModule } from './budget/budget.module'; import { ReservationsModule } from './reservations/reservations.module'; import { DaysModule } from './days/days.module'; +import { DayNotesModule } from './day-notes/day-notes.module'; import { AssignmentsModule } from './assignments/assignments.module'; import { PlacesModule } from './places/places.module'; import { TripsModule } from './trips/trips.module'; @@ -60,7 +61,7 @@ import { IdempotencyInterceptor } from './common/idempotency.interceptor'; * migrated. */ @Module({ - imports: [AppConfigModule, DatabaseModule, RealtimeModule, McpModule.forRoot({ accessPolicy: trekMcpAccessPolicy, validateAccess: trekMcpValidateAccess }), HealthModule, WeatherModule, HelpModule, AirportsModule, ConfigModule, SystemNoticesModule, MapsModule, PlaceEnrichmentModule, CategoriesModule, TagsModule, NotificationsModule, AtlasModule, VacayModule, PackingModule, TodoModule, BudgetModule, ReservationsModule, DaysModule, AssignmentsModule, PlacesModule, TripsModule, CollabModule, FilesModule, PhotosModule, MemoriesModule, AirtrailModule, JourneyModule, CollectionsModule, ShareModule, TripInviteModule, TransitModule, FeedsModule, SettingsModule, BackupModule, AuthModule, OidcModule, OauthModule, AdminModule, AddonsModule, AuditModule, PermissionsModule, PluginsModule, BookingImportModule, LlmParseModule], + imports: [AppConfigModule, DatabaseModule, RealtimeModule, McpModule.forRoot({ accessPolicy: trekMcpAccessPolicy, validateAccess: trekMcpValidateAccess }), HealthModule, WeatherModule, HelpModule, AirportsModule, ConfigModule, SystemNoticesModule, MapsModule, PlaceEnrichmentModule, CategoriesModule, TagsModule, NotificationsModule, AtlasModule, VacayModule, PackingModule, TodoModule, BudgetModule, ReservationsModule, DaysModule, DayNotesModule, AssignmentsModule, PlacesModule, TripsModule, CollabModule, FilesModule, PhotosModule, MemoriesModule, AirtrailModule, JourneyModule, CollectionsModule, ShareModule, TripInviteModule, TransitModule, FeedsModule, SettingsModule, BackupModule, AuthModule, OidcModule, OauthModule, AdminModule, AddonsModule, AuditModule, PermissionsModule, PluginsModule, BookingImportModule, LlmParseModule], providers: [ // Global error-envelope normaliser (DI-registered so it also catches // framework-level exceptions like the not-found handler). diff --git a/server/src/nest/days/day-notes.controller.ts b/server/src/nest/day-notes/day-notes.controller.ts similarity index 100% rename from server/src/nest/days/day-notes.controller.ts rename to server/src/nest/day-notes/day-notes.controller.ts diff --git a/server/src/nest/days/day-notes.dto.ts b/server/src/nest/day-notes/day-notes.dto.ts similarity index 100% rename from server/src/nest/days/day-notes.dto.ts rename to server/src/nest/day-notes/day-notes.dto.ts diff --git a/server/src/nest/days/day-notes.mcp.ts b/server/src/nest/day-notes/day-notes.mcp.ts similarity index 100% rename from server/src/nest/days/day-notes.mcp.ts rename to server/src/nest/day-notes/day-notes.mcp.ts diff --git a/server/src/nest/day-notes/day-notes.module.ts b/server/src/nest/day-notes/day-notes.module.ts new file mode 100644 index 000000000..8a3f55446 --- /dev/null +++ b/server/src/nest/day-notes/day-notes.module.ts @@ -0,0 +1,28 @@ +import { Module } from '@nestjs/common'; +import { DayNotesController } from './day-notes.controller'; +import { DayNotesService } from './day-notes.service'; +import { DayNotesMcp } from './day-notes.mcp'; +import { DayNotesRpc } from './day-notes.rpc'; +import { PermissionsModule } from '../permissions/permissions.module'; +import { AuthModule } from '../auth/auth.module'; +import { RealtimeModule } from '../realtime/realtime.module'; +import { PluginGuardsModule } from '../plugins/host/plugin-guards.module'; + +/** + * Day notes. Its own domain rather than a second file set inside days/, which + * carried two fachlichkeiten with a full controller/service/mcp/rpc/dto each. + * + * Deliberately unconnected to DaysModule in both directions: `dayExists` is raw + * SQL on `days` here, and DaysService reads `day_notes` the same way. Wiring the + * modules to each other would buy nothing and cost a cycle. + * + * AuthModule is only for DayNotesMcp's demo-user gate, PluginGuardsModule only + * for DayNotesRpc. + */ +@Module({ + imports: [PermissionsModule, AuthModule, RealtimeModule, PluginGuardsModule], + controllers: [DayNotesController], + providers: [DayNotesService, DayNotesMcp, DayNotesRpc], + exports: [DayNotesService], +}) +export class DayNotesModule {} diff --git a/server/src/nest/days/day-notes.rpc.ts b/server/src/nest/day-notes/day-notes.rpc.ts similarity index 100% rename from server/src/nest/days/day-notes.rpc.ts rename to server/src/nest/day-notes/day-notes.rpc.ts diff --git a/server/src/nest/days/day-notes.service.ts b/server/src/nest/day-notes/day-notes.service.ts similarity index 100% rename from server/src/nest/days/day-notes.service.ts rename to server/src/nest/day-notes/day-notes.service.ts diff --git a/server/src/nest/days/days.module.ts b/server/src/nest/days/days.module.ts index 5bdb9b4a0..c3ed54524 100644 --- a/server/src/nest/days/days.module.ts +++ b/server/src/nest/days/days.module.ts @@ -2,32 +2,28 @@ import { Module } from '@nestjs/common'; import { DaysController } from './days.controller'; import { DaysService } from './days.service'; import { DaysMcp } from './days.mcp'; -import { DayNotesController } from './day-notes.controller'; -import { DayNotesService } from './day-notes.service'; -import { DayNotesRpc } from './day-notes.rpc'; import { DaysRpc } from './days.rpc'; import { AccommodationsRpc } from './accommodations.rpc'; import { PluginGuardsModule } from '../plugins/host/plugin-guards.module'; import { RealtimeModule } from '../realtime/realtime.module'; -import { DayNotesMcp } from './day-notes.mcp'; import { PermissionsModule } from '../permissions/permissions.module'; import { QueryHelpersModule } from '../query-helpers/query-helpers.module'; import { PlacesModule } from '../places/places.module'; import { AuthModule } from '../auth/auth.module'; /** - * Days + day-notes domain (S6 — Phase 2 trip sub-domain). The single prefix - * /api/trips/:tripId/days covers both the days mount and the nested - * /days/:dayId/notes mount. DaysMcp/DayNotesMcp carry the - * decorator-registered MCP tools + resources. DaysService and DayNotesService - * are exported for in-container consumers (DaysRpc, DayNotesRpc, - * AccommodationsService, TripsService, the assignments/reservations MCP - * controllers). + * Days (S6 — Phase 2 trip sub-domain), mounted at /api/trips/:tripId/days. + * DaysMcp carries the decorator-registered MCP tools + resources. DaysService is + * exported for in-container consumers (DaysRpc, AccommodationsService, + * TripsService, the assignments/reservations MCP controllers). + * + * Day notes used to live here too, with their own full file set; they are their + * own domain now (day-notes/). */ @Module({ imports: [PermissionsModule, QueryHelpersModule, PlacesModule, AuthModule, RealtimeModule, PluginGuardsModule], - controllers: [DaysController, DayNotesController], - providers: [DaysService, DaysMcp, DayNotesService, DayNotesMcp, DayNotesRpc, DaysRpc, AccommodationsRpc], - exports: [DaysService, DayNotesService], + controllers: [DaysController], + providers: [DaysService, DaysMcp, DaysRpc, AccommodationsRpc], + exports: [DaysService], }) export class DaysModule {} diff --git a/server/src/nest/plugins/plugins-runtime.module.ts b/server/src/nest/plugins/plugins-runtime.module.ts index 80324bb12..94c6c0da3 100644 --- a/server/src/nest/plugins/plugins-runtime.module.ts +++ b/server/src/nest/plugins/plugins-runtime.module.ts @@ -20,6 +20,7 @@ import { ReservationsModule } from '../reservations/reservations.module'; import { TodoModule } from '../todo/todo.module'; import { PackingModule } from '../packing/packing.module'; import { DaysModule } from '../days/days.module'; +import { DayNotesModule } from '../day-notes/day-notes.module'; import { AssignmentsModule } from '../assignments/assignments.module'; import { LlmParseModule } from '../llm-parse/llm-parse.module'; import { FilesModule } from '../files/files.module'; @@ -61,7 +62,7 @@ import { JourneyDomainModule } from '../journey/journey-domain.module'; PluginGuardsModule, PluginOAuthModule, WeatherModule, TagsModule, CategoriesModule, BudgetModule, ReservationsModule, - TodoModule, PackingModule, DaysModule, AssignmentsModule, LlmParseModule, + TodoModule, PackingModule, DaysModule, DayNotesModule, AssignmentsModule, LlmParseModule, FilesModule, CollabModule, VacayModule, TripsModule, PlacesModule, PermissionsModule, AuditModule, AddonsModule, CollectionsModule, AtlasModule, NotificationsModule, TripMembershipModule, JourneyDomainModule, diff --git a/server/tests/e2e/days.e2e.test.ts b/server/tests/e2e/days.e2e.test.ts index e625c5802..4c1bf5934 100644 --- a/server/tests/e2e/days.e2e.test.ts +++ b/server/tests/e2e/days.e2e.test.ts @@ -81,6 +81,10 @@ import { PermissionsService } from '../../src/nest/permissions/permissions.servi let checkPermission: MockInstance; import { DaysModule } from '../../src/nest/days/days.module'; +// The note routes nest under the days prefix but live in their own domain now; +// this container has to assemble both or /days/:dayId/notes 404s here while +// working in production. +import { DayNotesModule } from '../../src/nest/day-notes/day-notes.module'; import { TrekExceptionFilter } from '../../src/nest/common/trek-exception.filter'; import { ZodValidationPipe } from '../../src/nest/common/zod-validation.pipe'; @@ -89,7 +93,7 @@ describe('Days + day-notes e2e (real auth guard + temp SQLite, real day SQL)', ( let app: Awaited>; async function build() { - const moduleRef = await Test.createTestingModule({ imports: [DatabaseModule, RealtimeModule, DaysModule] }).compile(); + const moduleRef = await Test.createTestingModule({ imports: [DatabaseModule, RealtimeModule, DaysModule, DayNotesModule] }).compile(); const nest = moduleRef.createNestApplication(); nest.use(cookieParser()); nest.useGlobalPipes(new ZodValidationPipe()); diff --git a/server/tests/helpers/mcp-test-controllers.ts b/server/tests/helpers/mcp-test-controllers.ts index 9db4b6a53..4210d521a 100644 --- a/server/tests/helpers/mcp-test-controllers.ts +++ b/server/tests/helpers/mcp-test-controllers.ts @@ -16,8 +16,8 @@ import { CollabService } from '../../src/nest/collab/collab.service'; import { CollectionsMcp } from '../../src/nest/collections/collections.mcp'; import { CollectionsService } from '../../src/nest/collections/collections.service'; import { DatabaseService } from '../../src/nest/database/database.service'; -import { DayNotesMcp } from '../../src/nest/days/day-notes.mcp'; -import { DayNotesService } from '../../src/nest/days/day-notes.service'; +import { DayNotesMcp } from '../../src/nest/day-notes/day-notes.mcp'; +import { DayNotesService } from '../../src/nest/day-notes/day-notes.service'; import { DaysMcp } from '../../src/nest/days/days.mcp'; import { DaysService } from '../../src/nest/days/days.service'; import { MapsMcp } from '../../src/nest/maps/maps.mcp'; diff --git a/server/tests/helpers/plugin-host.ts b/server/tests/helpers/plugin-host.ts index 5548b123f..52f7e6d27 100644 --- a/server/tests/helpers/plugin-host.ts +++ b/server/tests/helpers/plugin-host.ts @@ -18,7 +18,7 @@ import { TagsService } from '../../src/nest/tags/tags.service'; import { CategoriesService } from '../../src/nest/categories/categories.service'; import { TodoService } from '../../src/nest/todo/todo.service'; import { PackingService } from '../../src/nest/packing/packing.service'; -import { DayNotesService } from '../../src/nest/days/day-notes.service'; +import { DayNotesService } from '../../src/nest/day-notes/day-notes.service'; import { DaysService } from '../../src/nest/days/days.service'; import { AssignmentsService } from '../../src/nest/assignments/assignments.service'; import { LlmConfigResolver } from '../../src/nest/llm-parse/llm-config.resolver'; @@ -44,7 +44,7 @@ import { WeatherRpc } from '../../src/nest/weather/weather.rpc'; import { WeatherService } from '../../src/nest/weather/weather.service'; import { ExchangeRatesRpc } from '../../src/nest/budget/exchange-rates.rpc'; import { TodoRpc } from '../../src/nest/todo/todo.rpc'; -import { DayNotesRpc } from '../../src/nest/days/day-notes.rpc'; +import { DayNotesRpc } from '../../src/nest/day-notes/day-notes.rpc'; import { PackingRpc } from '../../src/nest/packing/packing.rpc'; import { FilesRpc } from '../../src/nest/files/files.rpc'; import { PlacesRpc } from '../../src/nest/places/places.rpc'; diff --git a/server/tests/helpers/rpc-host-deps.ts b/server/tests/helpers/rpc-host-deps.ts index b18d5379e..6eb109451 100644 --- a/server/tests/helpers/rpc-host-deps.ts +++ b/server/tests/helpers/rpc-host-deps.ts @@ -5,7 +5,7 @@ import { CategoriesRpc } from '../../src/nest/categories/categories.rpc'; import { WeatherRpc } from '../../src/nest/weather/weather.rpc'; import { ExchangeRatesRpc } from '../../src/nest/budget/exchange-rates.rpc'; import { TodoRpc } from '../../src/nest/todo/todo.rpc'; -import { DayNotesRpc } from '../../src/nest/days/day-notes.rpc'; +import { DayNotesRpc } from '../../src/nest/day-notes/day-notes.rpc'; import { PackingRpc } from '../../src/nest/packing/packing.rpc'; import { FilesRpc } from '../../src/nest/files/files.rpc'; import { PlacesRpc } from '../../src/nest/places/places.rpc'; diff --git a/server/tests/unit/days/day-notes.rpc.test.ts b/server/tests/unit/days/day-notes.rpc.test.ts index 50d8bec24..2e1994f64 100644 --- a/server/tests/unit/days/day-notes.rpc.test.ts +++ b/server/tests/unit/days/day-notes.rpc.test.ts @@ -9,9 +9,9 @@ import { expectRegisteredProvider } from '../../helpers/module-providers'; import { PluginRpcHost } from '../../../src/nest/plugins/host/rpc-host'; import { createTestPluginRegistry } from '../../../src/nest/plugins/host/rpc-kit/testing'; import { PluginGuards } from '../../../src/nest/plugins/host/plugin-guards.service'; -import { DayNotesRpc } from '../../../src/nest/days/day-notes.rpc'; -import { DaysModule } from '../../../src/nest/days/days.module'; -import type { DayNotesService } from '../../../src/nest/days/day-notes.service'; +import { DayNotesRpc } from '../../../src/nest/day-notes/day-notes.rpc'; +import { DayNotesModule } from '../../../src/nest/day-notes/day-notes.module'; +import type { DayNotesService } from '../../../src/nest/day-notes/day-notes.service'; import type { RealtimeService } from '../../../src/nest/realtime/realtime.service'; import type { DatabaseService } from '../../../src/nest/database/database.service'; import type { PermissionsService } from '../../../src/nest/permissions/permissions.service'; @@ -115,6 +115,6 @@ describe('DayNotesRpc through the router', () => { }); it('DAYNOTES-RPC-009 the class is listed in its module providers', () => { - expectRegisteredProvider(DaysModule, DayNotesRpc); + expectRegisteredProvider(DayNotesModule, DayNotesRpc); }); }); diff --git a/server/tests/unit/nest/day-notes.service.test.ts b/server/tests/unit/nest/day-notes.service.test.ts index 334ede046..356f096fa 100644 --- a/server/tests/unit/nest/day-notes.service.test.ts +++ b/server/tests/unit/nest/day-notes.service.test.ts @@ -48,7 +48,7 @@ import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createDay, addTripMember } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; import { PermissionsService } from '../../../src/nest/permissions/permissions.service'; -import { DayNotesService } from '../../../src/nest/days/day-notes.service'; +import { DayNotesService } from '../../../src/nest/day-notes/day-notes.service'; import type { DayNote } from '../../../src/types'; import { RealtimeService } from '../../../src/nest/realtime/realtime.service'; diff --git a/server/tests/unit/nest/days.controller.test.ts b/server/tests/unit/nest/days.controller.test.ts index e9407157a..5b88a3c01 100644 --- a/server/tests/unit/nest/days.controller.test.ts +++ b/server/tests/unit/nest/days.controller.test.ts @@ -1,11 +1,11 @@ import { describe, it, expect, vi } from 'vitest'; import { HttpException } from '@nestjs/common'; import { DaysController } from '../../../src/nest/days/days.controller'; -import { DayNotesController } from '../../../src/nest/days/day-notes.controller'; -import { DayNoteCreateDto, DayNoteUpdateDto } from '../../../src/nest/days/day-notes.dto'; +import { DayNotesController } from '../../../src/nest/day-notes/day-notes.controller'; +import { DayNoteCreateDto, DayNoteUpdateDto } from '../../../src/nest/day-notes/day-notes.dto'; import { DayReorderError } from '../../../src/nest/days/days.service'; import type { DaysService } from '../../../src/nest/days/days.service'; -import type { DayNotesService } from '../../../src/nest/days/day-notes.service'; +import type { DayNotesService } from '../../../src/nest/day-notes/day-notes.service'; import type { User } from '../../../src/types'; const user = { id: 1, role: 'user', email: 'u@example.test' } as User; diff --git a/server/tests/unit/plugins/rpc-fallbacks.test.ts b/server/tests/unit/plugins/rpc-fallbacks.test.ts index e04165965..59954674d 100644 --- a/server/tests/unit/plugins/rpc-fallbacks.test.ts +++ b/server/tests/unit/plugins/rpc-fallbacks.test.ts @@ -13,7 +13,7 @@ import { PluginRpcHost } from '../../../src/nest/plugins/host/rpc-host'; import { createTestPluginRegistry } from '../../../src/nest/plugins/host/rpc-kit/testing'; import { PluginGuards } from '../../../src/nest/plugins/host/plugin-guards.service'; import { TagsRpc } from '../../../src/nest/tags/tags.rpc'; -import { DayNotesRpc } from '../../../src/nest/days/day-notes.rpc'; +import { DayNotesRpc } from '../../../src/nest/day-notes/day-notes.rpc'; import { PlacesRpc } from '../../../src/nest/places/places.rpc'; import { PackingRpc } from '../../../src/nest/packing/packing.rpc'; import { FilesRpc } from '../../../src/nest/files/files.rpc'; diff --git a/server/vitest.config.ts b/server/vitest.config.ts index cc4068213..133d5eef0 100644 --- a/server/vitest.config.ts +++ b/server/vitest.config.ts @@ -79,6 +79,7 @@ export default defineConfig({ 'src/nest/common/**/*.ts': { statements: 95, branches: 89, functions: 99, lines: 97 }, 'src/nest/config/**/*.ts': { statements: 99, branches: 100, functions: 99, lines: 99 }, 'src/nest/database/**/*.ts': { statements: 99, branches: 100, functions: 99, lines: 99 }, + 'src/nest/day-notes/**/*.ts': { statements: 92, branches: 82, functions: 98, lines: 97 }, 'src/nest/days/**/*.ts': { statements: 92, branches: 82, functions: 98, lines: 97 }, 'src/nest/feeds/**/*.ts': { statements: 91, branches: 83, functions: 83, lines: 91 }, 'src/nest/files/**/*.ts': { statements: 97, branches: 95, functions: 99, lines: 98 },