Complete documentation for future sessions

- CLAUDE.md for AI agents to understand the codebase
- GITEA-GUIDE.md centralizes all Gitea operations (API, Registry, Auth)
- DEVELOPMENT-WORKFLOW.md explains complete dev process
- ROADMAP.md, NEXT-SESSION.md for planning
- QUICK-REFERENCE.md, TROUBLESHOOTING.md for daily use
- 40+ detailed docs in /docs folder
- Backend as submodule from Gitea

Everything documented for autonomous operation.

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hector Ros
2026-01-20 00:36:53 +01:00
commit db71705842
49 changed files with 19162 additions and 0 deletions

View File

@@ -0,0 +1,480 @@
# 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
```