11import express from 'express' ;
22import cookieParser from 'cookie-parser' ;
33import dotenv from 'dotenv' ;
4+ import * as Sentry from '@sentry/node' ;
5+ import { nodeProfilingIntegration } from '@sentry/profiling-node' ;
46import swaggerUi from 'swagger-ui-express' ;
57// Load environment variables before importing other modules
68dotenv . config ( ) ;
@@ -118,6 +120,211 @@ app.get('/api/admin/metrics/subscriptions', adminAuth, async (req, res) => {
118120 }
119121} ) ;
120122
123+ // Load environment variables before importing other modules
124+ dotenv . config ( ) ;
125+
126+ Sentry . init ( {
127+ dsn : process . env . SENTRY_DSN ,
128+ environment : process . env . NODE_ENV ,
129+ integrations : [ nodeProfilingIntegration ( ) ] ,
130+ tracesSampleRate : 0.1 ,
131+ profilesSampleRate : 0.1 ,
132+ } ) ;
133+
134+
135+ import logger from './config/logger' ;
136+ import { requestIdMiddleware } from './middleware/requestContext' ;
137+ import { requestLoggerMiddleware } from './middleware/requestLogger' ;
138+ import { schedulerService } from './services/scheduler' ;
139+ import { reminderEngine } from './services/reminder-engine' ;
140+ import subscriptionRoutes from './routes/subscriptions' ;
141+ import riskScoreRoutes from './routes/risk-score' ;
142+ import simulationRoutes from './routes/simulation' ;
143+ import merchantRoutes from './routes/merchants' ;
144+ import teamRoutes from './routes/team' ;
145+ import auditRoutes from './routes/audit' ;
146+ import webhookRoutes from './routes/webhooks' ;
147+ import { monitoringService } from './services/monitoring-service' ;
148+ import { healthService } from './services/health-service' ;
149+ import { eventListener } from './services/event-listener' ;
150+ import { expiryService } from './services/expiry-service' ;
151+ import { scheduleAutoResume } from './jobs/auto-resume' ;
152+
153+ const app = express ( ) ;
154+
155+ // Add Sentry request handler before routes
156+ app . use ( Sentry . Handlers . requestHandler ( ) ) ;
157+
158+ const PORT = process . env . PORT || 3001 ;
159+ const ADMIN_API_KEY = process . env . ADMIN_API_KEY || 'development-admin-key' ;
160+
161+ // CORS configuration
162+ const FRONTEND_URL = process . env . FRONTEND_URL || 'http://localhost:3000' ;
163+ app . use ( ( req , res , next ) => {
164+ res . header ( 'Access-Control-Allow-Origin' , FRONTEND_URL ) ;
165+ res . header ( 'Access-Control-Allow-Credentials' , 'true' ) ;
166+ res . header ( 'Access-Control-Allow-Methods' , 'GET, POST, PUT, PATCH, DELETE, OPTIONS' ) ;
167+ res . header ( 'Access-Control-Allow-Headers' , 'Content-Type, Authorization, Idempotency-Key, If-Match' ) ;
168+
169+ if ( req . method === 'OPTIONS' ) {
170+ return res . sendStatus ( 200 ) ;
171+ }
172+ next ( ) ;
173+ } ) ;
174+
175+ // Middleware
176+ app . use ( cookieParser ( ) ) ;
177+ app . use ( express . json ( ) ) ;
178+ app . use ( express . urlencoded ( { extended : true } ) ) ;
179+
180+ // Request tracing — must come before routes so every log line carries requestId
181+ app . use ( requestIdMiddleware ) ;
182+ app . use ( requestLoggerMiddleware ) ;
183+
184+
185+ import { adminAuth } from './middleware/admin' ;
186+ import { createAdminLimiter , RateLimiterFactory } from './middleware/rate-limit-factory' ;
187+
188+ // Health check endpoint
189+ app . get ( '/health' , ( req , res ) => {
190+ res . json ( { status : 'ok' , timestamp : new Date ( ) . toISOString ( ) } ) ;
191+ } ) ;
192+
193+ // API Routes
194+ app . use ( '/api/subscriptions' , subscriptionRoutes ) ;
195+ app . use ( '/api/risk-score' , riskScoreRoutes ) ;
196+ app . use ( '/api/simulation' , simulationRoutes ) ;
197+ app . use ( '/api/merchants' , merchantRoutes ) ;
198+ app . use ( '/api/team' , teamRoutes ) ;
199+ app . use ( '/api/audit' , auditRoutes ) ;
200+ app . use ( '/api/webhooks' , webhookRoutes ) ;
201+
202+ // API Routes (Public/Standard)
203+ app . get ( '/api/reminders/status' , ( req , res ) => {
204+ const status = schedulerService . getStatus ( ) ;
205+ res . json ( status ) ;
206+ } ) ;
207+
208+ // Admin Monitoring Endpoints (Read-only)
209+ app . get ( '/api/admin/metrics/subscriptions' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
210+ try {
211+ const metrics = await monitoringService . getSubscriptionMetrics ( ) ;
212+ res . json ( metrics ) ;
213+ } catch ( error ) {
214+ res . status ( 500 ) . json ( { error : 'Failed to fetch subscription metrics' } ) ;
215+ }
216+ } ) ;
217+
218+ app . get ( '/api/admin/metrics/renewals' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
219+ try {
220+ const metrics = await monitoringService . getRenewalMetrics ( ) ;
221+ res . json ( metrics ) ;
222+ } catch ( error ) {
223+ res . status ( 500 ) . json ( { error : 'Failed to fetch renewal metrics' } ) ;
224+ }
225+ } ) ;
226+
227+ app . get ( '/api/admin/metrics/activity' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
228+ try {
229+ const metrics = await monitoringService . getAgentActivity ( ) ;
230+ res . json ( metrics ) ;
231+ } catch ( error ) {
232+ res . status ( 500 ) . json ( { error : 'Failed to fetch agent activity' } ) ;
233+ }
234+ } ) ;
235+
236+ // Protocol Health Monitor: unified admin health (metrics, alerts, history)
237+ app . get ( '/api/admin/health' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
238+ try {
239+ const includeHistory = req . query . history !== 'false' ;
240+ const health = await healthService . getAdminHealth ( includeHistory ) ;
241+ const statusCode = health . status === 'unhealthy' ? 503 : 200 ;
242+ res . status ( statusCode ) . json ( health ) ;
243+ } catch ( error ) {
244+ logger . error ( 'Error fetching admin health:' , error ) ;
245+ res . status ( 500 ) . json ( { error : 'Failed to fetch health status' } ) ;
246+ }
247+ } ) ;
248+
249+ // Manual trigger endpoints (for testing/admin - Should eventually be protected)
250+ app . post ( '/api/reminders/process' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
251+ try {
252+ await reminderEngine . processReminders ( ) ;
253+ res . json ( { success : true , message : 'Reminders processed' } ) ;
254+ } catch ( error ) {
255+ logger . error ( 'Error processing reminders:' , error ) ;
256+ res . status ( 500 ) . json ( {
257+ success : false ,
258+ error : error instanceof Error ? error . message : String ( error ) ,
259+ } ) ;
260+ }
261+ } ) ;
262+
263+ app . post ( '/api/reminders/schedule' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
264+ try {
265+ const daysBefore = req . body . daysBefore || [ 7 , 3 , 1 ] ;
266+ await reminderEngine . scheduleReminders ( daysBefore ) ;
267+ res . json ( { success : true , message : 'Reminders scheduled' } ) ;
268+ } catch ( error ) {
269+ logger . error ( 'Error scheduling reminders:' , error ) ;
270+ res . status ( 500 ) . json ( {
271+ success : false ,
272+ error : error instanceof Error ? error . message : String ( error ) ,
273+ } ) ;
274+ }
275+ } ) ;
276+
277+ app . post ( '/api/reminders/retry' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
278+ try {
279+ await reminderEngine . processRetries ( ) ;
280+ res . json ( { success : true , message : 'Retries processed' } ) ;
281+ } catch ( error ) {
282+ logger . error ( 'Error processing retries:' , error ) ;
283+ res . status ( 500 ) . json ( {
284+ success : false ,
285+ error : error instanceof Error ? error . message : String ( error ) ,
286+ } ) ;
287+ }
288+ } ) ;
289+
290+ // Protocol Health Monitor: record metrics snapshot periodically (historical storage)
291+ const HEALTH_SNAPSHOT_INTERVAL_MS = 15 * 60 * 1000 ; // 15 minutes
292+ function startHealthSnapshotInterval ( ) {
293+ setInterval ( ( ) => {
294+ healthService . recordSnapshot ( ) . catch ( ( ) => { } ) ;
295+ } , HEALTH_SNAPSHOT_INTERVAL_MS ) ;
296+ // Record one snapshot shortly after startup
297+ setTimeout ( ( ) => healthService . recordSnapshot ( ) . catch ( ( ) => { } ) , 5000 ) ;
298+ }
299+
300+ app . post ( '/api/admin/expiry/process' , createAdminLimiter ( ) , adminAuth , async ( req , res ) => {
301+ try {
302+ const result = await expiryService . processExpiries ( ) ;
303+ res . json ( { success : true , data : result } ) ;
304+ } catch ( error ) {
305+ logger . error ( 'Error processing expiries:' , error ) ;
306+ res . status ( 500 ) . json ( {
307+ success : false ,
308+ error : error instanceof Error ? error . message : String ( error ) ,
309+ } ) ;
310+ }
311+ } ) ;
312+
313+ // Add Sentry error handler after all routes
314+ app . use ( Sentry . Handlers . errorHandler ( ) ) ;
315+
316+ // Start server
317+ const server = app . listen ( PORT , async ( ) => {
318+ logger . info ( `Server running on port ${ PORT } ` ) ;
319+ logger . info ( `Environment: ${ process . env . NODE_ENV || 'development' } ` ) ;
320+
321+ // Initialize rate limiting Redis store
322+ try {
323+ await RateLimiterFactory . initializeRedisStore ( ) ;
324+ logger . info ( 'Rate limiting initialized successfully' ) ;
325+ } catch ( error ) {
326+ logger . warn ( 'Rate limiting initialization failed, using memory store:' , error ) ;
327+ import * as bip39 from 'bip39' ;
121328/**
122329 * @openapi
123330 * /api/admin/metrics/renewals:
@@ -132,6 +339,8 @@ app.get('/api/admin/metrics/subscriptions', adminAuth, async (req, res) => {
132339 * 401:
133340 * description: Unauthorized
134341 */
342+ export function generateMnemonic ( ) : string {
343+ return bip39 . generateMnemonic ( 128 ) ;
135344app . get ( '/api/admin/metrics/renewals' , adminAuth , async ( req , res ) => {
136345 try {
137346 const metrics = await monitoringService . getRenewalMetrics ( ) ;
0 commit comments