"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const express_1 = __importDefault(require("express")); const dotenv_1 = __importDefault(require("dotenv")); const cors_1 = __importDefault(require("cors")); const simpleAuth_1 = __importStar(require("./routes/simpleAuth")); const flightService_1 = __importDefault(require("./services/flightService")); const driverConflictService_1 = __importDefault(require("./services/driverConflictService")); const scheduleValidationService_1 = __importDefault(require("./services/scheduleValidationService")); const flightTrackingScheduler_1 = __importDefault(require("./services/flightTrackingScheduler")); const enhancedDataService_1 = __importDefault(require("./services/enhancedDataService")); const databaseService_1 = __importDefault(require("./services/databaseService")); const jwtKeyManager_1 = __importDefault(require("./services/jwtKeyManager")); // Initialize JWT Key Manager const errorHandler_1 = require("./middleware/errorHandler"); const logger_1 = require("./middleware/logger"); const validation_1 = require("./middleware/validation"); const schemas_1 = require("./types/schemas"); dotenv_1.default.config(); const app = (0, express_1.default)(); const port = process.env.PORT ? parseInt(process.env.PORT) : 3000; // Middleware app.use((0, cors_1.default)({ origin: [ process.env.FRONTEND_URL || 'http://localhost:5173', 'http://localhost:5173', 'http://localhost:3000', 'http://localhost', // Frontend Docker container (local testing) 'https://bsa.madeamess.online' // Production frontend domain (where users access the site) ], credentials: true })); app.use(express_1.default.json()); app.use(express_1.default.urlencoded({ extended: true })); // Add request logging app.use(logger_1.requestLogger); // Simple JWT-based authentication - no passport needed // Authentication routes app.use('/auth', simpleAuth_1.default); // Temporary admin bypass route (remove after setup) app.get('/admin-bypass', (req, res) => { res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:5173'}/admin?bypass=true`); }); // Serve static files from public directory app.use(express_1.default.static('public')); // Enhanced health check endpoint with authentication system status app.get('/api/health', (0, errorHandler_1.asyncHandler)(async (req, res) => { const timestamp = new Date().toISOString(); // Check JWT Key Manager status const jwtStatus = jwtKeyManager_1.default.getStatus(); // Check environment variables const envCheck = { google_client_id: !!process.env.GOOGLE_CLIENT_ID, google_client_secret: !!process.env.GOOGLE_CLIENT_SECRET, google_redirect_uri: !!process.env.GOOGLE_REDIRECT_URI, frontend_url: !!process.env.FRONTEND_URL, database_url: !!process.env.DATABASE_URL, admin_password: !!process.env.ADMIN_PASSWORD }; // Check database connectivity let databaseStatus = 'unknown'; let userCount = 0; try { userCount = await databaseService_1.default.getUserCount(); databaseStatus = 'connected'; } catch (dbError) { databaseStatus = 'disconnected'; console.error('Health check - Database error:', dbError); } // Overall system health const isHealthy = databaseStatus === 'connected' && jwtStatus.hasCurrentKey && envCheck.google_client_id && envCheck.google_client_secret; const healthData = { status: isHealthy ? 'OK' : 'DEGRADED', timestamp, version: '1.0.0', environment: process.env.NODE_ENV || 'development', services: { database: { status: databaseStatus, user_count: databaseStatus === 'connected' ? userCount : null }, authentication: { jwt_key_manager: jwtStatus, oauth_configured: envCheck.google_client_id && envCheck.google_client_secret, environment_variables: envCheck } }, uptime: process.uptime(), memory: process.memoryUsage() }; // Log health check for monitoring console.log(`🏥 Health Check [${timestamp}]:`, { status: healthData.status, database: databaseStatus, jwt_keys: jwtStatus.hasCurrentKey, oauth: envCheck.google_client_id && envCheck.google_client_secret }); res.status(isHealthy ? 200 : 503).json(healthData); })); // Data is now persisted using dataService - no more in-memory storage! // Admin password - MUST be set via environment variable in production const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'CHANGE_ME_ADMIN_PASSWORD'; // Initialize flight tracking scheduler const flightTracker = new flightTrackingScheduler_1.default(flightService_1.default); // VIP routes (protected) app.post('/api/vips', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), (0, validation_1.validate)(schemas_1.createVipSchema), (0, errorHandler_1.asyncHandler)(async (req, res) => { // Create a new VIP - data is already validated const { name, organization, department, // New: Office of Development or Admin transportMode, flightNumber, // Legacy single flight flights, // New: array of flights expectedArrival, needsAirportPickup, needsVenueTransport, notes } = req.body; const newVip = { id: Date.now().toString(), // Simple ID generation name, organization, department: department || 'Office of Development', // Default to Office of Development transportMode: transportMode || 'flight', // Support both legacy single flight and new multiple flights flightNumber: transportMode === 'flight' && !flights ? flightNumber : undefined, flights: transportMode === 'flight' && flights ? flights : undefined, expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, arrivalTime: transportMode === 'flight' ? undefined : expectedArrival, // Legacy field for flight arrivals needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, needsVenueTransport: needsVenueTransport !== false, // Default to true assignedDriverIds: [], notes: notes || '', schedule: [] }; const savedVip = await enhancedDataService_1.default.addVip(newVip); // Add flights to tracking scheduler if applicable if (savedVip.transportMode === 'flight' && savedVip.flights && savedVip.flights.length > 0) { flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); } res.status(201).json(savedVip); })); app.get('/api/vips', simpleAuth_1.requireAuth, async (req, res) => { try { // Fetch all VIPs const vips = await enhancedDataService_1.default.getVips(); res.json(vips); } catch (error) { res.status(500).json({ error: 'Failed to fetch VIPs' }); } }); app.put('/api/vips/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), (0, validation_1.validate)(schemas_1.updateVipSchema), (0, errorHandler_1.asyncHandler)(async (req, res) => { // Update a VIP - data is already validated const { id } = req.params; const { name, organization, department, // New: Office of Development or Admin transportMode, flightNumber, // Legacy single flight flights, // New: array of flights expectedArrival, needsAirportPickup, needsVenueTransport, notes } = req.body; const updatedVip = { name, organization, department: department || 'Office of Development', transportMode: transportMode || 'flight', // Support both legacy single flight and new multiple flights flights: transportMode === 'flight' && flights ? flights : undefined, expectedArrival: transportMode === 'self-driving' ? expectedArrival : undefined, needsAirportPickup: transportMode === 'flight' ? (needsAirportPickup !== false) : false, needsVenueTransport: needsVenueTransport !== false, notes: notes || '' }; const savedVip = await enhancedDataService_1.default.updateVip(id, updatedVip); if (!savedVip) { return res.status(404).json({ error: 'VIP not found' }); } // Update flight tracking if needed if (savedVip.transportMode === 'flight') { // Remove old flights flightTracker.removeVipFlights(id); // Add new flights if any if (savedVip.flights && savedVip.flights.length > 0) { flightTracker.addVipFlights(savedVip.id, savedVip.name, savedVip.flights); } } res.json(savedVip); })); app.delete('/api/vips/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { // Delete a VIP const { id } = req.params; try { const deletedVip = await enhancedDataService_1.default.deleteVip(id); if (!deletedVip) { return res.status(404).json({ error: 'VIP not found' }); } // Remove from flight tracking flightTracker.removeVipFlights(id); res.json({ message: 'VIP deleted successfully', vip: deletedVip }); } catch (error) { res.status(500).json({ error: 'Failed to delete VIP' }); } }); // Driver routes (protected) app.post('/api/drivers', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), (0, validation_1.validate)(schemas_1.createDriverSchema), (0, errorHandler_1.asyncHandler)(async (req, res) => { // Create a new driver - data is already validated const { name, phone, email, vehicleInfo, status } = req.body; const newDriver = { id: Date.now().toString(), name, phone, email, vehicleInfo, status: status || 'available', department: 'Office of Development', // Default to Office of Development currentLocation: { lat: 0, lng: 0 }, assignedVipIds: [] }; const savedDriver = await enhancedDataService_1.default.addDriver(newDriver); res.status(201).json(savedDriver); })); app.get('/api/drivers', simpleAuth_1.requireAuth, async (req, res) => { try { // Fetch all drivers const drivers = await enhancedDataService_1.default.getDrivers(); res.json(drivers); } catch (error) { res.status(500).json({ error: 'Failed to fetch drivers' }); } }); app.put('/api/drivers/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { // Update a driver const { id } = req.params; const { name, phone, currentLocation, department } = req.body; try { const updatedDriver = { name, phone, department: department || 'Office of Development', currentLocation: currentLocation || { lat: 0, lng: 0 } }; const savedDriver = await enhancedDataService_1.default.updateDriver(id, updatedDriver); if (!savedDriver) { return res.status(404).json({ error: 'Driver not found' }); } res.json(savedDriver); } catch (error) { res.status(500).json({ error: 'Failed to update driver' }); } }); app.delete('/api/drivers/:id', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { // Delete a driver const { id } = req.params; try { const deletedDriver = await enhancedDataService_1.default.deleteDriver(id); if (!deletedDriver) { return res.status(404).json({ error: 'Driver not found' }); } res.json({ message: 'Driver deleted successfully', driver: deletedDriver }); } catch (error) { res.status(500).json({ error: 'Failed to delete driver' }); } }); // Enhanced flight tracking routes with date specificity app.get('/api/flights/:flightNumber', async (req, res) => { try { const { flightNumber } = req.params; const { date, departureAirport, arrivalAirport } = req.query; // Default to today if no date provided const flightDate = date || new Date().toISOString().split('T')[0]; const flightData = await flightService_1.default.getFlightInfo({ flightNumber, date: flightDate, departureAirport: departureAirport, arrivalAirport: arrivalAirport }); if (flightData) { // Always return flight data for validation, even if date doesn't match res.json(flightData); } else { // Only return 404 if the flight number itself is invalid res.status(404).json({ error: 'Invalid flight number - this flight does not exist' }); } } catch (error) { res.status(500).json({ error: 'Failed to fetch flight data' }); } }); // Start periodic updates for a flight app.post('/api/flights/:flightNumber/track', async (req, res) => { try { const { flightNumber } = req.params; const { date, intervalMinutes = 5 } = req.body; if (!date) { return res.status(400).json({ error: 'Flight date is required' }); } flightService_1.default.startPeriodicUpdates({ flightNumber, date }, intervalMinutes); res.json({ message: `Started tracking ${flightNumber} on ${date}` }); } catch (error) { res.status(500).json({ error: 'Failed to start flight tracking' }); } }); // Stop periodic updates for a flight app.delete('/api/flights/:flightNumber/track', async (req, res) => { try { const { flightNumber } = req.params; const { date } = req.query; if (!date) { return res.status(400).json({ error: 'Flight date is required' }); } const key = `${flightNumber}_${date}`; flightService_1.default.stopPeriodicUpdates(key); res.json({ message: `Stopped tracking ${flightNumber} on ${date}` }); } catch (error) { res.status(500).json({ error: 'Failed to stop flight tracking' }); } }); app.post('/api/flights/batch', async (req, res) => { try { const { flights } = req.body; if (!Array.isArray(flights)) { return res.status(400).json({ error: 'flights must be an array of {flightNumber, date} objects' }); } // Validate flight objects for (const flight of flights) { if (!flight.flightNumber || !flight.date) { return res.status(400).json({ error: 'Each flight must have flightNumber and date' }); } } const flightData = await flightService_1.default.getMultipleFlights(flights); res.json(flightData); } catch (error) { res.status(500).json({ error: 'Failed to fetch flight data' }); } }); // Get flight tracking status app.get('/api/flights/tracking/status', (req, res) => { const status = flightTracker.getTrackingStatus(); res.json(status); }); // Schedule management routes (protected) app.get('/api/vips/:vipId/schedule', simpleAuth_1.requireAuth, async (req, res) => { const { vipId } = req.params; try { const vipSchedule = await enhancedDataService_1.default.getSchedule(vipId); res.json(vipSchedule); } catch (error) { res.status(500).json({ error: 'Failed to fetch schedule' }); } }); app.post('/api/vips/:vipId/schedule', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { const { vipId } = req.params; const { title, location, startTime, endTime, description, type, assignedDriverId } = req.body; // Validate the event const validationErrors = scheduleValidationService_1.default.validateEvent({ title: title || '', location: location || '', startTime: startTime || '', endTime: endTime || '', type: type || '' }, false); const { critical, warnings } = scheduleValidationService_1.default.categorizeErrors(validationErrors); // Return validation errors if any critical errors exist if (critical.length > 0) { return res.status(400).json({ error: 'Validation failed', validationErrors: critical, warnings: warnings, message: scheduleValidationService_1.default.getErrorSummary(critical) }); } const newEvent = { id: Date.now().toString(), title, location, startTime, endTime, description: description || '', assignedDriverId: assignedDriverId || '', status: 'scheduled', type }; try { const savedEvent = await enhancedDataService_1.default.addScheduleEvent(vipId, newEvent); // Include warnings in the response if any const response = { ...savedEvent }; if (warnings.length > 0) { response.warnings = warnings; } res.status(201).json(response); } catch (error) { res.status(500).json({ error: 'Failed to create schedule event' }); } }); app.put('/api/vips/:vipId/schedule/:eventId', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { const { vipId, eventId } = req.params; const { title, location, startTime, endTime, description, type, assignedDriverId, status } = req.body; // Validate the updated event (with edit flag for grace period) const validationErrors = scheduleValidationService_1.default.validateEvent({ title: title || '', location: location || '', startTime: startTime || '', endTime: endTime || '', type: type || '' }, true); const { critical, warnings } = scheduleValidationService_1.default.categorizeErrors(validationErrors); // Return validation errors if any critical errors exist if (critical.length > 0) { return res.status(400).json({ error: 'Validation failed', validationErrors: critical, warnings: warnings, message: scheduleValidationService_1.default.getErrorSummary(critical) }); } const updatedEvent = { id: eventId, title, location, startTime, endTime, description: description || '', assignedDriverId: assignedDriverId || '', type, status: status || 'scheduled' }; try { const savedEvent = await enhancedDataService_1.default.updateScheduleEvent(vipId, eventId, updatedEvent); if (!savedEvent) { return res.status(404).json({ error: 'Event not found' }); } // Include warnings in the response if any const response = { ...savedEvent }; if (warnings.length > 0) { response.warnings = warnings; } res.json(response); } catch (error) { res.status(500).json({ error: 'Failed to update schedule event' }); } }); app.patch('/api/vips/:vipId/schedule/:eventId/status', simpleAuth_1.requireAuth, async (req, res) => { const { vipId, eventId } = req.params; const { status } = req.body; try { const currentSchedule = await enhancedDataService_1.default.getSchedule(vipId); const currentEvent = currentSchedule.find((event) => event.id === eventId); if (!currentEvent) { return res.status(404).json({ error: 'Event not found' }); } const updatedEvent = { ...currentEvent, status }; const savedEvent = await enhancedDataService_1.default.updateScheduleEvent(vipId, eventId, updatedEvent); if (!savedEvent) { return res.status(404).json({ error: 'Event not found' }); } res.json(savedEvent); } catch (error) { res.status(500).json({ error: 'Failed to update event status' }); } }); app.delete('/api/vips/:vipId/schedule/:eventId', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['coordinator', 'administrator']), async (req, res) => { const { vipId, eventId } = req.params; try { const deletedEvent = await enhancedDataService_1.default.deleteScheduleEvent(vipId, eventId); if (!deletedEvent) { return res.status(404).json({ error: 'Event not found' }); } res.json({ message: 'Event deleted successfully', event: deletedEvent }); } catch (error) { res.status(500).json({ error: 'Failed to delete schedule event' }); } }); // Driver availability and conflict checking (protected) app.post('/api/drivers/availability', simpleAuth_1.requireAuth, async (req, res) => { const { startTime, endTime, location } = req.body; if (!startTime || !endTime) { return res.status(400).json({ error: 'startTime and endTime are required' }); } try { const allSchedules = await enhancedDataService_1.default.getAllSchedules(); const drivers = await enhancedDataService_1.default.getDrivers(); const availability = driverConflictService_1.default.getDriverAvailability({ startTime, endTime, location: location || '' }, allSchedules, drivers); res.json(availability); } catch (error) { res.status(500).json({ error: 'Failed to check driver availability' }); } }); // Check conflicts for specific driver assignment (protected) app.post('/api/drivers/:driverId/conflicts', simpleAuth_1.requireAuth, async (req, res) => { const { driverId } = req.params; const { startTime, endTime, location } = req.body; if (!startTime || !endTime) { return res.status(400).json({ error: 'startTime and endTime are required' }); } try { const allSchedules = await enhancedDataService_1.default.getAllSchedules(); const drivers = await enhancedDataService_1.default.getDrivers(); const conflicts = driverConflictService_1.default.checkDriverConflicts(driverId, { startTime, endTime, location: location || '' }, allSchedules, drivers); res.json({ conflicts }); } catch (error) { res.status(500).json({ error: 'Failed to check driver conflicts' }); } }); // Get driver's complete schedule (protected) app.get('/api/drivers/:driverId/schedule', simpleAuth_1.requireAuth, async (req, res) => { const { driverId } = req.params; try { const drivers = await enhancedDataService_1.default.getDrivers(); const driver = drivers.find((d) => d.id === driverId); if (!driver) { return res.status(404).json({ error: 'Driver not found' }); } // Get all events assigned to this driver across all VIPs const driverSchedule = []; const allSchedules = await enhancedDataService_1.default.getAllSchedules(); const vips = await enhancedDataService_1.default.getVips(); Object.entries(allSchedules).forEach(([vipId, events]) => { events.forEach((event) => { if (event.assignedDriverId === driverId) { // Get VIP name const vip = vips.find((v) => v.id === vipId); driverSchedule.push({ ...event, vipId, vipName: vip ? vip.name : 'Unknown VIP' }); } }); }); // Sort by start time driverSchedule.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime()); res.json({ driver: { id: driver.id, name: driver.name, phone: driver.phone, department: driver.department }, schedule: driverSchedule }); } catch (error) { res.status(500).json({ error: 'Failed to fetch driver schedule' }); } }); // Admin routes app.post('/api/admin/authenticate', (req, res) => { const { password } = req.body; if (password === ADMIN_PASSWORD) { res.json({ success: true }); } else { res.status(401).json({ error: 'Invalid password' }); } }); app.get('/api/admin/settings', async (req, res) => { const adminAuth = req.headers['admin-auth']; if (adminAuth !== 'true') { return res.status(401).json({ error: 'Unauthorized' }); } try { const adminSettings = await enhancedDataService_1.default.getAdminSettings(); // Return settings but mask API keys for display only // IMPORTANT: Don't return the actual keys, just indicate they exist const maskedSettings = { apiKeys: { aviationStackKey: adminSettings.apiKeys.aviationStackKey ? '***' + adminSettings.apiKeys.aviationStackKey.slice(-4) : '', googleMapsKey: adminSettings.apiKeys.googleMapsKey ? '***' + adminSettings.apiKeys.googleMapsKey.slice(-4) : '', twilioKey: adminSettings.apiKeys.twilioKey ? '***' + adminSettings.apiKeys.twilioKey.slice(-4) : '', googleClientId: adminSettings.apiKeys.googleClientId ? '***' + adminSettings.apiKeys.googleClientId.slice(-4) : '', googleClientSecret: adminSettings.apiKeys.googleClientSecret ? '***' + adminSettings.apiKeys.googleClientSecret.slice(-4) : '' }, systemSettings: adminSettings.systemSettings }; res.json(maskedSettings); } catch (error) { res.status(500).json({ error: 'Failed to fetch admin settings' }); } }); app.post('/api/admin/settings', async (req, res) => { const adminAuth = req.headers['admin-auth']; if (adminAuth !== 'true') { return res.status(401).json({ error: 'Unauthorized' }); } try { const { apiKeys, systemSettings } = req.body; const currentSettings = await enhancedDataService_1.default.getAdminSettings(); // Update API keys (only if provided and not masked) if (apiKeys) { if (apiKeys.aviationStackKey && !apiKeys.aviationStackKey.startsWith('***')) { currentSettings.apiKeys.aviationStackKey = apiKeys.aviationStackKey; // Update the environment variable for the flight service process.env.AVIATIONSTACK_API_KEY = apiKeys.aviationStackKey; } if (apiKeys.googleMapsKey && !apiKeys.googleMapsKey.startsWith('***')) { currentSettings.apiKeys.googleMapsKey = apiKeys.googleMapsKey; } if (apiKeys.twilioKey && !apiKeys.twilioKey.startsWith('***')) { currentSettings.apiKeys.twilioKey = apiKeys.twilioKey; } if (apiKeys.googleClientId && !apiKeys.googleClientId.startsWith('***')) { currentSettings.apiKeys.googleClientId = apiKeys.googleClientId; // Update the environment variable for Google OAuth process.env.GOOGLE_CLIENT_ID = apiKeys.googleClientId; } if (apiKeys.googleClientSecret && !apiKeys.googleClientSecret.startsWith('***')) { currentSettings.apiKeys.googleClientSecret = apiKeys.googleClientSecret; // Update the environment variable for Google OAuth process.env.GOOGLE_CLIENT_SECRET = apiKeys.googleClientSecret; } } // Update system settings if (systemSettings) { currentSettings.systemSettings = { ...currentSettings.systemSettings, ...systemSettings }; } // Save the updated settings await enhancedDataService_1.default.updateAdminSettings(currentSettings); res.json({ success: true }); } catch (error) { res.status(500).json({ error: 'Failed to update admin settings' }); } }); app.post('/api/admin/test-api/:apiType', async (req, res) => { const adminAuth = req.headers['admin-auth']; if (adminAuth !== 'true') { return res.status(401).json({ error: 'Unauthorized' }); } const { apiType } = req.params; const { apiKey } = req.body; try { switch (apiType) { case 'aviationStackKey': // Test AviationStack API const testUrl = `http://api.aviationstack.com/v1/flights?access_key=${apiKey}&limit=1`; const response = await fetch(testUrl); if (response.ok) { const data = await response.json(); if (data.error) { res.status(400).json({ error: data.error.message || 'Invalid API key' }); } else { res.json({ success: true, message: 'API key is valid!' }); } } else { res.status(400).json({ error: 'Failed to validate API key' }); } break; case 'googleMapsKey': res.json({ success: true, message: 'Google Maps API testing not yet implemented' }); break; case 'twilioKey': res.json({ success: true, message: 'Twilio API testing not yet implemented' }); break; default: res.status(400).json({ error: 'Unknown API type' }); } } catch (error) { res.status(500).json({ error: 'Failed to test API connection' }); } }); // JWT Key Management endpoints (admin only) app.get('/api/admin/jwt-status', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['administrator']), (req, res) => { const jwtKeyManager = require('./services/jwtKeyManager').default; const status = jwtKeyManager.getStatus(); res.json({ keyRotationEnabled: true, rotationInterval: '24 hours', gracePeriod: '24 hours', ...status, message: 'JWT keys are automatically rotated every 24 hours for enhanced security' }); }); app.post('/api/admin/jwt-rotate', simpleAuth_1.requireAuth, (0, simpleAuth_1.requireRole)(['administrator']), (req, res) => { const jwtKeyManager = require('./services/jwtKeyManager').default; try { jwtKeyManager.forceRotation(); res.json({ success: true, message: 'JWT key rotation triggered successfully. New tokens will use the new key.' }); } catch (error) { res.status(500).json({ error: 'Failed to rotate JWT keys' }); } }); // Initialize database and start server // Add 404 handler for undefined routes app.use(errorHandler_1.notFoundHandler); // Add error logging middleware app.use(logger_1.errorLogger); // Add global error handler (must be last!) app.use(errorHandler_1.errorHandler); async function startServer() { try { // Initialize database schema and migrate data await databaseService_1.default.initializeDatabase(); console.log('✅ Database initialization completed'); // Start the server app.listen(port, () => { console.log(`🚀 Server is running on port ${port}`); console.log(`🔐 Admin password: ${ADMIN_PASSWORD}`); console.log(`📊 Admin dashboard: http://localhost:${port === 3000 ? 5173 : port}/admin`); console.log(`🏥 Health check: http://localhost:${port}/api/health`); console.log(`📚 API docs: http://localhost:${port}/api-docs.html`); }); } catch (error) { console.error('❌ Failed to start server:', error); process.exit(1); } } startServer(); //# sourceMappingURL=index.original.js.map