# Estructura del Backend ## Árbol de Directorios ``` backend/ ├── src/ │ ├── index.ts # Entry point │ ├── config/ │ │ ├── database.ts # MySQL connection │ │ ├── redis.ts # Redis connection │ │ └── env.ts # Environment variables │ │ │ ├── api/ │ │ ├── app.ts # Express app setup │ │ ├── routes/ │ │ │ ├── index.ts │ │ │ ├── projects.ts # /api/projects │ │ │ ├── tasks.ts # /api/tasks │ │ │ ├── agents.ts # /api/agents │ │ │ ├── deployments.ts# /api/deployments │ │ │ └── health.ts # /api/health │ │ │ │ │ ├── middleware/ │ │ │ ├── auth.ts # JWT validation │ │ │ ├── error.ts # Error handler │ │ │ ├── logger.ts # Request logging │ │ │ └── validate.ts # Schema validation │ │ │ │ │ └── websocket/ │ │ ├── server.ts # Socket.io setup │ │ └── handlers.ts # WS event handlers │ │ │ ├── db/ │ │ ├── schema.ts # Drizzle schema │ │ ├── migrations/ # SQL migrations │ │ └── client.ts # DB client instance │ │ │ ├── services/ │ │ ├── mcp/ │ │ │ ├── server.ts # MCP server for agents │ │ │ ├── tools.ts # MCP tool definitions │ │ │ └── handlers.ts # Tool implementations │ │ │ │ │ ├── gitea/ │ │ │ ├── client.ts # Gitea API client │ │ │ ├── repos.ts # Repo operations │ │ │ ├── pulls.ts # PR operations │ │ │ └── webhooks.ts # Webhook handling │ │ │ │ │ ├── kubernetes/ │ │ │ ├── client.ts # K8s API client │ │ │ ├── namespaces.ts # Namespace management │ │ │ ├── deployments.ts# Deployment management │ │ │ ├── pods.ts # Pod operations │ │ │ └── ingress.ts # Ingress management │ │ │ │ │ ├── queue/ │ │ │ ├── task-queue.ts # Task queue │ │ │ ├── deploy-queue.ts# Deploy queue │ │ │ └── workers.ts # Queue workers │ │ │ │ │ └── cache/ │ │ ├── redis.ts # Redis operations │ │ └── strategies.ts # Caching strategies │ │ │ ├── models/ │ │ ├── Project.ts # Project model │ │ ├── Task.ts # Task model │ │ ├── Agent.ts # Agent model │ │ ├── TaskGroup.ts # TaskGroup model │ │ └── Deployment.ts # Deployment model │ │ │ ├── types/ │ │ ├── api.ts # API types │ │ ├── mcp.ts # MCP types │ │ ├── k8s.ts # K8s types │ │ └── common.ts # Common types │ │ │ └── utils/ │ ├── logger.ts # Winston logger │ ├── errors.ts # Custom errors │ ├── validators.ts # Validation helpers │ └── helpers.ts # General helpers │ ├── drizzle/ # Drizzle config │ ├── drizzle.config.ts │ └── migrations/ │ ├── tests/ │ ├── unit/ │ ├── integration/ │ └── e2e/ │ ├── scripts/ │ ├── seed.ts # Seed database │ ├── migrate.ts # Run migrations │ └── generate-types.ts # Generate types │ ├── .env.example ├── .eslintrc.json ├── .prettierrc ├── tsconfig.json ├── package.json └── README.md ``` ## Entry Point (index.ts) ```typescript import { startServer } from './api/app' import { connectDatabase } from './config/database' import { connectRedis } from './config/redis' import { startMCPServer } from './services/mcp/server' import { startQueueWorkers } from './services/queue/workers' import { logger } from './utils/logger' async function bootstrap() { try { // Connect to MySQL await connectDatabase() logger.info('✓ MySQL connected') // Connect to Redis await connectRedis() logger.info('✓ Redis connected') // Start MCP Server for agents await startMCPServer() logger.info('✓ MCP Server started') // Start BullMQ workers await startQueueWorkers() logger.info('✓ Queue workers started') // Start HTTP + WebSocket server await startServer() logger.info('✓ API Server started on port 3000') } catch (error) { logger.error('Failed to start server:', error) process.exit(1) } } bootstrap() ``` ## Express App Setup (api/app.ts) ```typescript import express from 'express' import cors from 'cors' import { createServer } from 'http' import { Server as SocketIOServer } from 'socket.io' import routes from './routes' import { errorHandler } from './middleware/error' import { requestLogger } from './middleware/logger' import { setupWebSocket } from './websocket/server' export async function startServer() { const app = express() const httpServer = createServer(app) const io = new SocketIOServer(httpServer, { cors: { origin: process.env.FRONTEND_URL } }) // Middleware app.use(cors()) app.use(express.json()) app.use(requestLogger) // Routes app.use('/api', routes) // Error handling app.use(errorHandler) // WebSocket setupWebSocket(io) // Start const port = process.env.PORT || 3000 httpServer.listen(port) return { app, httpServer, io } } ``` ## Configuración de Base de Datos ```typescript // config/database.ts import { drizzle } from 'drizzle-orm/mysql2' import mysql from 'mysql2/promise' import * as schema from '../db/schema' let connection: mysql.Connection let db: ReturnType export async function connectDatabase() { connection = await mysql.createConnection({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT || '3306'), user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, }) db = drizzle(connection, { schema, mode: 'default' }) return db } export function getDatabase() { if (!db) { throw new Error('Database not initialized') } return db } ``` ## Configuración de Redis ```typescript // config/redis.ts import Redis from 'ioredis' let redis: Redis export async function connectRedis() { redis = new Redis({ host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379'), password: process.env.REDIS_PASSWORD, retryStrategy: (times) => { const delay = Math.min(times * 50, 2000) return delay } }) await redis.ping() return redis } export function getRedis() { if (!redis) { throw new Error('Redis not initialized') } return redis } ``` ## Variables de Entorno ```bash # .env.example # Server NODE_ENV=development PORT=3000 FRONTEND_URL=http://localhost:5173 # Database DB_HOST=localhost DB_PORT=3306 DB_USER=root DB_PASSWORD=password DB_NAME=aiworker # Redis REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= # Gitea GITEA_URL=http://localhost:3001 GITEA_TOKEN=your-gitea-token GITEA_OWNER=aiworker # Kubernetes K8S_IN_CLUSTER=false K8S_CONFIG_PATH=~/.kube/config K8S_DEFAULT_NAMESPACE=aiworker # MCP Server MCP_SERVER_PORT=3100 MCP_AUTH_TOKEN=your-mcp-token # JWT JWT_SECRET=your-secret-key JWT_EXPIRES_IN=7d # Claude API ANTHROPIC_API_KEY=your-api-key ``` ## Scripts de Package.json ```json { "name": "aiworker-backend", "version": "1.0.0", "scripts": { "dev": "bun --watch src/index.ts", "build": "bun build src/index.ts --outdir dist --target node", "start": "bun dist/index.js", "db:generate": "drizzle-kit generate:mysql", "db:push": "drizzle-kit push:mysql", "db:migrate": "bun run scripts/migrate.ts", "db:seed": "bun run scripts/seed.ts", "test": "bun test", "test:watch": "bun test --watch", "lint": "eslint src/**/*.ts", "format": "prettier --write src/**/*.ts" }, "dependencies": { "express": "^4.19.0", "mysql2": "^3.11.0", "drizzle-orm": "^0.36.0", "ioredis": "^5.4.1", "bullmq": "^5.23.0", "socket.io": "^4.8.1", "@modelcontextprotocol/sdk": "^1.0.0", "@kubernetes/client-node": "^0.22.0", "axios": "^1.7.9", "zod": "^3.24.1", "winston": "^3.17.0", "jsonwebtoken": "^9.0.2", "cors": "^2.8.5", "dotenv": "^16.4.7" }, "devDependencies": { "@types/express": "^5.0.0", "@types/node": "^22.10.2", "drizzle-kit": "^0.31.0", "typescript": "^5.7.2", "prettier": "^3.4.2", "eslint": "^9.18.0" } } ``` ## Estructura de Rutas ```typescript // api/routes/index.ts import { Router } from 'express' import projectRoutes from './projects' import taskRoutes from './tasks' import agentRoutes from './agents' import deploymentRoutes from './deployments' import healthRoutes from './health' const router = Router() router.use('/projects', projectRoutes) router.use('/tasks', taskRoutes) router.use('/agents', agentRoutes) router.use('/deployments', deploymentRoutes) router.use('/health', healthRoutes) export default router ``` ## Middleware de Validación ```typescript // middleware/validate.ts import { Request, Response, NextFunction } from 'express' import { ZodSchema } from 'zod' export function validate(schema: ZodSchema) { return (req: Request, res: Response, next: NextFunction) => { try { schema.parse({ body: req.body, query: req.query, params: req.params, }) next() } catch (error) { res.status(400).json({ error: 'Validation error', details: error }) } } } ``` ## Logger Setup ```typescript // utils/logger.ts import winston from 'winston' export const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) }), new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }) ] }) ``` ## Manejo de Errores ```typescript // middleware/error.ts import { Request, Response, NextFunction } from 'express' import { logger } from '../utils/logger' export class AppError extends Error { statusCode: number isOperational: boolean constructor(message: string, statusCode: number) { super(message) this.statusCode = statusCode this.isOperational = true Error.captureStackTrace(this, this.constructor) } } export function errorHandler( err: Error | AppError, req: Request, res: Response, next: NextFunction ) { logger.error('Error:', err) if (err instanceof AppError) { return res.status(err.statusCode).json({ error: err.message }) } res.status(500).json({ error: 'Internal server error' }) } ``` ## Comandos Útiles ```bash # Desarrollo bun run dev # Generar migraciones bun run db:generate # Aplicar migraciones bun run db:migrate # Seed inicial bun run db:seed # Tests bun test # Build para producción bun run build # Producción bun run start ```