# 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<typeof drizzle>

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
```
