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