From 734cbc2a947d02d9a3ef1ddbad4ce862dca531e1 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 10 Oct 2026 12:46:49 +0200 Subject: [PATCH] refactor(vacay): the invite and share tools read the caller's name from UsersRepository VacayMcp injected AuthService only to call getCurrentUser for the caller's username and email before sending an invite or sharing the calendar, which kept VacayModule importing AuthModule. UsersRepository.findUsernameEmail answers the same two fields (and undefined for an unknown id, which the tools already refuse as 'User not found.'). --- .../scripts/import-boundaries-baseline.json | 5 +-- server/src/nest/vacay/vacay.mcp.ts | 11 +++--- server/src/nest/vacay/vacay.module.ts | 5 +-- server/tests/helpers/mcp-test-controllers.ts | 36 +------------------ 4 files changed, 12 insertions(+), 45 deletions(-) diff --git a/server/scripts/import-boundaries-baseline.json b/server/scripts/import-boundaries-baseline.json index 94d14b60c..231bd4cd6 100644 --- a/server/scripts/import-boundaries-baseline.json +++ b/server/scripts/import-boundaries-baseline.json @@ -16,16 +16,13 @@ "places -> accommodations", "places -> assignments", "plugins -> trips", - "plugins -> vacay", "reservations -> integrations", "trip-members -> auth", "trip-members -> trips", "trip-read-model -> trip-members", "trip-read-model -> trips", "trips -> trip-members", - "trips -> trip-read-model", - "trips -> vacay", - "vacay -> auth" + "trips -> trip-read-model" ], "sharedImportsDomain": [ "nest/common/idempotency-cleanup.job.ts -> nest/audit/audit-log.logger.ts", diff --git a/server/src/nest/vacay/vacay.mcp.ts b/server/src/nest/vacay/vacay.mcp.ts index c5a280b47..e11ac6e7e 100644 --- a/server/src/nest/vacay/vacay.mcp.ts +++ b/server/src/nest/vacay/vacay.mcp.ts @@ -12,7 +12,9 @@ import { vacayUpdatePlanRequestSchema, } from '@trek/shared'; import type { VacayUpdatePlanRequest } from '@trek/shared'; -import { AuthService } from '../auth/auth.service'; +import { InjectRepository } from '@mikro-orm/nestjs'; +import { Users } from '../../db/entities/Users.entity'; +import type { UsersRepository } from '../../db/repositories/Users.repository'; import { ADDON_IDS } from '../../addons'; import { VacayService } from './vacay.service'; import { addonGate } from '../addons/addon-gate'; @@ -79,7 +81,8 @@ function calendarRegionCodes(country: string, data: unknown): { region: string; export class VacayMcp { constructor( private readonly vacay: VacayService, - private readonly auth: AuthService, + // The invite and share tools need the caller's username and email. + @InjectRepository(Users) private readonly users: UsersRepository, readonly addons: AddonsService, ) {} @@ -180,7 +183,7 @@ export class VacayMcp { }) async sendVacayInvite({ targetUserId }: { targetUserId: number }, ctx: McpContext) { const planId = await this.vacay.getActivePlanId(ctx.userId); - const me = await this.auth.getCurrentUser(ctx.userId); + const me = await this.users.findUsernameEmail(ctx.userId); if (!me) return errorResult('User not found.'); const result = await this.vacay.sendInvite(planId, ctx.userId, me.username, me.email, targetUserId); if (result.error) return errorResult(result.error); @@ -559,7 +562,7 @@ export class VacayMcp { access: { group: 'vacay', mode: 'write' }, }) async shareVacayCalendar({ targetUserId }: { targetUserId: number }, ctx: McpContext) { - const me = await this.auth.getCurrentUser(ctx.userId); + const me = await this.users.findUsernameEmail(ctx.userId); if (!me) return errorResult('User not found.'); const result = await this.vacay.shareCalendar(ctx.userId, me.email, targetUserId); if (result.error) return errorResult(result.error); diff --git a/server/src/nest/vacay/vacay.module.ts b/server/src/nest/vacay/vacay.module.ts index 2fbec8991..3237cbc37 100644 --- a/server/src/nest/vacay/vacay.module.ts +++ b/server/src/nest/vacay/vacay.module.ts @@ -5,7 +5,7 @@ import { VacayService } from './vacay.service'; import { VacayRpc } from './vacay.rpc'; import { PluginGuardsModule } from '../../nest-rpc/plugin-guards.module'; import { VacayMcp } from './vacay.mcp'; -import { AuthModule } from '../auth/auth.module'; +import { Users } from '../../db/entities/Users.entity'; import { AddonsModule } from '../addons/addons.module'; import { NotificationsModule } from '../notifications/notifications.module'; import { VacayPlans } from '../../db/entities/VacayPlans.entity'; @@ -37,13 +37,14 @@ import { SchoolHolidayRegions } from '../../db/entities/SchoolHolidayRegions.ent @Module({ imports: [ NotificationsModule, - AuthModule, PluginGuardsModule, AddonsModule, MikroOrmModule.forFeature([ VacayPlans, VacayPlanMembers, VacayYears, VacayUserYears, VacayUserColors, VacayEntries, VacayCompanyHolidays, VacayHolidayCalendars, VacayShares, VacayUserSettings, SchoolHolidayRegions, + // VacayMcp's invite and share tools read the caller's username/email. + Users, ]), ], controllers: [VacayController], diff --git a/server/tests/helpers/mcp-test-controllers.ts b/server/tests/helpers/mcp-test-controllers.ts index ce465bfcb..ff143c54b 100644 --- a/server/tests/helpers/mcp-test-controllers.ts +++ b/server/tests/helpers/mcp-test-controllers.ts @@ -7,7 +7,6 @@ import { AssignmentsMcp } from '../../src/nest/assignments/assignments.mcp'; import { AssignmentsService } from '../../src/nest/assignments/assignments.service'; import { AtlasMcp } from '../../src/nest/atlas/atlas.mcp'; import { AtlasService } from '../../src/nest/atlas/atlas.service'; -import { AuthService } from '../../src/nest/auth/auth.service'; import { BudgetMcp } from '../../src/nest/budget/budget.mcp'; import { BudgetService } from '../../src/nest/budget/budget.service'; import { ExchangeRatesService } from '../../src/nest/budget/exchange-rates.service'; @@ -73,9 +72,7 @@ import { TrekPhotoCacheMeta } from '../../src/db/entities/TrekPhotoCacheMeta.ent import { PhotoProviders } from '../../src/db/entities/PhotoProviders.entity'; import { UnsplashService } from '../../src/nest/unsplash/unsplash.service'; import { UserCleanupService } from '../../src/nest/auth/user-cleanup.service'; -import { WebauthnConfigService } from '../../src/nest/auth/webauthn-config.service'; import { TripMembershipService } from '../../src/nest/trip-membership/trip-membership.service'; -import { MailerService } from '../../src/nest/notifications/mailer/mailer.service'; import { CalendarService } from '../../src/nest/calendar/calendar.service'; import { FeedsMcp } from '../../src/nest/feeds/feeds.mcp'; import { FeedsService } from '../../src/nest/feeds/feeds.service'; @@ -182,18 +179,10 @@ import { createTestJourneyShareTokensRepo } from './journey-share-repos'; import { AppSettings } from '../../src/db/entities/AppSettings.entity'; import { AuditLog } from '../../src/db/entities/AuditLog.entity'; import { Users } from '../../src/db/entities/Users.entity'; -import { Settings } from '../../src/db/entities/Settings.entity'; -import { InviteTokens } from '../../src/db/entities/InviteTokens.entity'; -import { McpTokens } from '../../src/db/entities/McpTokens.entity'; -import { OauthTokens } from '../../src/db/entities/OauthTokens.entity'; -import { WebauthnCredentials } from '../../src/db/entities/WebauthnCredentials.entity'; -import { PasswordResetTokens } from '../../src/db/entities/PasswordResetTokens.entity'; import { noGoogleQuota } from './google-quota'; -import { createTestPushSubscriptionsRepo } from './notifications-repos'; import { createTestToursRepo } from './tours-repos'; import { createTestBudgetSettlementsRepo } from './budget-repos'; import { MaintenanceRepository } from '../../src/db/repositories/MaintenanceRepository'; -import { createTestSessionsService } from './sessions'; /** * Hand-wired counterpart of the boot-time discovery in McpRegistryService, @@ -214,36 +203,13 @@ export async function createMcpTestRegistry(): Promise { const appSettings = (await createTestOrm(db)).repo(AppSettings); const auditLogRepo = mcpOrm.repo(AuditLog); const usersRepo = mcpOrm.repo(Users); - const settingsRepo = mcpOrm.repo(Settings); - const inviteTokensRepo = mcpOrm.repo(InviteTokens); - const mcpTokensRepoForAuth = mcpOrm.repo(McpTokens); - const oauthTokensRepo = mcpOrm.repo(OauthTokens); - const webauthnCredentialsRepoForAuth = mcpOrm.repo(WebauthnCredentials); - const passwordResetTokensRepo = mcpOrm.repo(PasswordResetTokens); const permissionsService = new PermissionsService(await createTestAppSettingsRepo(db), await createTestUnitOfWork(db)); - // Same argument list as auth.bridge.ts. AtlasService used to sit in third - // place; when getTravelStats moved onto AtlasService itself the edge was - // dropped and four collaborators took its place, but this call site kept the - // old shape, so `membership` and `webauthn` held the wrong objects and - // userCleanup/mailer/tokens were undefined. const realtimeService = new RealtimeService(); // Plan 4 Task 1 constructor-ripple: the trip `user_id`/user `role` reads // moved off `DatabaseService` onto `TripsRepository`/`UsersRepository`. const guards = new McpToolGuardsService(mcpOrm.repo(Trips), usersRepo, permissionsService, realtimeService); const exchangeRatesService = new ExchangeRatesService(); const budgetService = new BudgetService(permissionsService, exchangeRatesService, realtimeService, await createTestUnitOfWork(db), ...(await budgetRepoArgs(db))); - const authService = new AuthService( - permissionsService, - new TripMembershipService(await createTestTripsRepo(db), await createTestTripMembersRepo(db)), - new WebauthnConfigService(appSettings), - new UserCleanupService(new MaintenanceRepository(mcpOrm.em), budgetService, await createTestUnitOfWork(db), usersRepo, await createTestTripMembersRepo(db), await createTestBudgetItemsRepo(db), await createTestBudgetSettlementsRepo(db), await createTestJourneyShareTokensRepo(db), await createTestJourneysRepo(db), await createTestJourneyEntriesRepo(db), await createTestJourneyContributorsRepo(db), await createTestShareTokensRepo(db), await createTestPluginsRepo(db), await createTestPluginUserErasureQueueRepo(db)), - new MailerService(usersRepo, settingsRepo, appSettings), - new EphemeralTokenService(), - new AllowedFileTypesService(appSettings), await createTestUnitOfWork(db), - appSettings, usersRepo, inviteTokensRepo, mcpTokensRepoForAuth, oauthTokensRepo, webauthnCredentialsRepoForAuth, passwordResetTokensRepo, - await createTestPushSubscriptionsRepo(db), - await createTestSessionsService(db), - ); const queryHelpersService = new QueryHelpersService(await createTestTagsRepo(db), await createTestPlaceRatingsRepo(db), await createTestAssignmentParticipantsRepo(db)); const daysService = new DaysService( permissionsService, @@ -453,7 +419,7 @@ export async function createMcpTestRegistry(): Promise { await createTestVacaySharesRepo(db), await createTestVacayUserSettingsRepo(db), await createTestSchoolHolidayRegionsRepo(db), realtimeService, notificationsStub(), await createTestUnitOfWork(db), - ), authService, addonsService), + ), usersRepo, addonsService), new SchoolHolidaysMcp( new SchoolHolidaysService( await createTestSchoolHolidayCountriesRepo(db),