@@ -19,6 +19,10 @@ const transactionProcessor = require('./workers/transactionProcessor');
1919const transactionEvents = require ( './events/transactionEvents' ) ;
2020const emailWorker = require ( './workers/emailWorker' ) ;
2121
22+ // Event Indexer – polls Soroban for on-chain events and syncs them to PostgreSQL
23+ let eventIndexerInstance = null ;
24+ const EVENT_INDEXER_ENABLED = process . env . EVENT_INDEXER_ENABLED === 'true' ;
25+
2226// Import security middleware
2327const {
2428 securityPerformanceTracker,
@@ -216,22 +220,46 @@ v1Router.use('/cross-protocol-bridge', crossProtocolBridgeRoutes);
216220const adminRoutes = require ( './routes/admin' ) ;
217221v1Router . use ( '/admin' , adminRoutes ) ;
218222
219- // Schemas helper for versioned responses
220- const { createVersionedResponse } = require ( './utils/schemas' ) ;
221- const { errorHandler } = require ( './middleware/errorHandler' ) ;
222- const { ValidationError } = require ( './utils/errors' ) ;
223- const { getCompressionStats } = require ( './middleware/compression' ) ;
223+ // Event Indexer admin routes (start / stop / status)
224+ const indexerAdminRouter = require ( 'express' ) . Router ( ) ;
224225
225- // Health check under v1 for OpenAPI spec compatibility
226- v1Router . get ( '/health' , ( req , res ) => {
227- const version = req . apiVersion || 'v1' ;
228- res . json ( createVersionedResponse ( {
229- status : 'healthy' ,
230- uptime : process . uptime ( ) ,
231- supportedVersions : SUPPORTED_VERSIONS ,
232- } , version ) ) ;
226+ indexerAdminRouter . get ( '/status' , ( req , res ) => {
227+ try {
228+ const { getIndexerStatus } = require ( './services/eventIndexer' ) ;
229+ res . json ( { eventIndexer : getIndexerStatus ( ) } ) ;
230+ } catch ( err ) {
231+ res . json ( { eventIndexer : { status : 'stopped' , error : err . message } } ) ;
232+ }
233233} ) ;
234234
235+ indexerAdminRouter . post ( '/start' , async ( req , res ) => {
236+ try {
237+ if ( ! eventIndexerInstance ) {
238+ return res . status ( 400 ) . json ( { error : 'Indexer not initialized' } ) ;
239+ }
240+ await eventIndexerInstance . start ( ) ;
241+ const { getIndexerStatus } = require ( './services/eventIndexer' ) ;
242+ res . json ( { message : 'Indexer started' , status : getIndexerStatus ( ) } ) ;
243+ } catch ( err ) {
244+ res . status ( 500 ) . json ( { error : err . message } ) ;
245+ }
246+ } ) ;
247+
248+ indexerAdminRouter . post ( '/stop' , async ( req , res ) => {
249+ try {
250+ if ( ! eventIndexerInstance ) {
251+ return res . status ( 400 ) . json ( { error : 'Indexer not initialized' } ) ;
252+ }
253+ await eventIndexerInstance . stop ( ) ;
254+ const { getIndexerStatus } = require ( './services/eventIndexer' ) ;
255+ res . json ( { message : 'Indexer stopped' , status : getIndexerStatus ( ) } ) ;
256+ } catch ( err ) {
257+ res . status ( 500 ) . json ( { error : err . message } ) ;
258+ }
259+ } ) ;
260+
261+ v1Router . use ( '/indexer' , require ( './middleware/auth' ) . requireAdmin , indexerAdminRouter ) ;
262+
235263// Mount v1 router at /api/v1
236264app . use ( '/api/v1' , v1Router ) ;
237265
@@ -304,6 +332,27 @@ async function startServer() {
304332 await transactionEvents . startListening ( ) ;
305333 emailWorker . getEmailWorker ( ) . start ( ) ;
306334
335+ // Start the event indexer if enabled
336+ if ( EVENT_INDEXER_ENABLED ) {
337+ try {
338+ const { Pool } = require ( 'pg' ) ;
339+ const { getEventIndexer } = require ( './services/eventIndexer' ) ;
340+ const indexerPool = new Pool ( {
341+ connectionString : process . env . DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/starked' ,
342+ max : 5 , // dedicated small pool for the indexer
343+ idleTimeoutMillis : 30000 ,
344+ connectionTimeoutMillis : 5000 ,
345+ } ) ;
346+ eventIndexerInstance = getEventIndexer ( indexerPool ) ;
347+ await eventIndexerInstance . start ( ) ;
348+ console . log ( '🔗 Event Indexer started – polling Soroban for on-chain events' ) ;
349+ } catch ( indexerErr ) {
350+ console . error ( '⚠️ Event Indexer failed to start (non-fatal):' , indexerErr . message ) ;
351+ }
352+ } else {
353+ console . log ( 'ℹ️ Event Indexer disabled. Set EVENT_INDEXER_ENABLED=true to enable.' ) ;
354+ }
355+
307356 server . listen ( PORT , ( ) => {
308357 console . log ( `🚀 StarkEd Education Backend running on port ${ PORT } ` ) ;
309358 console . log ( `📚 Quiz Management API available at /api/v1/quizzes` ) ;
@@ -317,6 +366,7 @@ async function startServer() {
317366 console . log ( `🌐 Federated Learning API available at /api/v1/federated-learning` ) ;
318367 console . log ( `🧠 AGI Tutor API available at /api/v1/agi-tutor` ) ;
319368 console . log ( `🔐 Quantum-Resistant Secure Communication API available at /api/v1/secure-comm` ) ;
369+ console . log ( `🔗 Event Indexer API available at /api/v1/indexer (admin-only)` ) ;
320370 console . log ( `🏥 Health check available at /api/health` ) ;
321371 console . log ( `✅ Transaction Queue System initialized successfully` ) ;
322372 } ) ;
@@ -328,7 +378,14 @@ async function startServer() {
328378
329379process . on ( 'SIGINT' , async ( ) => {
330380 console . log ( 'SIGINT received, shutting down gracefully...' ) ;
331- emailWorker . getEmailWorker ( ) . stop ( ) ;
381+ if ( eventIndexerInstance ) {
382+ try {
383+ await eventIndexerInstance . stop ( ) ;
384+ console . log ( 'Event Indexer stopped cleanly.' ) ;
385+ } catch ( err ) {
386+ console . error ( 'Error stopping event indexer:' , err . message ) ;
387+ }
388+ }
332389 await transactionQueue . stopProcessing ( ) ;
333390 await transactionProcessor . stop ( ) ;
334391 await transactionEvents . stopListening ( ) ;
0 commit comments