import express from 'express' import cors from 'cors' import helmet from 'helmet' import Redis from 'ioredis' import { v4 as uuidv4 } from 'uuid' const app = express() app.use(cors()) app.use(helmet()) app.use(express.json()) // ─── Redis ────────────────────────────────────────────────────────── const redis = new Redis({ host: 'redis', port: 6379, lazyConnect: true }) redis.on('error', (err) => console.error('[Redis] error:', err.message)) redis.on('ready', () => console.log('[Redis] conectado')) // ─── Task Queue ───────────────────────────────────────────────────── const TASK_QUEUE = 'dev-tasks' const LOG_CHANNEL = 'dev-logs' function log(level, msg) { const line = JSON.stringify({ ts: new Date().toISOString(), level, msg }) redis.publish(LOG_CHANNEL, line).catch(() => {}) console.log(`[${level}] ${msg}`) } // GET /tasks — lista todas // POST /tasks — cria tarefa // PUT /tasks/:id — atualiza status // GET /agents — lista agentes conectados // GET /logs/:limit — logs recentes app.get('/tasks', async (_req, res) => { const keys = await redis.keys('task:*') const tasks = [] for (const k of keys) { const t = await redis.get(k) if (t) tasks.push(JSON.parse(t)) } tasks.sort((a, b) => b.updated_at?.localeCompare(a.updated_at) || 0) res.json(tasks) }) app.post('/tasks', async (req, res) => { const id = uuidv4() const task = { id, title: req.body.title, type: req.body.type || 'feature', priority: req.body.priority || 'medium', status: 'todo', domain: req.body.domain || 'fullstack', assignee: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), } await redis.set(`task:${id}`, JSON.stringify(task)) await redis.rpush('task-queue', id) log('TASK', `Nova tarefa: "${task.title}" [${task.type}]`) res.status(201).json(task) }) app.put('/tasks/:id', async (req, res) => { const key = `task:${req.params.id}` const raw = await redis.get(key) if (!raw) return res.status(404).json({ error: 'Não encontrada' }) const task = JSON.parse(raw) Object.assign(task, req.body, { updated_at: new Date().toISOString() }) await redis.set(key, JSON.stringify(task)) log('INFO', `Tarefa atualizada: "${task.title}" status=${task.status}`) res.json(task) }) app.get('/agents', async (_req, res) => { const keys = await redis.keys('agent:*') const agents = [] for (const k of keys) { const a = await redis.get(k) if (a) agents.push(JSON.parse(a)) } res.json(agents) }) app.get('/health', (_req, res) => { res.json({ status: 'ok', ts: new Date().toISOString() }) }) // Promover tarefa automaticamente ao criar/modificar app.use('*', (req, res) => { res.status(404).json({ error: 'Not found' }) }) const PORT = 3001 app.listen(PORT, '0.0.0.0', () => { // Registrar agente backend ;(async () => { try { await redis.connect() } catch (e) { console.error('Redis connect:', e.message) } await redis.set('agent:dev-backend', JSON.stringify({ id: 'dev-backend', role: 'backend', status: 'online', started_at: new Date().toISOString(), })) log('AGENT', `Backend API online :${PORT}`) log('AGENT', `Redis conectado — queue: ${TASK_QUEUE}`) })() })