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:
480
docs/02-backend/estructura.md
Normal file
480
docs/02-backend/estructura.md
Normal 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
|
||||
```
|
||||
Reference in New Issue
Block a user