| const path = require("path"); |
| const { config } = require("./config/env"); |
| const { createLogger } = require("./config/logger"); |
| const { createApp } = require("./app"); |
| const { createRuntimeState } = require("./services/runtimeState"); |
| const { createDbPool, verifyDbConnection } = require("./db/pool"); |
| const { runMigrations, runSeeds } = require("./db/migrator"); |
| const { createMqttConsumer } = require("./mqtt/client"); |
| const { createTelemetryIngestService } = require("./services/telemetryIngestService"); |
| const { createAlertEngineService } = require("./services/alertEngineService"); |
| const { createFleetService } = require("./services/fleetService"); |
| const { createAlertsService } = require("./services/alertsService"); |
| const { createReportsService } = require("./services/reportsService"); |
| const { createAdminService } = require("./services/adminService"); |
| const { createAuthService } = require("./services/authService"); |
| const { createTripsService } = require("./services/tripsService"); |
| const { createTripSummaryAiService } = require("./services/tripSummaryAiService"); |
| const { startOfflineScannerJob } = require("./jobs/offlineScannerJob"); |
| const telemetryRepository = require("./repositories/telemetryRepository"); |
| const assetRepository = require("./repositories/assetRepository"); |
| const alertRulesRepository = require("./repositories/alertRulesRepository"); |
| const alertsRepository = require("./repositories/alertsRepository"); |
| const reportsRepository = require("./repositories/reportsRepository"); |
| const adminRepository = require("./repositories/adminRepository"); |
| const authRepository = require("./repositories/authRepository"); |
| const auditRepository = require("./repositories/auditRepository"); |
| const tripsRepository = require("./repositories/tripsRepository"); |
| const telemetryValidator = require("./validators/telemetryValidator"); |
|
|
| async function main() { |
| const logger = createLogger(config.logLevel); |
| const runtimeState = createRuntimeState(); |
| const pool = createDbPool(config, logger); |
|
|
| let mqttConsumer = null; |
| let offlineScannerJob = null; |
| let server = null; |
| let shuttingDown = false; |
|
|
| async function shutdown(signal) { |
| if (shuttingDown) { |
| return; |
| } |
| shuttingDown = true; |
|
|
| logger.info("Shutdown started", { signal }); |
|
|
| try { |
| if (offlineScannerJob) { |
| offlineScannerJob.stop(); |
| } |
| if (mqttConsumer) { |
| mqttConsumer.stop(); |
| } |
|
|
| if (server) { |
| await new Promise((resolve) => server.close(resolve)); |
| } |
|
|
| await pool.end(); |
| logger.info("Shutdown complete"); |
| process.exit(0); |
| } catch (error) { |
| logger.error("Shutdown failed", { error: error.message }); |
| process.exit(1); |
| } |
| } |
|
|
| process.on("SIGINT", () => shutdown("SIGINT")); |
| process.on("SIGTERM", () => shutdown("SIGTERM")); |
|
|
| try { |
| await verifyDbConnection(pool); |
| runtimeState.markDbHealthy(); |
| logger.info("PostgreSQL connection established"); |
|
|
| if (config.db.runMigrationsOnBoot) { |
| const migrationsDir = path.resolve(__dirname, "../migrations"); |
| await runMigrations(pool, migrationsDir, logger); |
| } |
|
|
| if (config.db.runSeedsOnBoot) { |
| const seedsDir = path.resolve(__dirname, "../seeds"); |
| await runSeeds(pool, seedsDir, logger); |
| } |
|
|
| const alertEngineService = createAlertEngineService({ |
| config, |
| alertsRepository, |
| alertRulesRepository, |
| }); |
|
|
| const telemetryIngestService = createTelemetryIngestService({ |
| pool, |
| logger, |
| runtimeState, |
| telemetryRepository, |
| assetRepository, |
| alertEngineService, |
| telemetryValidator, |
| }); |
|
|
| const tripSummaryAiService = createTripSummaryAiService({ |
| config, |
| logger, |
| }); |
|
|
| const services = { |
| authService: createAuthService({ |
| config, |
| pool, |
| authRepository, |
| }), |
| fleetService: createFleetService({ |
| pool, |
| config, |
| telemetryRepository, |
| }), |
| alertsService: createAlertsService({ |
| pool, |
| config, |
| alertsRepository, |
| }), |
| reportsService: createReportsService({ |
| pool, |
| config, |
| reportsRepository, |
| }), |
| tripsService: createTripsService({ |
| pool, |
| tripsRepository, |
| assetRepository, |
| auditRepository, |
| alertsRepository, |
| tripSummaryAiService, |
| }), |
| adminService: createAdminService({ |
| pool, |
| adminRepository, |
| auditRepository, |
| }), |
| }; |
|
|
| mqttConsumer = createMqttConsumer({ |
| config, |
| logger, |
| runtimeState, |
| onTelemetryMessage: telemetryIngestService.handleIncomingTelemetry, |
| }); |
|
|
| offlineScannerJob = startOfflineScannerJob({ |
| pool, |
| config, |
| logger, |
| runtimeState, |
| telemetryRepository, |
| alertEngineService, |
| }); |
|
|
| const app = createApp(config, logger, services, runtimeState); |
| server = app.listen(config.server.port, () => { |
| logger.info("Backend started", { |
| port: config.server.port, |
| apiPrefix: config.server.apiPrefix, |
| mqttTopicFilter: config.mqtt.topicFilter, |
| }); |
| }); |
| } catch (error) { |
| runtimeState.markDbError(error); |
| logger.error("Backend startup failed", { error: error.message }); |
| await pool.end(); |
| process.exit(1); |
| } |
| } |
|
|
| main(); |
|
|