import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import rateLimit from 'express-rate-limit'; import dotenv from 'dotenv'; import { Pool } from 'pg'; import { createClient } from 'redis'; import { createServer } from 'http'; import { WebSocketServer } from 'ws'; import applicationRoutes from './routes/applications'; import deploymentRoutes from './routes/deployments'; import storageRoutes from './routes/storage'; import usersRoutes from './routes/users'; import databaseRoutes from './routes/database'; import apiKeysRoutes from './routes/apiKeys'; import publicApiRoutes from './routes/publicApi'; import smtpRoutes from './routes/smtp'; import rlsRoutes from './routes/rls'; import emailTemplatesRoutes from './routes/emailTemplates'; import { authenticateToken, optionalAuth } from './middleware/auth'; import { errorHandler } from './middleware/errorHandler'; import { logger } from './utils/logger'; import { setupWebSocket } from './utils/websocket'; import emailService from './services/emailService'; dotenv.config(); const app = express(); const server = createServer(app); const PORT = process.env.PORT || 3000; // Trust proxy for rate limiting app.set('trust proxy', 1); // Rate limiting (temporarily disabled for testing) // const limiter = rateLimit({ // windowMs: 15 * 60 * 1000, // 15 minutes // max: 1000, // limit each IP to 1000 requests per windowMs // message: 'Too many requests from this IP, please try again later.', // }); // Middleware app.use(helmet()); app.use(cors()); app.use(express.json()); // app.use(limiter); // WebSocket setup const wss = new WebSocketServer({ server }); setupWebSocket(wss); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok', service: 'api', timestamp: new Date().toISOString(), version: process.env.npm_package_version || '1.0.0' }); }); // Dashboard stats (authenticated) app.get('/dashboard/stats', authenticateToken, async (req, res) => { try { // Query real counts from database const usersResult = await pool.query('SELECT COUNT(*) FROM __sys_users'); const appsResult = await pool.query('SELECT COUNT(*) FROM __sys_applications'); const deploymentsResult = await pool.query('SELECT COUNT(*) FROM __sys_deployments'); const apiKeysResult = await pool.query('SELECT COUNT(*) FROM __sys_api_keys'); const stats = { total_users: parseInt(usersResult.rows[0].count), total_applications: parseInt(appsResult.rows[0].count), total_deployments: parseInt(deploymentsResult.rows[0].count), total_api_keys: parseInt(apiKeysResult.rows[0].count), }; res.json(stats); } catch (error) { logger.error('Error fetching dashboard stats:', error); res.status(500).json({ error: 'Failed to fetch dashboard stats' }); } }); // Recent activity from audit logs app.get('/dashboard/activity', authenticateToken, async (req, res) => { try { const limit = parseInt(req.query.limit as string) || 10; const result = await pool.query(` SELECT al.action, al.resource_type, al.resource_id, al.details, al.created_at, u.email as user_email FROM __sys_audit_logs al LEFT JOIN __sys_users u ON al.user_id = u.id ORDER BY al.created_at DESC LIMIT $1 `, [limit]); const activities = result.rows.map(row => ({ type: row.resource_type, action: row.action, message: `${row.action.charAt(0).toUpperCase() + row.action.slice(1)} ${row.resource_type}`, userEmail: row.user_email, time: row.created_at, details: row.details, })); res.json(activities); } catch (error) { logger.error('Error fetching recent activity:', error); res.status(500).json({ error: 'Failed to fetch recent activity' }); } }); // Authenticated dashboard routes (require JWT token) app.use('/users', authenticateToken, usersRoutes); app.use('/applications', authenticateToken, applicationRoutes); app.use('/deployments', authenticateToken, deploymentRoutes); app.use('/storage', authenticateToken, storageRoutes); app.use('/database', authenticateToken, databaseRoutes); app.use('/api-keys', authenticateToken, apiKeysRoutes); app.use('/smtp', authenticateToken, smtpRoutes); app.use('/email-templates', authenticateToken, emailTemplatesRoutes); app.use('/rls', authenticateToken, rlsRoutes); // Public API routes (require API key) app.use('/v1', publicApiRoutes); // Public routes (for deployed apps) app.use('/apps', optionalAuth, express.static('/apps')); // Error handling app.use(errorHandler); // Database connection export const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Redis connection export const redisClient = createClient({ url: process.env.REDIS_URL, }); redisClient.on('error', (err) => logger.error('Redis Client Error:', err)); redisClient.on('connect', () => logger.info('Redis Client Connected')); // Auth service client export const authClient = { verifyToken: async (token: string) => { try { const response = await fetch(`${process.env.AUTH_SERVICE_URL}/auth/me`, { headers: { 'Authorization': `Bearer ${token}`, }, }); if (response.ok) { return await response.json(); } return null; } catch (error) { logger.error('Auth service error:', error); return null; } }, }; async function startServer() { try { // Test database connection await pool.query('SELECT NOW()'); logger.info('Database connected successfully'); // Connect to Redis await redisClient.connect(); logger.info('Redis connected successfully'); server.listen(PORT, () => { logger.info(`API service running on port ${PORT}`); // Start email queue processor (check every minute) emailService.startQueueProcessor(60000); }); } catch (error) { logger.error('Failed to start server:', error); process.exit(1); } } startServer(); // Graceful shutdown process.on('SIGINT', async () => { logger.info('Shutting down gracefully...'); await pool.end(); await redisClient.quit(); server.close(); process.exit(0); });