Files
TREK/server/src/nest/plugins/plugins.module.ts
T
jubnl ae3ba18b9e chore: move atlas backend to proper DI
Fold the 1612-line services/atlasService.ts into the DI-native AtlasService:
the stats aggregation, visited countries/regions with the #1490 tombstone and
cascade logic, and the bucket-list CRUD become methods over the injected
DatabaseService. The ~750-line pure-geo half — the bundled admin0/admin1
boundary stores with their #1576 streaming builders, the point-in-polygon
indexes, Nominatim geocoding with its shared throttle, and every module-scoped
cache including the import-time unref'd cleanup interval — moves verbatim to
the plain module atlas-geo.ts so the caches stay process-global across the
container instance, the bridge instance and test helpers (assetPath
re-anchored one directory deeper, the only non-verbatim line).

The 10-tool mcp/tools/atlas.ts registrar plus all four atlas resources in
mcp/resources.ts move onto the decorator registry as atlas.mcp.ts — the when:
atlas-addon gate is parity here (legacy tools and resources both gated; the
REST controller deliberately does not). resources.test.ts retires with its
last two cases, which move to tools-atlas-expanded.test.ts. The plugin RPC
host swaps its 9 atlas imports for the injected AtlasService (23rd constructor
dep); a minimal 2-export atlas.bridge.ts serves the one legacy consumer,
authService.getTravelStats, and dies when authService migrates.

Adopts atlas.dto.ts over the existing shared schemas and clears all three
AtlasController entries from body-contract-allow-list.ts: the hand-rolled
'name and country_code are required' 400 becomes the ZodValidationPipe
envelope (todo/places trade) while the whitespace-only bucket name keeps its
legacy 'Name is required' trim guard.

Fixes the verified defects the relocation had faithfully carried, each with a
regression test (ATLAS-SVC-031..036 plus two MCP casing cases): the four
multi-statement mark/unmark writes now run in db.transaction() (the region
cascade nests as a savepoint), the trip-less countryPlaces early return
honours manually_marked, the '|| null' bindings that dropped lat/lng 0 and
empty-string notes on bucket update become '?? null', the mutating bucket SQL
is user-scoped, and the MCP region/country-places tools uppercase their codes
to match REST.

Quirks deliberately preserved: the detached GET-path place_regions backfill
writes, getStats' two divergent return shapes, the import-time cache-cleanup
interval, getCountryFromAddress's unvalidated bare 2-letter codes, and the
whitespace-only-name silent no-op on bucket update.

Tests move with their case IDs: atlasService.test.ts becomes
unit/nest/atlas.service.test.ts (pure-geo imports from atlas-geo, DB calls
through the constructed service, plus bridge-delegation cases), the atlas e2e
converts to the DI-native pattern (real SQL against the full temp-db schema),
and the plugin-host path mock becomes a constructor stub.
2026-08-01 15:50:36 +02:00

65 lines
4.4 KiB
TypeScript

import { Module } from '@nestjs/common';
import { PluginsController } from './plugins.controller';
import { PluginsFeedController } from './plugins-feed.controller';
import { PluginsProxyController } from './plugins-proxy.controller';
import { PluginFrameController } from './plugin-frame.controller';
import { PlaceDetailsController } from './place-details.controller';
import { TripWarningsController } from './trip-warnings.controller';
import { ViewContributionsController } from './view-contributions.controller';
import { TripCardContributionsController } from './trip-card-contributions.controller';
import { PluginPhotosController } from './plugin-photos.controller';
import { PluginCalendarController } from './plugin-calendar.controller';
import { MapMarkersController } from './map-markers.controller';
import { MapLayersController } from './map-layers.controller';
import { PluginRoutesController } from './plugin-routes.controller';
import { DayScheduleController } from './day-schedule.controller';
import { PluginActivityController } from './plugin-activity.controller';
import { PdfSectionsController } from './pdf-sections.controller';
import { AtlasLayersController } from './atlas-layers.controller';
import { JournalEntryRowsController } from './journal-entry-rows.controller';
import { PluginUserSettingsController } from './plugin-user-settings.controller';
import { PluginOAuthController } from './plugin-oauth.controller';
import { PluginOAuthService } from './plugin-oauth.service';
import { PluginsService } from './plugins.service';
import { PluginRuntimeService } from './plugin-runtime.service';
import { PluginRegistryService } from './registry/registry.service';
import { PluginHostDepsFactory } from './host/plugin-host-deps.factory';
import { TagsModule } from '../tags/tags.module';
import { CategoriesModule } from '../categories/categories.module';
import { BudgetModule } from '../budget/budget.module';
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 { AssignmentsModule } from '../assignments/assignments.module';
import { LlmParseModule } from '../llm-parse/llm-parse.module';
import { FilesModule } from '../files/files.module';
import { CollabModule } from '../collab/collab.module';
import { VacayModule } from '../vacay/vacay.module';
import { TripsModule } from '../trips/trips.module';
import { PlacesModule } from '../places/places.module';
import { PermissionsModule } from '../permissions/permissions.module';
import { AuditModule } from '../audit/audit.module';
import { AddonsModule } from '../addons/addons.module';
import { CollectionsModule } from '../collections/collections.module';
import { AtlasModule } from '../atlas/atlas.module';
/**
* Plugin system (#plugins). M0 read side + M2 isolated runtime + M3 frontend:
* the runtime service owns the process supervisor and boots active plugins on
* startup; the proxy forwards /api/plugins/:id/* to the child; the feed lists
* active plugins for the client; the frame controller serves sandboxed page/
* widget assets at /plugin-frame/:id/*.
*/
@Module({
// The DI-native domain services the plugin host wiring injects
// (PluginHostDepsFactory); DatabaseModule is @Global, so not listed.
imports: [TagsModule, CategoriesModule, BudgetModule, ReservationsModule, TodoModule, PackingModule, DaysModule, AssignmentsModule, LlmParseModule, FilesModule, CollabModule, VacayModule, TripsModule, PermissionsModule, AuditModule, AddonsModule, PlacesModule, CollectionsModule, AtlasModule],
controllers: [PluginsController, PluginsFeedController, PluginsProxyController, PluginFrameController, PlaceDetailsController, TripWarningsController, ViewContributionsController, TripCardContributionsController, PluginPhotosController, PluginCalendarController, MapMarkersController, MapLayersController, PluginRoutesController, DayScheduleController, PdfSectionsController, AtlasLayersController, JournalEntryRowsController, PluginUserSettingsController, PluginOAuthController, PluginActivityController],
providers: [PluginsService, PluginRuntimeService, PluginRegistryService, PluginOAuthService, PluginHostDepsFactory],
// Exported so the admin addon-toggle handler can cascade-disable plugins whose
// required addon was just turned off (#plugins dependencies).
exports: [PluginRuntimeService],
})
export class PluginsModule {}