claude-flow-novice
Version:
Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.
418 lines (417 loc) • 15 kB
JavaScript
/**
* Backend Server Application
*
* Production-ready Express server with comprehensive API endpoints,
* middleware, error handling, and monitoring capabilities.
*
* Features:
* - RESTful API endpoints for task and agent management
* - Real-time progress tracking via WebSocket
* - Comprehensive health checks
* - Authentication and authorization middleware
* - Rate limiting and request validation
* - Structured logging and error handling
* - Database integration with connection pooling
* - Redis coordination for CFN Loop workflows
*/ import express from 'express';
import http from 'http';
import { Server as SocketIOServer } from 'socket.io';
import cors from 'cors';
import helmet from 'helmet';
import compression from 'compression';
import morgan from 'morgan';
import { v4 as uuidv4 } from 'uuid';
// Import API routes
import { createHealthEndpoints } from '../api/health-endpoints.js';
import { createTaskEndpoints } from '../api/task-endpoints.js';
import { createProgressEndpoints } from '../api/progress-endpoints.js';
import { createAgentEndpoints } from '../api/agent-endpoints.js';
// Import middleware
import { authenticationMiddleware } from '../middleware/authentication.js';
import { authorizationMiddleware } from '../middleware/authorization.js';
import { requestValidationMiddleware } from '../middleware/request-validation.js';
import { errorHandlingMiddleware } from '../middleware/error-handling.js';
import { loggingMiddleware } from '../middleware/logging.js';
import { rateLimitingMiddleware } from '../middleware/rate-limiting.js';
// Import services
import { getDatabaseService } from '../../lib/database-service.js';
import { RedisQueueManager } from '../../lib/redis-queue-manager.js';
import { WebSocketManager } from './websocket-manager.js';
import { TaskManager } from './task-manager.js';
import { AgentManager } from './agent-manager.js';
import { logger } from '../utils/logger.js';
/**
* Backend server class that encapsulates all server functionality
*/ export class BackendServer {
app;
server;
io;
config;
wsManager;
taskManager;
agentManager;
redisManager = null;
constructor(config){
// Initialize configuration
this.config = this.initializeConfig(config);
// Initialize Express app
this.app = express();
this.server = http.createServer(this.app);
// Initialize Socket.IO
this.io = new SocketIOServer(this.server, {
cors: this.config.socketIO.cors,
transports: [
'websocket',
'polling'
]
});
// Initialize managers
this.wsManager = new WebSocketManager(this.io);
this.taskManager = new TaskManager(this.wsManager);
this.agentManager = new AgentManager(this.wsManager);
// Initialize Redis manager
this.initializeRedisManager();
// Setup server
this.setupMiddleware();
this.setupRoutes();
this.setupErrorHandling();
this.setupWebSocket();
}
/**
* Initialize server configuration with defaults
*/ initializeConfig(config) {
const defaultConfig = {
port: parseInt(process.env.PORT || '3001', 10),
host: process.env.HOST || '0.0.0.0',
env: process.env.NODE_ENV || 'development',
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
database: process.env.DB_NAME || 'cfn_loop',
username: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || '',
ssl: process.env.DB_SSL === 'true',
maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS || '20', 10)
},
redis: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379', 10),
password: process.env.REDIS_PASSWORD || undefined,
db: parseInt(process.env.REDIS_DB || '0', 10)
},
auth: {
jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
jwtExpiration: process.env.JWT_EXPIRATION || '24h',
bcryptRounds: parseInt(process.env.BCRYPT_ROUNDS || '12', 10)
},
rateLimit: {
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000', 10),
max: parseInt(process.env.RATE_LIMIT_MAX || '100', 10),
message: 'Too many requests from this IP'
},
socketIO: {
cors: {
origin: process.env.CORS_ORIGIN || '*',
methods: [
'GET',
'POST'
],
credentials: true
}
}
};
return {
...defaultConfig,
...config
};
}
/**
* Initialize Redis manager for coordination
*/ initializeRedisManager() {
try {
this.redisManager = new RedisQueueManager();
logger.info('Redis manager initialized successfully');
} catch (error) {
logger.warn('Redis manager initialization failed:', error);
// Continue without Redis - some features may be limited
}
}
/**
* Setup all middleware
*/ setupMiddleware() {
// Security middleware
this.app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [
"'self'"
],
styleSrc: [
"'self'",
"'unsafe-inline'"
],
scriptSrc: [
"'self'"
],
imgSrc: [
"'self'",
"data:",
"https:"
]
}
}
}));
// CORS middleware
this.app.use(cors({
origin: this.config.env === 'production' ? process.env.ALLOWED_ORIGINS?.split(',') || [] : '*',
credentials: true,
methods: [
'GET',
'POST',
'PUT',
'DELETE',
'PATCH',
'OPTIONS'
],
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With'
]
}));
// Compression middleware
this.app.use(compression());
// Body parsing middleware
this.app.use(express.json({
limit: '10mb'
}));
this.app.use(express.urlencoded({
extended: true,
limit: '10mb'
}));
// Logging middleware
this.app.use(morgan('combined', {
stream: {
write: (message)=>{
logger.info(message.trim());
}
}
}));
// Custom logging middleware
this.app.use(loggingMiddleware);
// Rate limiting
this.app.use(rateLimitingMiddleware(this.config.rateLimit));
// Request ID middleware
this.app.use((req, res, next)=>{
req.headers['x-request-id'] = req.headers['x-request-id'] || uuidv4();
res.setHeader('X-Request-ID', req.headers['x-request-id']);
next();
});
// Request validation middleware
this.app.use(requestValidationMiddleware);
// Authentication middleware (optional for public endpoints)
this.app.use('/api', authenticationMiddleware(this.config.auth));
}
/**
* Setup API routes
*/ setupRoutes() {
// Health check endpoints (public)
const healthEndpoints = createHealthEndpoints();
this.app.use('/health', healthEndpoints.getRouter());
// API versioning
const apiRouter = express.Router();
// Task management endpoints
const taskEndpoints = createTaskEndpoints(this.taskManager);
apiRouter.use('/tasks', taskEndpoints.getRouter());
// Progress tracking endpoints
const progressEndpoints = createProgressEndpoints(this.taskManager);
apiRouter.use('/progress', progressEndpoints.getRouter());
// Agent management endpoints
const agentEndpoints = createAgentEndpoints(this.agentManager);
apiRouter.use('/agents', agentEndpoints.getRouter());
// Apply authorization middleware to protected routes
apiRouter.use('/tasks', authorizationMiddleware([
'user',
'admin'
]));
apiRouter.use('/agents', authorizationMiddleware([
'admin'
]));
// Mount API routes
this.app.use('/api/v1', apiRouter);
// API documentation endpoint
this.app.get('/api', (req, res)=>{
res.json({
name: 'CFN Loop Backend API',
version: '1.0.0',
description: 'Backend API for CFN Loop agent orchestration system',
endpoints: {
health: '/health',
tasks: '/api/v1/tasks',
progress: '/api/v1/progress',
agents: '/api/v1/agents'
},
documentation: '/api/docs',
version: '1.0.0'
});
});
// Root endpoint
this.app.get('/', (req, res)=>{
res.json({
name: 'CFN Loop Backend Server',
version: '1.0.0',
status: 'running',
timestamp: new Date().toISOString(),
environment: this.config.env
});
});
}
/**
* Setup error handling middleware
*/ setupErrorHandling() {
// 404 handler
this.app.use('*', (req, res)=>{
res.status(404).json({
error: 'Not Found',
message: `Endpoint ${req.method} ${req.originalUrl} not found`,
timestamp: new Date().toISOString()
});
});
// Global error handler
this.app.use(errorHandlingMiddleware);
}
/**
* Setup WebSocket handlers
*/ setupWebSocket() {
this.io.on('connection', (socket)=>{
logger.info(`WebSocket client connected: ${socket.id}`);
// Handle task subscriptions
socket.on('subscribe-task', (taskId)=>{
this.wsManager.subscribeToTask(socket, taskId);
});
socket.on('unsubscribe-task', (taskId)=>{
this.wsManager.unsubscribeFromTask(socket, taskId);
});
// Handle agent subscriptions
socket.on('subscribe-agent', (agentId)=>{
this.wsManager.subscribeToAgent(socket, agentId);
});
socket.on('unsubscribe-agent', (agentId)=>{
this.wsManager.unsubscribeFromAgent(socket, agentId);
});
// Handle disconnection
socket.on('disconnect', (reason)=>{
logger.info(`WebSocket client disconnected: ${socket.id}, reason: ${reason}`);
this.wsManager.handleDisconnection(socket);
});
// Handle errors
socket.on('error', (error)=>{
logger.error(`WebSocket error for client ${socket.id}:`, error);
});
});
}
/**
* Start the server
*/ async start() {
try {
// Test database connection
const db = getDatabaseService();
await db.query('SELECT 1');
logger.info('Database connection verified');
// Start HTTP server
this.server.listen(this.config.port, this.config.host, ()=>{
logger.info(`Backend server started on ${this.config.host}:${this.config.port}`);
logger.info(`Environment: ${this.config.env}`);
logger.info(`API endpoints: http://${this.config.host}:${this.config.port}/api/v1`);
logger.info(`Health checks: http://${this.config.host}:${this.config.port}/health`);
logger.info(`WebSocket server ready`);
});
// Handle server errors
this.server.on('error', (error)=>{
if (error.code === 'EADDRINUSE') {
logger.error(`Port ${this.config.port} is already in use`);
} else {
logger.error('Server error:', error);
}
throw error;
});
} catch (error) {
logger.error('Failed to start server:', error);
throw error;
}
}
/**
* Graceful shutdown
*/ async stop() {
logger.info('Starting graceful shutdown...');
// Stop accepting new connections
this.server.close(async (err)=>{
if (err) {
logger.error('Error during server shutdown:', err);
} else {
logger.info('HTTP server closed');
}
// Close WebSocket connections
this.io.close(()=>{
logger.info('WebSocket server closed');
});
// Close Redis connections
if (this.redisManager) {
try {
await this.redisManager.disconnect();
logger.info('Redis connections closed');
} catch (error) {
logger.error('Error closing Redis connections:', error);
}
}
// Close database connections
try {
const db = getDatabaseService();
await db.close();
logger.info('Database connections closed');
} catch (error) {
logger.error('Error closing database connections:', error);
}
logger.info('Graceful shutdown completed');
});
}
/**
* Get server instance
*/ getServer() {
return this.server;
}
/**
* Get Express app instance
*/ getApp() {
return this.app;
}
/**
* Get Socket.IO instance
*/ getIO() {
return this.io;
}
/**
* Get configuration
*/ getConfig() {
return this.config;
}
/**
* Get managers
*/ getManagers() {
return {
taskManager: this.taskManager,
agentManager: this.agentManager,
wsManager: this.wsManager,
redisManager: this.redisManager
};
}
}
/**
* Create and configure a backend server instance
*/ export function createBackendServer(config) {
return new BackendServer(config);
}
/**
* Default export for backward compatibility
*/ export default BackendServer;
//# sourceMappingURL=server.js.map