Files
corrosion-admin-panel/backend-nest/src/app.module.ts
Vantz Stockwell 47fa72763c feat(api): host-agent protocol v2 consumer — heartbeat persistence, auto-register, staleness sweep
Nothing persisted agent heartbeats before: companion_last_seen was
written once at setup and connection_status stayed 'connected' forever.
HostAgentConsumerService now consumes corrosion.*.host.heartbeat
(updates last_seen + status, auto-creates the bare_metal connection row
on first contact), host.going_offline (graceful offline), and sweeps
connections offline after 180s of heartbeat silence. License-existence
tenant validation with caching per NATS-consumer doctrine. WS bridge
forwards host_heartbeat/host_going_offline to the panel.

Contract-verified against production NATS with the backend's own nats
lib: v2 subjects, schema 2, real telemetry, offline beacon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 10:35:58 -04:00

152 lines
5.3 KiB
TypeScript

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { JwtModule } from '@nestjs/jwt';
import { APP_GUARD } from '@nestjs/core';
import { ScheduleModule } from '@nestjs/schedule';
import configuration from './config/configuration';
// Guards
import { JwtAuthGuard } from './common/guards/jwt-auth.guard';
import { PermissionsGuard } from './common/guards/permissions.guard';
import { LicenseGuard } from './common/guards/license.guard';
// Feature Modules
import { AuthModule } from './modules/auth/auth.module';
import { UsersModule } from './modules/users/users.module';
import { LicensesModule } from './modules/licenses/licenses.module';
import { ServersModule } from './modules/servers/servers.module';
import { PlayersModule } from './modules/players/players.module';
import { WipesModule } from './modules/wipes/wipes.module';
import { MapsModule } from './modules/maps/maps.module';
import { PluginsModule } from './modules/plugins/plugins.module';
import { ChatModule } from './modules/chat/chat.module';
import { TeamModule } from './modules/team/team.module';
import { NotificationsModule } from './modules/notifications/notifications.module';
import { SettingsModule } from './modules/settings/settings.module';
import { SchedulesModule } from './modules/schedules/schedules.module';
import { AnalyticsModule } from './modules/analytics/analytics.module';
import { AlertsModule } from './modules/alerts/alerts.module';
import { StatusModule } from './modules/status/status.module';
import { StoreModule } from './modules/store/store.module';
import { WebstoreModule } from './modules/webstore/webstore.module';
import { AdminModule } from './modules/admin/admin.module';
import { SetupModule } from './modules/setup/setup.module';
import { MigrationModule } from './modules/migration/migration.module';
import { ChangelogModule } from './modules/changelog/changelog.module';
import { FilesModule } from './modules/files/files.module';
import { LootModule } from './modules/loot/loot.module';
import { TeleportModule } from './modules/teleport/teleport.module';
import { GatherModule } from './modules/gather/gather.module';
import { AutoDoorsModule } from './modules/autodoors/autodoors.module';
import { KitsModule } from './modules/kits/kits.module';
import { FurnaceSplitterModule } from './modules/furnacesplitter/furnacesplitter.module';
import { BetterChatModule } from './modules/betterchat/betterchat.module';
import { TimedExecuteModule } from './modules/timedexecute/timedexecute.module';
import { RaidableBasesModule } from './modules/raidablebases/raidablebases.module';
import { EarlyAccessModule } from './modules/early-access/early-access.module';
// Shared Services
import { NatsService } from './services/nats.service';
import { NatsBridgeService } from './services/nats-bridge.service';
import { HostAgentConsumerService } from './services/host-agent-consumer.service';
import { ServerConnection } from './entities/server-connection.entity';
import { License } from './entities/license.entity';
import { SteamService } from './services/steam.service';
// Gateway
import { NatsBridgeGateway } from './gateways/nats-bridge.gateway';
@Module({
imports: [
// Configuration
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
}),
// Database
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres' as const,
url: config.get<string>('database.url'),
autoLoadEntities: true,
synchronize: false, // NEVER auto-sync — use migrations only
extra: {
max: config.get<number>('database.maxConnections') || 20,
},
}),
}),
// JWT (global)
JwtModule.registerAsync({
global: true,
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get<string>('jwt.secret'),
signOptions: {
expiresIn: `${config.get<number>('jwt.accessExpirySeconds') || 900}s`,
},
}),
}),
// Scheduler
ScheduleModule.forRoot(),
// Repositories for app-level shared services (host-agent consumer)
TypeOrmModule.forFeature([ServerConnection, License]),
// Feature Modules
AuthModule,
UsersModule,
LicensesModule,
ServersModule,
PlayersModule,
WipesModule,
MapsModule,
PluginsModule,
ChatModule,
TeamModule,
NotificationsModule,
SettingsModule,
SchedulesModule,
AnalyticsModule,
AlertsModule,
StatusModule,
StoreModule,
WebstoreModule,
AdminModule,
SetupModule,
MigrationModule,
ChangelogModule,
FilesModule,
LootModule,
TeleportModule,
GatherModule,
AutoDoorsModule,
KitsModule,
FurnaceSplitterModule,
BetterChatModule,
TimedExecuteModule,
RaidableBasesModule,
EarlyAccessModule,
],
providers: [
// Global guards (order matters: auth first, then license, then permissions)
{ provide: APP_GUARD, useClass: JwtAuthGuard },
{ provide: APP_GUARD, useClass: PermissionsGuard },
// Shared services
NatsService,
NatsBridgeService,
HostAgentConsumerService,
SteamService,
// WebSocket gateway
NatsBridgeGateway,
],
exports: [NatsService, NatsBridgeService, SteamService],
})
export class AppModule {}