docs(dev): taskboard + backend API + agent worker + Obsidian vault index
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
# 🚀 Ambiente de Desenvolvimento Full-Stack — Docker Swarm
|
||||
|
||||
## Arquitetura
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DEV ORCHESTRATOR │
|
||||
│ (gerencia agentes, tasks, logs, hot reload) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ FRONTEND │ │ BACKEND │ │ WORKERS │ │
|
||||
│ │ (Vite+React)│ │ Node+TS) │ │ (Agentes paralelos) │ │
|
||||
│ │ HMR: 5173 │ │ HMR: 3001 │ │ Python/Node/Go │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
│ ↘ ↘ ↘ │
|
||||
│ └───────────────┴────────── Redis Queue ───────────┘│
|
||||
│ TaskBoard (live) │
|
||||
│ LogStream (live) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Stacks
|
||||
|
||||
| Stack | Propósito |
|
||||
|-------|-----------|
|
||||
| `dev` | Ambiente completo (frontend + backend + workers + Redis + taskboard + logs) |
|
||||
| `dev-frontend` | Apenas Vite HMR (para desenvolvimento isolado) |
|
||||
| `dev-backend` | Apenas API + workers |
|
||||
|
||||
## Hot Reload
|
||||
|
||||
### Frontend (React + Vite)
|
||||
```yaml
|
||||
services:
|
||||
frontend:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./frontend:/app:cached # código montado
|
||||
- /app/node_modules # cache node_modules
|
||||
command: sh -c "npm install && npm run dev -- --host 0.0.0.0"
|
||||
networks: [public]
|
||||
deploy:
|
||||
replicas: 1
|
||||
endpoint_mode: dnsrr
|
||||
```
|
||||
|
||||
### Backend (Node + ts-node)
|
||||
```yaml
|
||||
services:
|
||||
backend:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./backend:/app:cached
|
||||
- /app/node_modules
|
||||
command: sh -c "npm install && npx tsx watch src/server.ts"
|
||||
networks: [public]
|
||||
```
|
||||
|
||||
### Python Worker (uvicorn + watcher)
|
||||
```yaml
|
||||
services:
|
||||
worker-py:
|
||||
image: python:3.12-slim
|
||||
volumes:
|
||||
- ./workers/python:/app:cached
|
||||
- /app/.venv
|
||||
command: sh -c "pip install -r requirements.txt && watchmedo shell-command --patterns='*.py' --recursive --command='echo CHANGED'"
|
||||
networks: [public]
|
||||
```
|
||||
|
||||
## Logs em Tempo Real
|
||||
|
||||
### Opção 1 — Loki + Promtail (filebeat alternativa)
|
||||
```yaml
|
||||
services:
|
||||
promtail:
|
||||
image: grafana/promtail:latest
|
||||
volumes:
|
||||
- /var/log:/var/log:ro
|
||||
- ./promtail-config.yml:/etc/promtail/config.yml
|
||||
command: -config.file=/etc/promtail/config.yml
|
||||
```
|
||||
|
||||
### Opção 2 — Direto no Dev Orchestrator (stdout aggregator)
|
||||
```yaml
|
||||
services:
|
||||
logstream:
|
||||
image: node:24-alpine
|
||||
command: node -e "
|
||||
const {Redis} = require('ioredis');
|
||||
const r = new Redis(process.env.REDIS_URL);
|
||||
r.subscribe('dev-logs', (err) => {
|
||||
if(err) return;
|
||||
r.on('message', (ch, msg) => process.stdout.write(msg));
|
||||
});
|
||||
"
|
||||
depends_on: [{service: redis, condition: service_healthy}]
|
||||
```
|
||||
|
||||
## Task Queue + Dev Board
|
||||
|
||||
```yaml
|
||||
# Redis Queue
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
command: redis-server --appendonly yes --save 5 1
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# Task Board Web
|
||||
taskboard:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./taskboard:/app
|
||||
command: sh -c "npm install && npm run dev -- --host 0.0.0.0"
|
||||
ports:
|
||||
- target: 5174
|
||||
published: 3082
|
||||
protocol: tcp
|
||||
mode: host
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
networks: [public]
|
||||
labels:
|
||||
- caddy=board.octal.tec.br
|
||||
- caddy.reverse_proxy={{upstreams 5174}}
|
||||
|
||||
# Worker Agents (paralelos)
|
||||
worker-a: # Frontend specialist
|
||||
image: node:24-alpine
|
||||
command: node /app/workers/agent-a.js
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- AGENT_ROLE=frontend
|
||||
- TASK_QUEUE=dev-tasks
|
||||
depends_on: [{redis, condition: service_healthy}]
|
||||
|
||||
worker-b: # Backend specialist
|
||||
image: node:24-alpine
|
||||
command: node /app/workers/agent-b.js
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- AGENT_ROLE=backend
|
||||
- TASK_QUEUE=dev-tasks
|
||||
|
||||
worker-c: # DevOps / infra specialist
|
||||
image: python:3.12-slim
|
||||
command: python /app/workers/agent-c.py
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- AGENT_ROLE=devops
|
||||
- TASK_QUEUE=dev-tasks
|
||||
```
|
||||
|
||||
## Task Protocol (Redis)
|
||||
|
||||
```json
|
||||
// Tarefa enfileirada
|
||||
{
|
||||
"id": "uuid",
|
||||
"type": "feature|bug|refactor|test",
|
||||
"priority": "low|medium|high|critical",
|
||||
"domain": "frontend|backend|devops|fullstack",
|
||||
"title": "...",
|
||||
"description": "...",
|
||||
"files_affected": ["src/..."],
|
||||
"acceptance_criteria": ["..."],
|
||||
"created_at": "2026-05-20T19:00:00Z",
|
||||
"status": "pending|in_progress|done|blocked"
|
||||
}
|
||||
```
|
||||
|
||||
## Volumes
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
redis-data:
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
docker stack deploy -c runbooks/dev-stack.yml dev
|
||||
```
|
||||
|
||||
## Acesso
|
||||
|
||||
| Serviço | URL | Descrição |
|
||||
|---------|-----|-----------|
|
||||
| Task Board | `board.octal.tec.br` | Kanban + task queue em tempo real |
|
||||
| Frontend HMR | `frontend.octal.tec.br` | Vite React com hot reload |
|
||||
| Backend API | `api.octal.tec.br` | API Node com hot reload (tsx watch) |
|
||||
| Log Stream | `logs.octal.tec.br` | Logs agregados de todos os workers |
|
||||
|
||||
## 🔗 Links Úteis
|
||||
- `board.octal.tec.br` → TaskBoard (kanban + logs)
|
||||
- `api.octal.tec.br` → API REST (health, tasks, agents)
|
||||
- `frontend.octal.tec.br` → Frontend HMR
|
||||
- Vault Obsidian: `/root/Obsidian-Pulse/` — `Projetos/pulse-dev.md`
|
||||
@@ -0,0 +1,191 @@
|
||||
version: '3.9'
|
||||
|
||||
networks:
|
||||
public:
|
||||
external: true # overlay swarm — Attachable=false
|
||||
|
||||
volumes:
|
||||
redis-data:
|
||||
dev-frontend-node_modules:
|
||||
dev-backend-node_modules:
|
||||
|
||||
services:
|
||||
|
||||
# ─── FILA DE TAREFAS + COORDENAÇÃO ───────────────────────────────
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
networks: [public]
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
deploy:
|
||||
replicas: 1
|
||||
endpoint_mode: dnsrr
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# ─── TASKBOARD WEB (React standalone — sem build, direto no nginx) ──
|
||||
taskboard:
|
||||
image: nginx:alpine
|
||||
networks: [public]
|
||||
volumes:
|
||||
- /root/.openclaw/workspace/pulse-dev/taskboard/public:/usr/share/nginx/html:ro
|
||||
- /root/.openclaw/workspace/pulse-dev/taskboard/public/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
deploy:
|
||||
replicas: 1
|
||||
endpoint_mode: dnsrr
|
||||
labels:
|
||||
- caddy=board.octal.tec.br
|
||||
- caddy.reverse_proxy={{upstreams 80}}
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
dev-frontend:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
networks: [public]
|
||||
volumes:
|
||||
- /root/.openclaw/workspace/pulse-dev/frontend:/app:cached
|
||||
- dev-frontend-node_modules:/app/node_modules
|
||||
command: >
|
||||
sh -c "
|
||||
if [ ! -d node_modules ]; then
|
||||
npm install react react-dom vite @vitejs/plugin-react typescript --save-dev 2>/dev/null;
|
||||
fi &&
|
||||
echo 'FRONTEND hot reload iniciado em :5173' &&
|
||||
npx vite --host 0.0.0.0 --port 5173
|
||||
"
|
||||
deploy:
|
||||
replicas: 1
|
||||
endpoint_mode: dnsrr
|
||||
labels:
|
||||
- caddy=frontend.octal.tec.br
|
||||
- caddy.reverse_proxy={{upstreams 5173}}
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'wget -qO- http://localhost:5173 | head -1']
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
dev-backend:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
networks: [public]
|
||||
volumes:
|
||||
- /root/.openclaw/workspace/pulse-dev/backend:/app:cached
|
||||
- dev-backend-node_modules:/app/node_modules
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- NODE_ENV=development
|
||||
command: >
|
||||
sh -c "
|
||||
if [ ! -d node_modules ]; then
|
||||
npm install tsx express ioredis cors helmet --save-dev 2>/dev/null;
|
||||
fi &&
|
||||
echo 'BACKEND hot reload iniciado em :3001' &&
|
||||
npx tsx watch --dir ./src server.ts
|
||||
"
|
||||
deploy:
|
||||
replicas: 1
|
||||
endpoint_mode: dnsrr
|
||||
labels:
|
||||
- caddy=api.octal.tec.br
|
||||
- caddy.reverse_proxy={{upstreams 3001}}
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
agent-frontend:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
networks: [public]
|
||||
volumes:
|
||||
- /root/.openclaw/workspace/pulse-dev/agents/frontend:/app:cached
|
||||
- dev-frontend-node_modules:/app/node_modules
|
||||
command: >
|
||||
sh -c "
|
||||
if [ ! -f /app/agent.js ]; then echo 'ESPERANDO CODIGO DO AGENTE FRONTEND' && sleep infinity; fi &&
|
||||
if [ ! -d node_modules ]; then npm install ioredis uuid --save 2>/dev/null; fi &&
|
||||
node /app/agent.js
|
||||
"
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- AGENT_ID=agent-frontend
|
||||
- AGENT_ROLE=frontend
|
||||
- TASK_QUEUE=dev-tasks
|
||||
- LOG_CHANNEL=dev-logs
|
||||
deploy:
|
||||
replicas: 2
|
||||
endpoint_mode: dnsrr
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
max_attempts: 5
|
||||
delay: 5s
|
||||
|
||||
agent-backend:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
networks: [public]
|
||||
volumes:
|
||||
- /root/.openclaw/workspace/pulse-dev/agents/backend:/app:cached
|
||||
- dev-backend-node_modules:/app/node_modules
|
||||
command: >
|
||||
sh -c "
|
||||
if [ ! -f /app/agent.js ]; then echo 'ESPERANDO CODIGO DO AGENTE BACKEND' && sleep infinity; fi &&
|
||||
if [ ! -d node_modules ]; then npm install ioredis uuid --save 2>/dev/null; fi &&
|
||||
node /app/agent.js
|
||||
"
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- AGENT_ID=agent-backend
|
||||
- AGENT_ROLE=backend
|
||||
- TASK_QUEUE=dev-tasks
|
||||
- LOG_CHANNEL=dev-logs
|
||||
deploy:
|
||||
replicas: 2
|
||||
endpoint_mode: dnsrr
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
max_attempts: 5
|
||||
delay: 5s
|
||||
|
||||
agent-devops:
|
||||
image: node:24-alpine
|
||||
working_dir: /app
|
||||
networks: [public]
|
||||
volumes:
|
||||
- /root/.openclaw/workspace/pulse-dev/agents/devops:/app:cached
|
||||
- /usr/local/bin/docker:/usr/local/bin/docker:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
command: >
|
||||
sh -c "
|
||||
if [ ! -f /app/agent.js ]; then echo 'ESPERANDO CODIGO DO AGENTE DEVOPS' && sleep infinity; fi &&
|
||||
npm install ioredis uuid 2>/dev/null &&
|
||||
node /app/agent.js
|
||||
"
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- AGENT_ID=agent-devops
|
||||
- AGENT_ROLE=devops
|
||||
- TASK_QUEUE=dev-tasks
|
||||
- LOG_CHANNEL=dev-logs
|
||||
deploy:
|
||||
replicas: 1
|
||||
endpoint_mode: dnsrr
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
max_attempts: 5
|
||||
delay: 5s
|
||||
Reference in New Issue
Block a user