1-
2- import { Router } from 'express'
1+ import { Router , Request , Response } from 'express'
32import { StellarService } from '../services/stellar.js'
43import { ReflectorService } from '../services/reflector.js'
5- import { RebalanceHistoryService } from '../services/rebalanceHistory.js'
6- import { RiskManagementService } from '../services/riskManagements.js'
4+ import { riskManagementService , rebalanceHistoryService } from '../services/serviceContainer.js'
75import { portfolioStorage } from '../services/portfolioStorage.js'
86import { CircuitBreakers } from '../services/circuitBreakers.js'
97import { analyticsService } from '../services/analyticsService.js'
108import { notificationService } from '../services/notificationService.js'
119import { contractEventIndexerService } from '../services/contractEventIndexer.js'
10+ import { AutoRebalancerService } from '../services/autoRebalancer.js'
1211import { logger } from '../utils/logger.js'
1312import { idempotencyMiddleware } from '../middleware/idempotency.js'
13+ import { requireAdmin } from '../middleware/auth.js'
14+ import { writeRateLimiter } from '../middleware/rateLimit.js'
15+ import { blockDebugInProduction } from '../middleware/debugGate.js'
16+ import { getFeatureFlags , getPublicFeatureFlags } from '../config/featureFlags.js'
17+ import { getQueueMetrics } from '../queue/queueMetrics.js'
18+ import { getErrorMessage , getErrorObject , parseOptionalBoolean } from '../utils/helpers.js'
19+
20+ const router = Router ( )
21+ const stellarService = new StellarService ( )
22+ const reflectorService = new ReflectorService ( )
23+ const autoRebalancer = new AutoRebalancerService ( )
24+ const featureFlags = getFeatureFlags ( )
25+ const publicFeatureFlags = getPublicFeatureFlags ( )
1426
1527const parseOptionalTimestamp = ( value : unknown ) : string | undefined => {
1628 if ( value === undefined || value === null || value === '' ) return undefined
@@ -20,17 +32,17 @@ const parseOptionalTimestamp = (value: unknown): string | undefined => {
2032 return ts . toISOString ( )
2133}
2234
23- const parseHistorySource = ( value : unknown ) : 'all' | ' offchain' | 'simulated' | 'onchain' => {
24- if ( typeof value !== 'string' ) return 'all'
35+ const parseHistorySource = ( value : unknown ) : 'offchain' | 'simulated' | 'onchain' | undefined => {
36+ if ( typeof value !== 'string' ) return undefined
2537 const normalized = value . trim ( ) . toLowerCase ( )
2638 if ( normalized === 'offchain' ) return 'offchain'
2739 if ( normalized === 'simulated' ) return 'simulated'
2840 if ( normalized === 'onchain' ) return 'onchain'
29- return 'all'
41+ return undefined
3042}
3143
3244
33- router . get ( '/rebalance/history' , async ( req , res ) => {
45+ router . get ( '/rebalance/history' , async ( req : Request , res : Response ) => {
3446 try {
3547 const portfolioId = req . query . portfolioId as string
3648 const limit = parseInt ( req . query . limit as string ) || 50
@@ -77,7 +89,7 @@ router.get('/rebalance/history', async (req, res) => {
7789} )
7890
7991// Record new rebalance event
80- router . post ( '/rebalance/history' , idempotencyMiddleware , async ( req , res ) => {
92+ router . post ( '/rebalance/history' , idempotencyMiddleware , async ( req : Request , res : Response ) => {
8193 try {
8294 const eventData = req . body
8395
@@ -102,7 +114,7 @@ router.post('/rebalance/history', idempotencyMiddleware, async (req, res) => {
102114 }
103115} )
104116
105- router . post ( '/rebalance/history/sync-onchain' , requireAdmin , async ( req , res ) => {
117+ router . post ( '/rebalance/history/sync-onchain' , requireAdmin , async ( req : Request , res : Response ) => {
106118 try {
107119 const result = await contractEventIndexerService . syncOnce ( )
108120 res . json ( {
@@ -124,7 +136,7 @@ router.post('/rebalance/history/sync-onchain', requireAdmin, async (req, res) =>
124136// ================================
125137
126138// Get risk metrics for a portfolio
127- router . get ( '/risk/metrics/:portfolioId' , async ( req , res ) => {
139+ router . get ( '/risk/metrics/:portfolioId' , async ( req : Request , res : Response ) => {
128140 try {
129141 const { portfolioId } = req . params
130142
@@ -134,7 +146,14 @@ router.get('/risk/metrics/:portfolioId', async (req, res) => {
134146 const prices = await reflectorService . getCurrentPrices ( )
135147
136148 // Calculate risk metrics with proper type conversion
137- const allocationsRecord = getPortfolioAllocationsAsRecord ( portfolio )
149+ const allocationsRecord : Record < string , number > = { }
150+ if ( Array . isArray ( portfolio . allocations ) ) {
151+ portfolio . allocations . forEach ( ( a : any ) => {
152+ allocationsRecord [ a . asset ] = a . target
153+ } )
154+ } else {
155+ Object . assign ( allocationsRecord , portfolio . allocations )
156+ }
138157 const riskMetrics = riskManagementService . analyzePortfolioRisk ( allocationsRecord , prices )
139158 const recommendations = riskManagementService . getRecommendations ( riskMetrics , allocationsRecord )
140159 const circuitBreakers = riskManagementService . getCircuitBreakerStatus ( )
@@ -164,7 +183,7 @@ router.get('/risk/metrics/:portfolioId', async (req, res) => {
164183} )
165184
166185// Check if rebalancing should be allowed based on risk conditions
167- router . get ( '/risk/check/:portfolioId' , async ( req , res ) => {
186+ router . get ( '/risk/check/:portfolioId' , async ( req : Request , res : Response ) => {
168187 try {
169188 const { portfolioId } = req . params
170189
@@ -198,7 +217,7 @@ router.get('/risk/check/:portfolioId', async (req, res) => {
198217// ================================
199218
200219// Get current prices - FIXED to return direct format for frontend
201- router . get ( '/prices' , async ( req , res ) => {
220+ router . get ( '/prices' , async ( req : Request , res : Response ) => {
202221 try {
203222 console . log ( '[DEBUG] Fetching prices for frontend...' )
204223 const prices = await reflectorService . getCurrentPrices ( )
@@ -232,7 +251,7 @@ router.get('/prices', async (req, res) => {
232251} )
233252
234253// Enhanced prices endpoint with risk analysis
235- router . get ( '/prices/enhanced' , async ( req , res ) => {
254+ router . get ( '/prices/enhanced' , async ( req : Request , res : Response ) => {
236255 try {
237256 console . log ( '[INFO] Fetching enhanced prices with risk analysis' )
238257
@@ -275,7 +294,7 @@ router.get('/prices/enhanced', async (req, res) => {
275294} )
276295
277296// Get detailed market data for specific asset
278- router . get ( '/market/:asset/details' , async ( req , res ) => {
297+ router . get ( '/market/:asset/details' , async ( req : Request , res : Response ) => {
279298 try {
280299 const asset = req . params . asset . toUpperCase ( )
281300 const reflector = new ReflectorService ( )
@@ -292,7 +311,7 @@ router.get('/market/:asset/details', async (req, res) => {
292311} )
293312
294313// Get price charts for frontend
295- router . get ( '/market/:asset/chart' , async ( req , res ) => {
314+ router . get ( '/market/:asset/chart' , async ( req : Request , res : Response ) => {
296315 try {
297316 const asset = req . params . asset . toUpperCase ( )
298317 const days = parseInt ( req . query . days as string ) || 7
@@ -316,7 +335,7 @@ router.get('/market/:asset/chart', async (req, res) => {
316335// AUTO-REBALANCER ROUTES
317336// ================================
318337
319- router . get ( '/auto-rebalancer/status' , async ( req , res ) => {
338+ router . get ( '/auto-rebalancer/status' , async ( req : Request , res : Response ) => {
320339 try {
321340 if ( ! autoRebalancer ) {
322341 return res . json ( {
@@ -343,7 +362,7 @@ router.get('/auto-rebalancer/status', async (req, res) => {
343362 }
344363} )
345364
346- router . post ( '/auto-rebalancer/start' , requireAdmin , ( req , res ) => {
365+ router . post ( '/auto-rebalancer/start' , requireAdmin , ( req : Request , res : Response ) => {
347366 try {
348367 if ( ! autoRebalancer ) {
349368 return res . status ( 500 ) . json ( {
@@ -368,7 +387,7 @@ router.post('/auto-rebalancer/start', requireAdmin, (req, res) => {
368387 }
369388} )
370389
371- router . post ( '/auto-rebalancer/stop' , requireAdmin , ( req , res ) => {
390+ router . post ( '/auto-rebalancer/stop' , requireAdmin , ( req : Request , res : Response ) => {
372391 try {
373392 if ( ! autoRebalancer ) {
374393 return res . status ( 500 ) . json ( {
@@ -393,7 +412,7 @@ router.post('/auto-rebalancer/stop', requireAdmin, (req, res) => {
393412 }
394413} )
395414
396- router . post ( '/auto-rebalancer/force-check' , requireAdmin , async ( req , res ) => {
415+ router . post ( '/auto-rebalancer/force-check' , requireAdmin , async ( req : Request , res : Response ) => {
397416 try {
398417 if ( ! autoRebalancer ) {
399418 return res . status ( 500 ) . json ( {
@@ -417,7 +436,7 @@ router.post('/auto-rebalancer/force-check', requireAdmin, async (req, res) => {
417436 }
418437} )
419438
420- router . get ( '/auto-rebalancer/history' , requireAdmin , async ( req , res ) => {
439+ router . get ( '/auto-rebalancer/history' , requireAdmin , async ( req : Request , res : Response ) => {
421440 try {
422441 const portfolioId = req . query . portfolioId as string
423442 const limit = parseInt ( req . query . limit as string ) || 50
@@ -450,7 +469,7 @@ router.get('/auto-rebalancer/history', requireAdmin, async (req, res) => {
450469// ================================
451470
452471// Get comprehensive system status
453- router . get ( '/system/status' , async ( req , res ) => {
472+ router . get ( '/system/status' , async ( req : Request , res : Response ) => {
454473 try {
455474 const portfolioCount = await portfolioStorage . getPortfolioCount ( )
456475 const historyStats = await rebalanceHistoryService . getHistoryStats ( )
@@ -518,7 +537,7 @@ router.get('/system/status', async (req, res) => {
518537// ANALYTICS ROUTES
519538// ================================
520539
521- router . get ( '/portfolio/:id/analytics' , async ( req , res ) => {
540+ router . get ( '/portfolio/:id/analytics' , async ( req : Request , res : Response ) => {
522541 try {
523542 const portfolioId = req . params . id
524543 const days = parseInt ( req . query . days as string ) || 30
@@ -551,7 +570,7 @@ router.get('/portfolio/:id/analytics', async (req, res) => {
551570 }
552571} )
553572
554- router . get ( '/portfolio/:id/performance-summary' , async ( req , res ) => {
573+ router . get ( '/portfolio/:id/performance-summary' , async ( req : Request , res : Response ) => {
555574 try {
556575 const portfolioId = req . params . id
557576
@@ -586,7 +605,7 @@ router.get('/portfolio/:id/performance-summary', async (req, res) => {
586605// ================================
587606
588607// Subscribe to notifications
589- router . post ( '/notifications/subscribe' , writeRateLimiter , idempotencyMiddleware , async ( req , res ) => {
608+ router . post ( '/notifications/subscribe' , writeRateLimiter , idempotencyMiddleware , async ( req : Request , res : Response ) => {
590609 try {
591610 const { userId, emailEnabled, emailAddress, webhookEnabled, webhookUrl, events } = req . body
592611
@@ -666,7 +685,7 @@ router.post('/notifications/subscribe', writeRateLimiter, idempotencyMiddleware,
666685} )
667686
668687// Get notification preferences
669- router . get ( '/notifications/preferences' , async ( req , res ) => {
688+ router . get ( '/notifications/preferences' , async ( req : Request , res : Response ) => {
670689 try {
671690 const userId = req . query . userId as string
672691
@@ -702,7 +721,7 @@ router.get('/notifications/preferences', async (req, res) => {
702721} )
703722
704723// Unsubscribe from notifications
705- router . delete ( '/notifications/unsubscribe' , async ( req , res ) => {
724+ router . delete ( '/notifications/unsubscribe' , async ( req : Request , res : Response ) => {
706725 try {
707726 const userId = req . query . userId as string
708727
@@ -736,7 +755,7 @@ router.delete('/notifications/unsubscribe', async (req, res) => {
736755// ================================
737756
738757// Test notification delivery
739- // router.post('/notifications/test', async (req, res) => {
758+ // router.post('/notifications/test', async (req: Request , res: Response ) => {
740759// try {
741760// const { userId, eventType } = req.body
742761
@@ -840,7 +859,7 @@ router.delete('/notifications/unsubscribe', async (req, res) => {
840859// })
841860
842861// Test all notification types at once
843- // router.post('/notifications/test-all', async (req, res) => {
862+ // router.post('/notifications/test-all', async (req: Request , res: Response ) => {
844863// try {
845864// const { userId } = req.body
846865
@@ -931,7 +950,7 @@ router.delete('/notifications/unsubscribe', async (req, res) => {
931950// DEBUG ROUTES
932951// ================================
933952
934- router . get ( '/debug/coingecko-test' , blockDebugInProduction , async ( req , res ) => {
953+ router . get ( '/debug/coingecko-test' , blockDebugInProduction , async ( req : Request , res : Response ) => {
935954 try {
936955 const apiKey = process . env . COINGECKO_API_KEY
937956 console . log ( '[DEBUG] API Key exists:' , ! ! apiKey )
@@ -971,7 +990,7 @@ router.get('/debug/coingecko-test', blockDebugInProduction, async (req, res) =>
971990 }
972991} )
973992
974- router . get ( '/debug/force-fresh-prices' , blockDebugInProduction , async ( req , res ) => {
993+ router . get ( '/debug/force-fresh-prices' , blockDebugInProduction , async ( req : Request , res : Response ) => {
975994 try {
976995 console . log ( '[DEBUG] Clearing cache and forcing fresh prices...' )
977996
@@ -1000,7 +1019,7 @@ router.get('/debug/force-fresh-prices', blockDebugInProduction, async (req, res)
10001019 }
10011020} )
10021021
1003- router . get ( '/debug/reflector-test' , blockDebugInProduction , async ( req , res ) => {
1022+ router . get ( '/debug/reflector-test' , blockDebugInProduction , async ( req : Request , res : Response ) => {
10041023 try {
10051024 console . log ( '[DEBUG] Testing reflector service...' )
10061025
@@ -1027,7 +1046,7 @@ router.get('/debug/reflector-test', blockDebugInProduction, async (req, res) =>
10271046 }
10281047} )
10291048
1030- router . get ( '/debug/env' , blockDebugInProduction , async ( req , res ) => {
1049+ router . get ( '/debug/env' , blockDebugInProduction , async ( req : Request , res : Response ) => {
10311050 try {
10321051 res . json ( {
10331052 environment : process . env . NODE_ENV ,
@@ -1046,7 +1065,7 @@ router.get('/debug/env', blockDebugInProduction, async (req, res) => {
10461065 }
10471066} )
10481067
1049- router . get ( '/debug/auto-rebalancer-test' , blockDebugInProduction , async ( req , res ) => {
1068+ router . get ( '/debug/auto-rebalancer-test' , blockDebugInProduction , async ( req : Request , res : Response ) => {
10501069 try {
10511070 if ( ! autoRebalancer ) {
10521071 return res . json ( {
@@ -1086,7 +1105,7 @@ router.get('/debug/auto-rebalancer-test', blockDebugInProduction, async (req, re
10861105 * Returns BullMQ queue depths and Redis connectivity status.
10871106 * Used for worker health monitoring and alerting (issue #38).
10881107 */
1089- router . get ( '/queue/health' , async ( req , res ) => {
1108+ router . get ( '/queue/health' , async ( req : Request , res : Response ) => {
10901109 try {
10911110 const metrics = await getQueueMetrics ( )
10921111 const httpStatus = metrics . redisConnected ? 200 : 503
0 commit comments