merge: unify dev into main with mcp postgres support

This commit is contained in:
Tiago Ribeiro 2026-03-09 09:47:32 -03:00
commit d64e38a610
16 changed files with 1378 additions and 615 deletions

View file

@ -1,52 +1,54 @@
# Infracloud - Infraestrutura Rede5 # Infracloud - Infraestrutura Rede5
Documentacao de infraestrutura como codigo (IaC) da Rede5. Este repositorio contem a documentacao e os manifestos de Infraestrutura como Codigo (IaC) da Rede5.
## Scripts ## Estrutura do Projeto
| Script | Funcao | ### Kubernetes (K3s)
|--------|--------|
| `scripts/check-connections.py` | Verifica todas as conexoes |
| `scripts/sync-vault.js` | Sincroniza credenciais entre `~/.ssh/` e object storage |
## Estrutura Clusters Kubernetes gerenciados via K3s (Lightweight Kubernetes).
```text - [**Documentacao Geral K3s**](./k3s/README.md)
infracloud/ - [Arquitetura](./k3s/architecture.md)
|-- CONNECTIONS.md - [Inventario de Servicos](./k3s/services_inventory.md)
|-- OBJECT-STORAGE.md
|-- containers/
|-- inventcloud/
| `-- invista/nexus/
| |-- OCI.md
| `-- azure-devops/
|-- scripts/
| |-- check-connections.py
| `-- sync-vault.js
`-- vps/
```
## Acesso rapido ### VPS (Virtual Private Servers)
| Servidor | Endereco | Plataforma | Documentacao | Servidores standalone gerenciados individualmente.
|----------|----------|------------|--------------|
| Redbull | 185.194.141.70 | Coolify v4 | [vps/redbull](./vps/redbull/) |
| Echo | 152.53.120.181 | Dokku | [vps/echo](./vps/echo/) |
| Absam DB | db-60604.dc-us-1.absamcloud.com:11985 | PostgreSQL 17 | [vps/absam-db](./vps/absam-db/) |
## Conexoes - [**Redbull**](./vps/redbull/README.md): Servidor de Desenvolvimento (Coolify PaaS). IP: 185.194.141.70
- [**Echo**](./vps/echo/README.md): VPS Civo usada para workloads em Docker puro. IP: 152.53.120.181
- [**Vim**](./vps/vim/README.md): Servidor de Producao (Dokku PaaS). IP: 38.19.201.52
- [**NC1**](./vps/nc1/README.md): Servidor de Producao (Podman + Systemd). IP: 185.194.141.70
- [Guia de Conexoes](./CONNECTIONS.md) ### Cloud Databases
- [Status Nexus](./invista/nexus/CONNECTION-STATUS.md)
## Ultima auditoria Bancos de dados gerenciados.
- Data: 2026-03-05 - [**Absam Cloud DB**](./databases/absam-cloud-db/README.md): PostgreSQL 17 (Absam.io). vpsID: 60604
- Comando: `python scripts/check-connections.py` - **CloudClusters**: PostgreSQL 16 externo para GoHorseJobs
- Resultado: `14/20` OK (6 erros)
- OCI: OK (namespace `grbb7qzeuoag`)
- Kubernetes/OKE: falha na verificacao de `kubectl cluster-info`
--- ### Containers
*Atualizado em: 2026-03-05* Definicoes de containers compartilhados entre servidores.
- [**Containers**](./containers/): manifests e units de Podman/Systemd
- [**Forgejo**](./containers/forgejo.md): Git self-hosted e CI/CD
### Scripts
Automacoes e utilitarios do ecossistema.
- [**scripts/auto-organized**](./scripts/auto-organized/): scripts PowerShell e Shell existentes
### MCP
Servidor MCP para sustentacao do repo.
- [**Infracloud MCP**](./mcp/README.md): FastMCP baseado no layout real do repositorio
## Notas Operacionais
- O repo atual usa `scripts/auto-organized`, nao `dev-scripts`.
- O repo atual nao possui `docs/openproject`.
- `AGENT.md` nao deve ser usado como fonte de runtime porque contem segredos em plaintext.

110
containers/forgejo.md Normal file
View file

@ -0,0 +1,110 @@
# Forgejo (Gitea) - pipe.gohorsejobs.com
Instância Forgejo self-hosted para mirror e CI/CD do projeto GoHorseJobs.
## Informacoes do Servico
| Campo | Valor |
|-------|-------|
| URL | https://pipe.gohorsejobs.com |
| Usuario | yamamoto (admin) |
| Email | yamamoto@rede5.com.br |
| Organizacao | bohessefm |
| Token | `~/.ssh/forgejo-token` |
## Autenticacao
### API Token
```bash
TOKEN=$(cat ~/.ssh/forgejo-token | grep FORGEJO_TOKEN | cut -d= -f2)
curl -H "Authorization: token $TOKEN" "https://pipe.gohorsejobs.com/api/v1/user"
```
### SSH
```bash
git clone git@pipe.gohorsejobs.com:bohessefm/gohorsejobs.git
```
## Repositorios
| Repositorio | Mirror de | Branch Principal |
|-------------|-----------|------------------|
| bohessefm/gohorsejobs | github.com/rede5/gohorsejobs | dev |
## CI/CD
O Forgejo roda workflows do `.forgejo/workflows/` para:
- Build e push de imagens Docker
- Deploy no K3s via kubectl
### Secrets e Variables
Configurados em Settings → Secrets and Variables:
**Secrets:**
- `FORGEJO_TOKEN` - Token de acesso ao registry
- `KUBE_CONFIG` - Configuracao kubectl
- `RSA_PRIVATE_KEY_BASE64` - Chave RSA privada
**Variables:**
- `DATABASE_URL` - String de conexao PostgreSQL
- `AMQP_URL` - URL RabbitMQ
- `JWT_SECRET` - Secret JWT
- `S3_BUCKET`, `AWS_*` - Credenciais S3
## API Endpoints
### Usuario
```bash
curl -H "Authorization: token $TOKEN" "https://pipe.gohorsejobs.com/api/v1/user"
```
### Repositorios
```bash
curl -H "Authorization: token $TOKEN" "https://pipe.gohorsejobs.com/api/v1/user/repos"
curl -H "Authorization: token $TOKEN" "https://pipe.gohorsejobs.com/api/v1/repos/bohessefm/gohorsejobs"
```
### Issues
```bash
curl -H "Authorization: token $TOKEN" "https://pipe.gohorsejobs.com/api/v1/repos/bohessefm/gohorsejobs/issues"
```
### Webhooks
```bash
# Listar webhooks
curl -H "Authorization: token $TOKEN" "https://pipe.gohorsejobs.com/api/v1/repos/bohessefm/gohorsejobs/hooks"
# Criar webhook
curl -X POST -H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
"https://pipe.gohorsejobs.com/api/v1/repos/bohessefm/gohorsejobs/hooks" \
-d '{
"type": "gitea",
"config": {
"url": "https://redbull.rede5.com.br/api/v1/webhooks/deploy?uuid=iw4sow8s0kkg4cccsk08gsoo",
"content_type": "json"
},
"events": ["push"],
"active": true
}'
```
## Docker Registry
O Forgejo inclui um registry Docker integrado:
```bash
# Login
echo $TOKEN | docker login pipe.gohorsejobs.com -u bohessefm --password-stdin
# Pull imagem
docker pull pipe.gohorsejobs.com/bohessefm/gohorsejobs:latest
docker pull pipe.gohorsejobs.com/bohessefm/backoffice:latest
```
## Links
- Console: https://pipe.gohorsejobs.com
- API Docs: https://pipe.gohorsejobs.com/api/swagger
- Credenciais: `~/.ssh/forgejo-token`

View file

@ -0,0 +1,130 @@
# Absam Cloud DB - PostgreSQL Gerenciado
Cloud Database gerenciado pela Absam.io (ID: 60604). Hospeda múltiplos bancos de dados para os projetos da Rede5.
## Informacoes do Servidor
| Campo | Valor |
|-------|-------|
| vpsID | 60604 |
| Nome | novo-cloud-database |
| OS | PostgreSQL 17 |
| Provedor | Absam.io |
## Conectividade
### SSH
| Campo | Valor |
|-------|-------|
| Host | db-60604.dc-us-1.absamcloud.com |
| Porta | 18863 |
| Usuario | admin |
| Senha | `~/.ssh/absam-db-novo` |
### PostgreSQL
| Tipo | Host | Porta |
|------|------|-------|
| Privado (interno) | 10.0.9.219 | 5432 |
| Publico | db-60604.dc-us-1.absamcloud.com | 11985 |
### Credenciais do Banco
| Campo | Valor |
|-------|-------|
| Database | saveinmed |
| Usuario | saveinmed |
| Senha | `8ljys1jlnfmu` |
> Credenciais SSH completas em `~/.ssh/absam-db-novo`
## Configuracao SSH
Adicionar ao `~/.ssh/config`:
```
Host absam-db
HostName db-60604.dc-us-1.absamcloud.com
User admin
Port 18863
PreferredAuthentications password
PubkeyAuthentication no
```
## Uso
### Conectar via SSH
```bash
ssh absam-db
# Senha: ver ~/.ssh/absam-db-novo
```
### Conectar ao PostgreSQL (via tunel SSH)
```bash
# Criar tunel SSH
ssh -L 15432:10.0.9.219:5432 absam-db -N
# Conectar ao banco
psql -h localhost -p 15432 -U saveinmed -d saveinmed
```
### Conectar ao PostgreSQL (porta publica)
```bash
psql -h db-60604.dc-us-1.absamcloud.com -p 11985 -U saveinmed -d saveinmed
```
## Databases Disponiveis
| Database | Proprietario |
|----------|--------------|
| saveinmed | postgres |
| gohorsejobs | postgres |
| rodiziosdaqui | postgres |
| ghj_codex | postgres |
| postgres | postgres |
## Projetos que utilizam este servidor
- [saveinmed](../../saveinmed/) - Backend Medusa v2
---
# CloudClusters Database (Externo)
Banco PostgreSQL externo hospedado no CloudClusters, utilizado pelo projeto GoHorseJobs.
## Informacoes do Servidor
| Campo | Valor |
|-------|-------|
| Host | postgresql-207755-0.cloudclusters.net |
| Porta | 10078 |
| Database | gohorsejobs |
| Usuario | gohorse |
| Senha | `wwc9dD04WAE44zX` |
| SSL | Desabilitado |
| Versao | PostgreSQL 16.0 |
## Connection String
```
postgres://gohorse:wwc9dD04WAE44zX@postgresql-207755-0.cloudclusters.net:10078/gohorsejobs?sslmode=disable
```
## Testar Conexao
```bash
# Via VPS (echo)
ssh echo "psql 'postgres://gohorse:wwc9dD04WAE44zX@postgresql-207755-0.cloudclusters.net:10078/gohorsejobs?sslmode=disable' -c 'SELECT version();'"
# Via psql local (se disponivel)
psql "postgres://gohorse:wwc9dD04WAE44zX@postgresql-207755-0.cloudclusters.net:10078/gohorsejobs?sslmode=disable" -c "SELECT version();"
```
## Projetos que utilizam este servidor
- [gohorsejobs](../../gohorsejobs/) - Plataforma de empregos

View file

@ -81,6 +81,7 @@ oci os object put \
- **Arquivo**: `~/.ssh/cloudflare-token` - **Arquivo**: `~/.ssh/cloudflare-token`
- **Email**: yamamoto@rede5.com.br - **Email**: yamamoto@rede5.com.br
- **Zone ID (gohorsejobs.com)**: `5e7e9286849525abf7f30b451b7964ac` - **Zone ID (gohorsejobs.com)**: `5e7e9286849525abf7f30b451b7964ac`
- **Zone ID (q1food.com)**: `70cdac5cc9787719d548ac6d74c98896`
### Coolify (Redbull) ### Coolify (Redbull)
- **Arquivo**: `~/.ssh/coolify-redbull-token` - **Arquivo**: `~/.ssh/coolify-redbull-token`
@ -227,3 +228,44 @@ curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "https://
curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "https://redbull.rede5.com.br/api/v1/deploy?uuid=s4kskw08400wcw8g40ossggo" # Backoffice curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "https://redbull.rede5.com.br/api/v1/deploy?uuid=s4kskw08400wcw8g40ossggo" # Backoffice
curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "https://redbull.rede5.com.br/api/v1/deploy?uuid=ko0kkw4kw8g80scksg8sk0wc" # Seeder curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "https://redbull.rede5.com.br/api/v1/deploy?uuid=ko0kkw4kw8g80scksg8sk0wc" # Seeder
``` ```
---
## Q1Food (Coolify - Redbull)
### URLs
- Frontend: https://dev.q1food.com
- Backend: https://api-dev.q1food.com
### DNS Cloudflare (Zone ID: 70cdac5cc9787719d548ac6d74c98896)
| Record | Tipo | IP |
|--------|------|-----|
| q1food.com | A | 185.194.141.70 |
| dev.q1food.com | A | 185.194.141.70 |
| api-dev.q1food.com | A | 185.194.141.70 |
### UUIDs Coolify
| Serviço | UUID |
|---------|------|
| Backend | eosgwscc4g044c884k0ws4gc |
| Frontend | g8w440g0w0oowo8skss440wk |
### Webhooks GitHub
| Serviço | Webhook Secret |
|---------|----------------|
| Backend | d66bac276faa04631124559d77199d0e |
| Frontend | c512dc54933829d1cde381e2c9bf394a |
### Deploy
```bash
# Deploy backend
curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "http://185.194.141.70:8000/api/v1/deploy?uuid=eosgwscc4g044c884k0ws4gc"
# Deploy frontend
curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "http://185.194.141.70:8000/api/v1/deploy?uuid=g8w440g0w0oowo8skss440wk"
```
**Deploy automático:** Push na branch `dev` dispara webhook do GitHub → Coolify

2
mcp/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__/
*.pyc

45
mcp/README.md Normal file
View file

@ -0,0 +1,45 @@
# Infracloud MCP
MCP server for the actual `infracloud` repository layout.
## Scope
- Reads `vps/*/services_inventory.md` and `k3s/services_inventory.md`
- Lists and reads `containers/*.container` and `containers/*.service`
- Lists scripts in `scripts/auto-organized`
- Executes only scripts classified as read-only diagnostics
- Provides repo grep and safe document reads
- Supports optional Postgres-backed notes and read-only SQL diagnostics
## Install
```powershell
pip install -r C:\dev\infracloud\mcp\requirements.txt
```
## Run
```powershell
python C:\dev\infracloud\mcp\server.py
```
## Postgres
Set `INFRA_MCP_POSTGRES_DSN` if you want the server to use Postgres for notes and SQL diagnostics.
Example:
```powershell
$env:INFRA_MCP_POSTGRES_DSN="postgresql://user:password@127.0.0.1:5432/infracloud_mcp"
python C:\dev\infracloud\mcp\server.py
```
## Claude Desktop
Use `claude_desktop_config.infracloud.json` as a base and merge it into your Claude Desktop MCP config.
## Notes
- This repo currently uses `scripts/auto-organized`, not `dev-scripts`.
- This repo currently does not have `docs/openproject`.
- `AGENT.md` includes secrets in plaintext. The MCP server does not expose that file.

7
mcp/bootstrap.sql Normal file
View file

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS infra_mcp_notes (
id BIGSERIAL PRIMARY KEY,
scope TEXT NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

View file

@ -0,0 +1,14 @@
{
"mcpServers": {
"infracloud-sustentacao": {
"command": "python",
"args": [
"C:\\dev\\infracloud\\mcp\\server.py"
],
"cwd": "C:\\dev\\infracloud",
"env": {
"INFRA_MCP_POSTGRES_DSN": "postgresql://user:password@127.0.0.1:5432/infracloud_mcp"
}
}
}
}

2
mcp/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
mcp>=1.6.0
psycopg[binary]>=3.2.0

536
mcp/server.py Normal file
View file

@ -0,0 +1,536 @@
from __future__ import annotations
import os
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from mcp.server.fastmcp import FastMCP
import psycopg
from psycopg.rows import dict_row
REPO_ROOT = Path(__file__).resolve().parents[1]
SCRIPTS_ROOT = REPO_ROOT / "scripts" / "auto-organized"
VPS_ROOT = REPO_ROOT / "vps"
CONTAINERS_ROOT = REPO_ROOT / "containers"
DATABASES_ROOT = REPO_ROOT / "databases"
K3S_ROOT = REPO_ROOT / "k3s"
POSTGRES_DSN_ENV = "INFRA_MCP_POSTGRES_DSN"
DOC_ALLOWLIST = (
REPO_ROOT / "README.md",
VPS_ROOT,
CONTAINERS_ROOT,
DATABASES_ROOT,
K3S_ROOT,
)
READ_ONLY_SCRIPT_PREFIXES = (
"check_",
"fetch_",
"get_",
"inspect_",
"verify_",
"final_status",
"watch_",
)
MUTATING_SCRIPT_PREFIXES = (
"approve_",
"complete_",
"fix_",
"merge_",
"retrigger_",
"revert_",
)
mcp = FastMCP(
"infracloud-sustentacao",
instructions=(
"Use the real infracloud repository as the source of truth. "
"Prefer inventory markdown, container unit files, and existing scripts. "
"Do not assume paths like dev-scripts or docs/openproject if they do not exist. "
"If Postgres is configured, prefer the MCP Postgres helpers for server-side persistence and diagnostics."
),
)
@dataclass(frozen=True)
class ScriptInfo:
path: Path
relative_path: str
is_read_only: bool
kind: str
def _postgres_dsn() -> str | None:
value = os.getenv(POSTGRES_DSN_ENV, "").strip()
return value or None
def _postgres_enabled() -> bool:
return _postgres_dsn() is not None
def _get_pg_connection():
dsn = _postgres_dsn()
if not dsn:
raise ValueError(f"{POSTGRES_DSN_ENV} is not configured")
return psycopg.connect(dsn, row_factory=dict_row)
def _ensure_mcp_tables() -> None:
if not _postgres_enabled():
return
with _get_pg_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"""
CREATE TABLE IF NOT EXISTS infra_mcp_notes (
id BIGSERIAL PRIMARY KEY,
scope TEXT NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
"""
)
conn.commit()
def _ensure_in_repo(path: Path) -> Path:
resolved = path.resolve()
if REPO_ROOT not in resolved.parents and resolved != REPO_ROOT:
raise ValueError(f"path escapes repository root: {path}")
return resolved
def _script_kind(name: str) -> str:
lower = name.lower()
if lower.endswith(".ps1"):
return "powershell"
if lower.endswith(".sh"):
return "shell"
return "other"
def _is_read_only_script(name: str) -> bool:
lower = name.lower()
if lower.endswith((".json", ".yaml", ".yml", ".txt", ".pem")):
return False
if lower.startswith(MUTATING_SCRIPT_PREFIXES):
return False
return lower.startswith(READ_ONLY_SCRIPT_PREFIXES)
def _list_scripts() -> list[ScriptInfo]:
if not SCRIPTS_ROOT.exists():
return []
results: list[ScriptInfo] = []
for path in sorted(SCRIPTS_ROOT.rglob("*")):
if not path.is_file():
continue
relative = path.relative_to(REPO_ROOT).as_posix()
results.append(
ScriptInfo(
path=path,
relative_path=relative,
is_read_only=_is_read_only_script(path.name),
kind=_script_kind(path.name),
)
)
return results
def _resolve_script(script_name: str) -> ScriptInfo:
script_name = script_name.replace("\\", "/").strip()
candidates = _list_scripts()
exact = [item for item in candidates if item.relative_path == script_name or item.path.name == script_name]
if len(exact) == 1:
return exact[0]
if len(exact) > 1:
raise ValueError(f"multiple scripts matched '{script_name}', use a repo-relative path")
fuzzy = [item for item in candidates if script_name.lower() in item.relative_path.lower()]
if len(fuzzy) == 1:
return fuzzy[0]
if len(fuzzy) > 1:
names = ", ".join(item.relative_path for item in fuzzy[:10])
raise ValueError(f"multiple scripts matched '{script_name}': {names}")
raise ValueError(f"script not found: {script_name}")
def _read_text(path: Path, max_chars: int = 20000) -> str:
resolved = _ensure_in_repo(path)
text = resolved.read_text(encoding="utf-8", errors="replace")
if len(text) > max_chars:
return text[:max_chars] + "\n... [truncated]"
return text
def _parse_markdown_table(lines: list[str], start_index: int) -> tuple[list[dict[str, str]], int]:
header_line = lines[start_index].strip()
separator_index = start_index + 1
if separator_index >= len(lines):
return [], start_index + 1
separator_line = lines[separator_index].strip()
if "|" not in header_line or "|" not in separator_line:
return [], start_index + 1
headers = [part.strip(" `") for part in header_line.strip("|").split("|")]
rows: list[dict[str, str]] = []
index = start_index + 2
while index < len(lines):
line = lines[index].rstrip()
if "|" not in line or not line.strip().startswith("|"):
break
values = [part.strip() for part in line.strip().strip("|").split("|")]
if len(values) == len(headers):
rows.append(dict(zip(headers, values)))
index += 1
return rows, index
def _parse_inventory_file(path: Path) -> dict[str, Any]:
lines = _read_text(path, max_chars=120000).splitlines()
parsed: dict[str, Any] = {"file": path.relative_to(REPO_ROOT).as_posix(), "sections": {}}
current_section = "root"
parsed["sections"][current_section] = {"tables": [], "paragraphs": []}
index = 0
while index < len(lines):
line = lines[index].rstrip()
if line.startswith("#"):
current_section = line.lstrip("#").strip()
parsed["sections"].setdefault(current_section, {"tables": [], "paragraphs": []})
index += 1
continue
if line.strip().startswith("|"):
rows, next_index = _parse_markdown_table(lines, index)
if rows:
parsed["sections"][current_section]["tables"].append(rows)
index = next_index
continue
if line.strip():
parsed["sections"][current_section]["paragraphs"].append(line.strip())
index += 1
return parsed
def _iter_inventory_files() -> list[Path]:
return sorted(VPS_ROOT.rglob("services_inventory.md")) + sorted(K3S_ROOT.rglob("services_inventory.md"))
def _match_service(query: str, row: dict[str, str]) -> bool:
haystack = " ".join(str(value) for value in row.values()).lower()
return query.lower() in haystack
def _safe_doc_path(relative_path: str) -> Path:
relative = Path(relative_path)
candidate = _ensure_in_repo(REPO_ROOT / relative)
for allowed in DOC_ALLOWLIST:
allowed_resolved = allowed.resolve()
if candidate == allowed_resolved or allowed_resolved in candidate.parents:
return candidate
raise ValueError(f"path not allowed: {relative_path}")
def _ensure_read_only_sql(sql: str) -> str:
normalized = sql.strip().lstrip("(").strip().lower()
if not normalized.startswith("select"):
raise ValueError("only SELECT queries are allowed")
forbidden = ("insert ", "update ", "delete ", "drop ", "alter ", "truncate ", "create ", "grant ", "revoke ")
if any(token in normalized for token in forbidden):
raise ValueError("query contains non-read-only statements")
return sql
@mcp.tool(
description="List scripts available in scripts/auto-organized, including whether each one is safe for read-only execution.",
)
def list_repo_scripts(name_filter: str | None = None) -> list[dict[str, Any]]:
scripts = _list_scripts()
if name_filter:
scripts = [item for item in scripts if name_filter.lower() in item.relative_path.lower()]
return [
{
"name": item.path.name,
"relative_path": item.relative_path,
"kind": item.kind,
"read_only": item.is_read_only,
}
for item in scripts
]
@mcp.tool(
description="Run an existing repo script from scripts/auto-organized. Only read-only diagnostic scripts are executable.",
)
def run_repo_script(script_name: str, args: list[str] | None = None, timeout_seconds: int = 60) -> dict[str, Any]:
script = _resolve_script(script_name)
if not script.is_read_only:
raise ValueError(
f"script '{script.relative_path}' is not classified as read-only and cannot be executed by this tool"
)
args = args or []
if script.kind == "powershell":
command = [
"powershell",
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
str(script.path),
*args,
]
elif script.kind == "shell":
command = ["bash", str(script.path), *args]
else:
raise ValueError(f"unsupported script type: {script.kind}")
completed = subprocess.run(
command,
cwd=REPO_ROOT,
capture_output=True,
text=True,
timeout=timeout_seconds,
check=False,
)
return {
"script": script.relative_path,
"exit_code": completed.returncode,
"stdout": completed.stdout[-12000:],
"stderr": completed.stderr[-12000:],
}
@mcp.tool(
description="Parse one services_inventory.md file into structured JSON. Server examples: redbull, vim, nc1, k3s.",
)
def read_services_inventory(server: str) -> dict[str, Any]:
inventory_files = {path.parent.name.lower(): path for path in _iter_inventory_files()}
server_key = server.lower().strip()
if server_key not in inventory_files:
raise ValueError(f"inventory not found for '{server}'. Available: {', '.join(sorted(inventory_files))}")
return _parse_inventory_file(inventory_files[server_key])
@mcp.tool(
description="Search all inventory files for an app, UUID, domain, server, or other service text.",
)
def find_service(query: str) -> list[dict[str, Any]]:
matches: list[dict[str, Any]] = []
for inventory_path in _iter_inventory_files():
parsed = _parse_inventory_file(inventory_path)
for section_name, section in parsed["sections"].items():
for table in section["tables"]:
for row in table:
if _match_service(query, row):
matches.append(
{
"inventory": parsed["file"],
"section": section_name,
"row": row,
}
)
return matches
@mcp.tool(
description="List Podman/Systemd unit files under containers/ and optionally filter by app name.",
)
def list_container_units(name_filter: str | None = None) -> list[dict[str, str]]:
results: list[dict[str, str]] = []
for path in sorted(CONTAINERS_ROOT.iterdir()):
if not path.is_file():
continue
if path.suffix not in {".container", ".service"}:
continue
relative = path.relative_to(REPO_ROOT).as_posix()
if name_filter and name_filter.lower() not in relative.lower():
continue
results.append({"name": path.name, "relative_path": relative, "kind": path.suffix.lstrip(".")})
return results
@mcp.tool(
description="Read a container unit file from containers/ for Podman/Systemd runtime analysis.",
)
def read_container_unit(name: str) -> dict[str, str]:
candidates = [
path
for path in CONTAINERS_ROOT.iterdir()
if path.is_file() and path.suffix in {".container", ".service"} and (path.name == name or name.lower() in path.name.lower())
]
if not candidates:
raise ValueError(f"container unit not found: {name}")
if len(candidates) > 1:
names = ", ".join(path.name for path in candidates)
raise ValueError(f"multiple container units matched '{name}': {names}")
path = candidates[0]
return {
"relative_path": path.relative_to(REPO_ROOT).as_posix(),
"content": _read_text(path, max_chars=16000),
}
@mcp.tool(
description="Read a repo document from README, vps, databases, k3s, or containers paths.",
)
def read_repo_document(relative_path: str, max_chars: int = 12000) -> dict[str, str]:
path = _safe_doc_path(relative_path)
return {
"relative_path": path.relative_to(REPO_ROOT).as_posix(),
"content": _read_text(path, max_chars=max_chars),
}
@mcp.tool(
description="Search the repo for infrastructure terms such as app names, domains, env keys, or container names.",
)
def grep_repo(query: str, glob: str | None = None) -> dict[str, Any]:
command = ["rg", "-n", query, str(REPO_ROOT)]
if glob:
command.extend(["-g", glob])
completed = subprocess.run(
command,
cwd=REPO_ROOT,
capture_output=True,
text=True,
timeout=30,
check=False,
)
results = completed.stdout.splitlines()
return {
"exit_code": completed.returncode,
"matches": results[:200],
"truncated": len(results) > 200,
"stderr": completed.stderr[-4000:],
}
@mcp.tool(
description="Return a compact summary of the actual infracloud repo layout so agents do not assume missing folders like dev-scripts or docs/openproject.",
)
def repo_layout_summary() -> dict[str, Any]:
return {
"repo_root": str(REPO_ROOT),
"present_top_level_dirs": sorted(path.name for path in REPO_ROOT.iterdir() if path.is_dir()),
"scripts_root": SCRIPTS_ROOT.relative_to(REPO_ROOT).as_posix() if SCRIPTS_ROOT.exists() else None,
"inventory_files": [path.relative_to(REPO_ROOT).as_posix() for path in _iter_inventory_files()],
"container_units": [path.name for path in CONTAINERS_ROOT.iterdir() if path.is_file() and path.suffix in {".container", ".service"}],
"notes": [
"The repo uses scripts/auto-organized instead of dev-scripts.",
"The repo does not currently include docs/openproject.",
"AGENT.md contains secrets and should not be used as a runtime configuration source.",
f"Optional Postgres integration is enabled through {POSTGRES_DSN_ENV}.",
],
}
@mcp.tool(
description="Return the configured Postgres status for the MCP server and basic connectivity details.",
)
def postgres_healthcheck() -> dict[str, Any]:
dsn = _postgres_dsn()
if not dsn:
return {"configured": False, "env_var": POSTGRES_DSN_ENV}
with _get_pg_connection() as conn:
with conn.cursor() as cur:
cur.execute("SELECT current_database() AS database, current_user AS user, version() AS version")
row = cur.fetchone()
return {
"configured": True,
"env_var": POSTGRES_DSN_ENV,
"database": row["database"],
"user": row["user"],
"version": row["version"],
}
@mcp.tool(
description="Execute a read-only SELECT query against the MCP Postgres database.",
)
def postgres_query(sql: str, limit: int = 100) -> dict[str, Any]:
if limit < 1 or limit > 500:
raise ValueError("limit must be between 1 and 500")
safe_sql = _ensure_read_only_sql(sql)
wrapped = f"SELECT * FROM ({safe_sql.rstrip().rstrip(';')}) AS q LIMIT {limit}"
with _get_pg_connection() as conn:
with conn.cursor() as cur:
cur.execute(wrapped)
rows = cur.fetchall()
return {
"row_count": len(rows),
"rows": rows,
}
@mcp.tool(
description="Store a short operational note in the MCP Postgres database for future support sessions.",
)
def add_operational_note(scope: str, title: str, body: str) -> dict[str, Any]:
if not scope.strip() or not title.strip() or not body.strip():
raise ValueError("scope, title, and body are required")
_ensure_mcp_tables()
with _get_pg_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"""
INSERT INTO infra_mcp_notes (scope, title, body)
VALUES (%s, %s, %s)
RETURNING id, scope, title, body, created_at
""",
(scope.strip(), title.strip(), body.strip()),
)
row = cur.fetchone()
conn.commit()
return row
@mcp.tool(
description="List recent operational notes stored in the MCP Postgres database.",
)
def list_operational_notes(scope: str | None = None, limit: int = 20) -> list[dict[str, Any]]:
if limit < 1 or limit > 200:
raise ValueError("limit must be between 1 and 200")
_ensure_mcp_tables()
query = """
SELECT id, scope, title, body, created_at
FROM infra_mcp_notes
"""
params: list[Any] = []
if scope and scope.strip():
query += " WHERE scope = %s"
params.append(scope.strip())
query += " ORDER BY created_at DESC LIMIT %s"
params.append(limit)
with _get_pg_connection() as conn:
with conn.cursor() as cur:
cur.execute(query, params)
return cur.fetchall()
if __name__ == "__main__":
_ensure_mcp_tables()
mcp.run()

View file

@ -1,121 +1,37 @@
# Echo - Servidor de Producao # VPS Echo
Servidor de producao utilizando a plataforma Dokku para deploy de aplicacoes. Servidor VPS gerenciado pela Rede5 hospedado na Civo.
## Visao Geral ## Informacoes de Conexao
O servidor Echo e uma infraestrutura de producao que hospeda multiplas aplicacoes web, seguindo uma arquitetura baseada em containers. A plataforma Dokku serve como camada de gerenciamento de aplicacoes, similar a um Heroku self-hosted, permitindo deploys rapidos atraves de Git pushes. O Nginx atua como proxy reverso, roteando o trafego baseado em dominios para as aplicacoes corretas. - **Nome**: echo
- **IP Publico**: 152.53.120.181
- **Usuario**: root
- **Chave SSH**: `~/.ssh/civo`
## Especificacoes do Servidor ## Como Conectar
| Recurso | Especificacao |
|---------|---------------|
| **Hostname** | v2202501247812309542 |
| **IP Publico** | 152.53.120.181 |
| **OS** | Ubuntu 24.04.3 LTS (Noble Numbat) |
| **Kernel** | 6.8.0-90-generic |
| **CPU** | AMD EPYC (Virtual) |
| **RAM** | 7.8 GB |
| **Disco** | 251 GB (7.1 GB usado) |
| **Uptime** | 19+ dias |
| **Plataforma** | Dokku (PaaS self-hosted) |
| **Container Runtime** | Docker |
| **Proxy Reverso** | Nginx |
## Dominios
| Dominio | Uso |
|---------|-----|
| echo.rede5.com.br | Dominio global |
| photum.rede5.com.br | Photum API |
| api-hml.q1food.com | Food Backend |
## Projetos Hospedados
| Aplicacao | Porta | Dominio | Status |
|-----------|:-----:|---------|:------:|
| **photum** | 5000 | photum.rede5.com.br | Running |
| **food-backend** | 8000 | api-hml.q1food.com | Running |
| **q1agenda-backend** | 8000 | 152.53.120.181 | Running |
## Bancos de Dados
| Servico | Versao | Container | Porta |
|---------|:------:|-----------|:-----:|
| **photum-db** | PostgreSQL 18.1 | dokku.postgres.photum-db | 5432 |
| **q1agenda-db** | PostgreSQL 18.1 | dokku.postgres.q1agenda-db | 5432 |
## Acesso SSH
```bash ```bash
# Conectar via SSH
ssh echo ssh echo
# Ou diretamente
ssh -i C:\Users\Administrator\.ssh\civo root@152.53.120.181
``` ```
## Comandos Dokku ## Estado Atual Observado
```bash - Runtime principal: Docker puro
# Listar aplicacoes - Repositorios presentes em `/root`:
dokku apps:list - `/root/gohorsejobs`
- `/root/infracloud`
- Containers Postgres ativos observados:
- `ghj-db-dev`
- `sim-db-dev`
- `saveinmed-postgres`
# Listar bancos PostgreSQL ## Uso no Contexto do Infracloud MCP
dokku postgres:list
# Ver logs de uma app - O clone do repo fica em `/root/infracloud`
dokku logs photum - O MCP pode usar um banco Postgres da propria VPS via variavel `INFRA_MCP_POSTGRES_DSN`
- Preferir banco dedicado `infracloud_mcp` em vez de reutilizar schemas de aplicacao
# Deploy de uma app ## Servicos e Finalidade
git remote add dokku dokku@echo.rede5.com.br:photum
git push dokku main:master
# Ver config de uma app Workloads de desenvolvimento e sustentacao operando fora de Dokku/Coolify, com compose e containers dedicados.
dokku config:show photum
# Adicionar variavel de ambiente
dokku config:set photum KEY=VALUE
# Restart app
dokku ps:restart photum
```
## Detalhes das Aplicacoes
### Photum
- **Dominio**: photum.rede5.com.br
- **Porta**: 5000
- **Banco**: photum-db (PostgreSQL 18.1)
- **Storage**: Civo Object Storage (ver ~/.ssh/civo-object-storage)
- **Stack**: Go
**Pastas do bucket rede5:**
- `vault/ssh/` - Backup credenciais SSH
- `ghorsejobs-dev/` - Resumes GoHorseJobs
- `photum-dev/` - Imagens Photum
- `obramarket-dev/` - Uploads ObraMarket
- `rodiziosdaqui-dev/` - Uploads Rodizios
- `q1-dev/`, `q1-hml/`, `q1/` - Uploads Q1
- `virtual-fashion-dev/` - Uploads Virtual Fashion
### Food Backend
- **Dominio**: api-hml.q1food.com
- **Porta**: 8000
- **Banco**: Supabase (externo)
- **Stack**: Python/FastAPI
### Q1 Agenda Backend
- **Dominio**: 152.53.120.181
- **Porta**: 8000
- **Banco**: q1agenda-db (PostgreSQL 18.1)
- **Stack**: Python/FastAPI
- **Integracao**: Appwrite
## Comparacao com Outros Servidores
| Servidor | Uso | Plataforma |
|----------|-----|------------|
| **Redbull** | Desenvolvimento (DEV) | Coolify |
| **Echo** | Producao (PROD) | Dokku |
| **Vim** | Producao (PROD) | Dokku |

View file

@ -10,8 +10,11 @@ O servidor Redbull é uma infraestrutura de desenvolvimento que hospeda múltipl
| Recurso | Especificação | | Recurso | Especificação |
|---------|---------------| |---------|---------------|
| **Hostname** | redbull.rede5.com.br | | **Hostname** | redbull |
| **IP Público** | 185.194.141.70 | | **IP Público** | 185.194.141.70 |
| **Domínio** | redbull.rede5.com.br |
| **Coolify Dashboard** | https://redbull.rede5.com.br |
| **API** | https://redbull.rede5.com.br/api/v1 |
| **Plataforma** | VPS Ubuntu/Debian | | **Plataforma** | VPS Ubuntu/Debian |
| **Orquestrador** | Coolify v4 (PaaS self-hosted) | | **Orquestrador** | Coolify v4 (PaaS self-hosted) |
| **Container Runtime** | Docker | | **Container Runtime** | Docker |
@ -20,18 +23,18 @@ O servidor Redbull é uma infraestrutura de desenvolvimento que hospeda múltipl
## Projetos Hospedados ## Projetos Hospedados
### GoHorse Jobs (LOCAL) ### GoHorse Jobs (DEV)
| Serviço | UUID | Porta | Domínio | CI/CD | Status | | Serviço | UUID | Porta | Domínio |
|---------|------|:-----:|---------|-------|:------:| |---------|------|:-----:|---------|
| Backend | `iw4sow8s0kkg4cccsk08gsoo` | 8521 | test2.q1agenda.com.br | ✅ GitHub | ✅ | | Backend | `iw4sow8s0kkg4cccsk08gsoo` | 8521 | coolify-dev.gohorsejobs.com |
| Frontend | `ao8g40scws0w4cgo8coc8o40` | 3000 | local.gohorsejobs.com | ✅ GitHub | ✅ | | Frontend | `ao8g40scws0w4cgo8coc8o40` | 3000 | local.gohorsejobs.com |
| Backoffice | `hg48wkw4wggwsswcwc8sooo4` | 3001 | b-local.gohorsejobs.com | ✅ GitHub | ✅ | | Backoffice | `hg48wkw4wggwsswcwc8sooo4` | 3001 | backoffice-dev.gohorsejobs.com |
| Seeder | `q4w48gos8cgssso00o8w8gck` | 8080 | s-local.gohorsejobs.com | ✅ GitHub | ✅ Healthy | | Seeder | `q4w48gos8cgssso00o8w8gck` | 8080 | seeder-dev.gohorsejobs.com |
| Database | `bgws48os8wgwk08o48wg8k80` | 5432 | Internal | - | ✅ Healthy | | Database | `bgws48os8wgwk08o48wg8k80` | 5432 | Internal |
### Subdomínios Locais (Cloudflare) ### Subdomínios Locais (Cloudflare)
指向 Coolify (redbull.rede5.com.br / 185.194.141.70): 指向 Coolify (185.194.141.70):
| Subdomínio | Tipo | UUID App | | Subdomínio | Tipo | UUID App |
|------------|------|----------| |------------|------|----------|
@ -43,45 +46,32 @@ O servidor Redbull é uma infraestrutura de desenvolvimento que hospeda múltipl
> **Cloudflare Zone**: gohorsejobs.com (Zone ID: 5e7e9286849525abf7f30b451b7964ac) > **Cloudflare Zone**: gohorsejobs.com (Zone ID: 5e7e9286849525abf7f30b451b7964ac)
### Rodizios Daqui (DEV) ### Rodizios Daqui (DEV)
| Serviço | UUID | Porta | Domínio | CI/CD | Status | | Serviço | UUID | Porta | Domínio |
|---------|------|:-----:|---------|-------|:------:| |---------|------|:-----:|---------|
| Backend | `ecso4woo4woo0ks84888k8ck` | 8080 | api.rodiziosdaqui.rede5.com.br | ✅ GitHub | ✅ | | Backend | `ecso4woo4woo0ks84888k8ck` | 8080 | api.rodiziosdaqui.rede5.com.br |
| Frontend | `l4g4cc884gc04kg8s480gs48` | 3000 | rodiziosdaqui.rede5.com.br | ✅ GitHub | ✅ | | Frontend | `l4g4cc884gc04kg8s480gs48` | 3000 | rodiziosdaqui.rede5.com.br |
| Backoffice | `s4kskw08400wcw8g40ossggo` | 8082 | backoffice.rodiziosdaqui.rede5.com.br | ✅ GitHub | ✅ | | Backoffice | `s4kskw08400wcw8g40ossggo` | 8082 | backoffice.rodiziosdaqui.rede5.com.br |
| Seeder | `ko0kkw4kw8g80scksg8sk0wc` | 8081 | seeder.rodiziosdaqui.rede5.com.br | ✅ GitHub | ✅ Healthy | | Seeder | `ko0kkw4kw8g80scksg8sk0wc` | 8081 | seeder.rodiziosdaqui.rede5.com.br |
### Q1 Agenda (DEV/HML)
| Serviço | UUID | Domínio | CI/CD | Status |
|---------|------|---------|-------|:------:|
| backend-dev | `h8kkc0gw04sow8oo08ccg00s` | sem domínio | ❌ | ❌ Exited |
| backend-hml | `bk0kcosgo0s8cogww8s4so0w` | sem domínio | ❌ | ❌ Exited |
| frontend-dev | `wwgccg0o8g4ogowgowcw4gcg` | sem domínio | ❌ | ❌ Exited |
| frontend-hml | `es0gg4gokssw8c88o4kg0c08` | sem domínio | ❌ | ❌ Exited |
### Q1 Food (DEV)
| Serviço | UUID | Domínio | CI/CD | Status |
|---------|------|---------|-------|:------:|
| Backend | `eosgwscc4g044c884k0ws4gc` | api-dev.q1food.com | ✅ GitHub | ❌ Exited |
| Frontend | `g8w440g0w0oowo8skss440wk` | dev.q1food.com | ✅ GitHub | ✅ Running |
### Sextando (DEV)
| Serviço | UUID | Domínio | CI/CD | Status |
|---------|------|---------|-------|:------:|
| Backend | `h8c4sg0sg80c4o8kkcw4ogcg` | api-dev.sextando.com.br | ❌ | ❌ Exited |
| Backoffice | `nsc0gw0og40g0c88soc0o0wc` | backoffice-dev.sextando.com.br | ❌ | ❌ Exited |
| Frontend | `vggkcs880o8go0ws84wcggco` | dev.sextando.com.br | ❌ | ✅ Running |
### ObraMarket (DEV) — ⚠️ 4 duplicatas, todas paradas
> Ver `services_inventory.md` para detalhe. Ação: limpar e manter apenas 1.
> **DNS Zone**: rede5.com.br (Zone ID: 7eb6587a2c867bb9743719a8fabcd455) > **DNS Zone**: rede5.com.br (Zone ID: 7eb6587a2c867bb9743719a8fabcd455)
> **Banco de Dados**: PostgreSQL externo (Absam Cloud) - db-60604.dc-us-1.absamcloud.com:11985 > **Banco de Dados**: PostgreSQL externo (Absam Cloud) - db-60604.dc-us-1.absamcloud.com:11985
### Q1Food (DEV)
| Serviço | UUID | Porta | Domínio |
|---------|------|:-----:|---------|
| Backend | `eosgwscc4g044c884k0ws4gc` | 8000 | api-dev.q1food.com |
| Frontend | `g8w440g0w0oowo8skss440wk` | 3000 | dev.q1food.com |
> **DNS Zone**: q1food.com (Zone ID: 70cdac5cc9787719d548ac6d74c98896)
> **Banco de Dados**: Supabase (externo)
## Documentação ## Documentação
- [**Resumo de Projetos**](./projetos-resumo.md): Status, URLs, CI/CD e webhooks
- [Inventário de Serviços](./services_inventory.md): Tabela completa de apps, bancos, portas e domínios - [Inventário de Serviços](./services_inventory.md): Tabela completa de apps, bancos, portas e domínios
- [Guia de Deploy](./deployment_guide.md): Instruções passo a passo para deployment e manutenção - [API Coolify](./coolify-api.md): Endpoints e comandos da API
## Quick Start ## Quick Start
@ -89,7 +79,7 @@ O servidor Redbull é uma infraestrutura de desenvolvimento que hospeda múltipl
```bash ```bash
# Token de autenticação # Token de autenticação
TOKEN="fzz2xL0UUwpeVB1mObEwxE9GO8sTV8z2OHiOojmdf0e428be" TOKEN="2|tjaxz6z6osQUz6whMvNMw23kcBqgToQAvMjSLos347164da9"
URL="https://redbull.rede5.com.br/api/v1" URL="https://redbull.rede5.com.br/api/v1"
# Deploy de uma aplicação # Deploy de uma aplicação
@ -135,15 +125,29 @@ curl -s -X DELETE -H "X-Auth-Email: $CF_AUTH_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY
-d '{"purge_everything":true}' -d '{"purge_everything":true}'
``` ```
### CI/CD Automático via Webhook ### CI/CD Automático via Webhook do Coolify
Os webhooks GitHub estão configurados para deploy automático:
- Push na branch `dev` → Deploy automático no Coolify O Coolify recebe webhooks do GitHub para deploy automático. Configure no repositório GitHub (Settings → Webhooks):
- URL do webhook: `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=<APP_UUID>&secret=<SECRET>`
| App | Webhook URL | Secret |
|-----|-------------|--------|
| Backend | `https://redbull.rede5.com.br/api/v1/webhooks/deploy?uuid=iw4sow8s0kkg4cccsk08gsoo` | `oRTKAwXgHrIzIcXdVqa7BXPkzXVQAhwS` |
| Frontend | `https://redbull.rede5.com.br/api/v1/webhooks/deploy?uuid=ao8g40scws0w4cgo8coc8o40` | `H3CfQuM9Jnpv9Tl37WKupEMlaaIWFlU5` |
| Backoffice | `https://redbull.rede5.com.br/api/v1/webhooks/deploy?uuid=hg48wkw4wggwsswcwc8sooo4` | `wvREqj94RRX60113cYb2nMCZf0pmg662` |
| Seeder | `https://redbull.rede5.com.br/api/v1/webhooks/deploy?uuid=q4w48gos8cgssso00o8w8gck` | `sa08PAj2RwyjW5VNmqKX0Ac9s7zIMyH2` |
| q1food-backend | `http://185.194.141.70:8000/api/v1/webhooks/deploy?uuid=eosgwscc4g044c884k0ws4gc` | `d66bac276faa04631124559d77199d0e` |
| q1food-frontend | `http://185.194.141.70:8000/api/v1/webhooks/deploy?uuid=g8w440g0w0oowo8skss440wk` | `c512dc54933829d1cde381e2c9bf394a` |
Configuração do webhook no GitHub:
- Payload URL: URL do webhook acima
- Content type: `application/json`
- Secret: webhook secret da app
- Events: `Push events` (branch `dev`)
## Acesso SSH ## Acesso SSH
```bash ```bash
# Conexão SSH (HostName aponta para 185.194.141.70 direto) # Conexão SSH
ssh redbull ssh redbull
# Verificar containers # Verificar containers
@ -153,9 +157,6 @@ docker ps --format 'table {{.Names}}\t{{.Status}}'
docker logs coolify -f docker logs coolify -f
``` ```
> ⚠️ **Nota:** `redbull.rede5.com.br` está atrás do Cloudflare — porta 22 bloqueada.
> O SSH config usa o IP `185.194.141.70` direto para contornar isso.
## Boas Práticas ## Boas Práticas
1. **Variáveis de Ambiente**: Configure todas as variáveis antes do deploy 1. **Variáveis de Ambiente**: Configure todas as variáveis antes do deploy

View file

@ -5,177 +5,99 @@
https://redbull.rede5.com.br/api/v1 https://redbull.rede5.com.br/api/v1
``` ```
## Authorization ## Authentication
Use Bearer token authentication:
A API requer um token `Bearer` no header `Authorization`.
### Gerar Token
1. Acesse `Keys & Tokens` / `API tokens` no painel Coolify
2. Defina um nome para o token e clique em `Create New Token`
3. Copie o token (so aparece uma vez)
### Token Atual
```
fzz2xL0UUwpeVB1mObEwxE9GO8sTV8z2OHiOojmdf0e428be
```
> Salvo em `~/.ssh/coolify-redbull-token`
### Permissoes
- `read-only` (default): apenas leitura
- `read:sensitive`: leitura + dados sensiveis
- `view:sensitive`: visualizar dados sensiveis
- `*`: acesso total
## Endpoints Principais
### Applications
| Metodo | Endpoint | Descricao |
|--------|----------|-----------|
| GET | `/applications` | Listar aplicacoes |
| GET | `/applications/{uuid}` | Obter aplicacao |
| GET | `/applications/{uuid}/start` | Iniciar aplicacao |
| GET | `/applications/{uuid}/stop` | Parar aplicacao |
| GET | `/applications/{uuid}/restart` | Reiniciar aplicacao |
| DELETE | `/applications/{uuid}` | Deletar aplicacao |
| PATCH | `/applications/{uuid}` | Atualizar aplicacao |
### Databases
| Metodo | Endpoint | Descricao |
|--------|----------|-----------|
| GET | `/databases` | Listar bancos |
| GET | `/databases/{uuid}` | Obter banco |
| POST | `/databases/postgresql` | Criar PostgreSQL |
| POST | `/databases/mysql` | Criar MySQL |
| POST | `/databases/mariadb` | Criar MariaDB |
| POST | `/databases/mongodb` | Criar MongoDB |
| POST | `/databases/redis` | Criar Redis |
| GET | `/databases/{uuid}/start` | Iniciar banco |
| GET | `/databases/{uuid}/stop` | Parar banco |
| DELETE | `/databases/{uuid}` | Deletar banco |
### Deployments
| Metodo | Endpoint | Descricao |
|--------|----------|-----------|
| GET | `/deployments` | Listar deployments |
| GET | `/deploy?uuid={uuid}` | Deploy por UUID |
| GET | `/deploy?tag={tag}` | Deploy por tag |
### Projects
| Metodo | Endpoint | Descricao |
|--------|----------|-----------|
| GET | `/projects` | Listar projetos |
| POST | `/projects` | Criar projeto |
| GET | `/projects/{uuid}` | Obter projeto |
| PATCH | `/projects/{uuid}` | Atualizar projeto |
| DELETE | `/projects/{uuid}` | Deletar projeto |
### Servers
| Metodo | Endpoint | Descricao |
|--------|----------|-----------|
| GET | `/servers` | Listar servidores |
| GET | `/servers/{uuid}` | Obter servidor |
| GET | `/servers/{uuid}/domains` | Listar dominios |
## Exemplos
### Listar Aplicacoes
```bash ```bash
curl -s -H "Authorization: Bearer $TOKEN" \ curl -H "Authorization: Bearer $TOKEN" ...
"https://redbull.rede5.com.br/api/v1/applications"
``` ```
### Criar PostgreSQL Database Token: `2|tjaxz6z6osQUz6whMvNMw23kcBqgToQAvMjSLos347164da9`
## Quick Commands
### List all applications
```bash ```bash
curl -s -X POST -H "Authorization: Bearer $TOKEN" \ curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" "https://redbull.rede5.com.br/api/v1/applications"
-H "Content-Type: application/json" \
"https://redbull.rede5.com.br/api/v1/databases/postgresql" \
-d '{
"name": "my-database",
"description": "PostgreSQL database",
"project_uuid": "t0c4ss0wkcgwg48g088wkwgs",
"environment_name": "production",
"server_uuid": "m844o4gkwkwcc0k48swgs8c8",
"destination_uuid": "e0ggcws4gsw4w4wkgggkg8ow",
"postgres_user": "myuser",
"postgres_password": "mypassword",
"postgres_db": "mydb",
"image": "postgres:16-alpine",
"instant_deploy": true
}'
``` ```
### Deploy por UUID ### Deploy by UUID
```bash ```bash
curl -s -H "Authorization: Bearer $TOKEN" \ # Frontend
"https://redbull.rede5.com.br/api/v1/deploy?uuid={UUID}" curl -s -X GET -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/deploy?uuid=ao8g40scws0w4cgo8coc8o40"
# Backend
curl -s -X GET -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/deploy?uuid=iw4sow8s0kkg4cccsk08gsoo"
``` ```
### Atualizar Dominio ### Check deployment status
```bash ```bash
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" \ curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/deployments/{deployment_uuid}"
-H "Content-Type: application/json" \
"https://redbull.rede5.com.br/api/v1/applications/{UUID}" \
-d '{"domains":"https://meu-dominio.com"}'
``` ```
## UUIDs Importantes ### Start/Stop/Restart application
### Server
| Nome | UUID |
|------|------|
| localhost | `m844o4gkwkwcc0k48swgs8c8` |
### Destination (Docker Network)
| Nome | UUID |
|------|------|
| coolify | `e0ggcws4gsw4w4wkgggkg8ow` |
### Projects
| Nome | UUID |
|------|------|
| Infraestrutura | `t0c4ss0wkcgwg48g088wkwgs` |
| gohorsejobs | `gkgksco0ow4kgwo8ow4cgs8c` |
| rodiziosdaqui | `iooos0488ww0coccosc0sk4c` |
| obramarket | `scwgsc8k0w84gkc88c080ck4` |
| q1food | `x0os8c40o00ssoo84sokwosk` |
| q1agenda | `ews8s8o40wkok0cgc8w4wc04` |
| sextando | `xk40w0s88wkcoco0css8c8kk` |
| q1total | `e0gg00w4sksk88cwksgc4cwo` |
| openproject | `dc40gww0gg4kw88k4gk88sws` |
| saveinmed | `lgko4gcg0okk48k04kcww0gs` |
| zeus-suplementos | `xwkcw80w0sgk8g84wo08s8ks` |
### Applications (Importantes)
| Nome | UUID |
|------|------|
| q1site-frontend | `y0wccwwsko4socokk804s0ww` |
| q1total-backend | `e8w040ow8skssoscookowgck` |
| q1food-backend-dev | `eosgwscc4g044c884k0ws4gc` |
| q1food-frontend-dev | `g8w440g0w0oowo8skss440wk` |
| q1fit-dev | `fk8wswsk8ssswggcsoc0kgcc` |
| zeus-frontend | `t4c8w08ocg8socoo8cg00c48` |
### Environments (Infraestrutura)
| Nome | UUID |
|------|------|
| production | `sw4ckcgkgs0wssgs40os8s0g` |
## Variaveis de Ambiente
```bash ```bash
export COOLIFY_TOKEN="fzz2xL0UUwpeVB1mObEwxE9GO8sTV8z2OHiOojmdf0e428be" # Start
export COOLIFY_URL="https://redbull.rede5.com.br/api/v1" curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/applications/{uuid}/start"
export COOLIFY_SERVER="m844o4gkwkwcc0k48swgs8c8"
export COOLIFY_DESTINATION="e0ggcws4gsw4w4wkgggkg8ow" # Stop
curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/applications/{uuid}/stop"
# Restart
curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/applications/{uuid}/restart"
``` ```
## Script de Health Check ## Application UUIDs
| App | UUID |
|-----|------|
| gohorsejobs-backend-dev | iw4sow8s0kkg4cccsk08gsoo |
| gohorsejobs-backoffice-dev | hg48wkw4wggwsswcwc8sooo4 |
| gohorsejobs-frontend-dev | ao8g40scws0w4cgo8coc8o40 |
| gohorsejobs-seeder-dev | q4w48gos8cgssso00o8w8gck |
| rodiziosdaqui-backend-dev | ecso4woo4woo0ks84888k8ck |
| rodiziosdaqui-backoffice-dev | s4kskw08400wcw8g40ossggo |
| rodiziosdaqui-frontend-dev | l4g4cc884gc04kg8s480gs48 |
| rodiziosdaqui-seeder-dev | ko0kkw4kw8g80scksg8sk0wc |
| q1food-backend-dev | eosgwscc4g044c884k0ws4gc |
| q1food-frontend-dev | g8w440g0w0oowo8skss440wk |
## Scripts
### Health Check Script
```bash ```bash
#!/bin/bash #!/bin/bash
TOKEN="fzz2xL0UUwpeVB1mObEwxE9GO8sTV8z2OHiOojmdf0e428be" TOKEN=$(cat ~/.ssh/coolify-redbull-token | tr -d '\n')
curl -s -H "Authorization: Bearer $TOKEN" \ echo "=== Coolify Health Check ==="
"https://redbull.rede5.com.br/api/v1/applications" | jq -r '.[] | "\(.name): \(.status)"' curl -s -H "Authorization: Bearer $TOKEN" "http://185.194.141.70:8000/api/v1/applications" | python -c "
import json,sys
d = json.load(sys.stdin)
for a in d:
status = a.get('status','unknown')
icon = '✅' if 'running' in status else '❌'
print(f'{icon} {a[\"name\"]}: {status}')
"
``` ```
## Links ### Restart All Services
- [Coolify API Docs](https://coolify.io/docs/api-reference/authorization) ```bash
- [GitHub Issues](https://github.com/coollabsio/coolify/issues) #!/bin/bash
TOKEN=$(cat ~/.ssh/coolify-redbull-token | tr -d '\n')
UUIDS=(
"ao8g40scws0w4cgo8coc8o40"
"q4w48gos8cgssso00o8w8gck"
"iw4sow8s0kkg4cccsk08gsoo"
"hg48wkw4wggwsswcwc8sooo4"
"ecso4woo4woo0ks84888k8ck"
"s4kskw08400wcw8g40ossggo"
"l4g4cc884gc04kg8s480gs48"
"ko0kkw4kw8g80scksg8sk0wc"
)
for uuid in "${UUIDS[@]}"; do
echo "Deploying $uuid..."
curl -s -X GET -H "Authorization: Bearer $TOKEN" "http://185.194.141.70:8000/api/v1/deploy?uuid=$uuid"
done
```
## Useful Links
- Coolify Docs: https://coolify.io/docs
- API Reference: https://coolify.io/docs/api-reference/authorization

View file

@ -0,0 +1,173 @@
# Resumo de Projetos - Redbull (Coolify DEV)
> Atualizado: 2026-02-18
## Status Geral
| Projeto | Ambiente | Status | CI/CD | Webhook |
|---------|----------|:------:|:-----:|:-------:|
| GoHorseJobs | DEV | ✅ Running | ✅ GitHub Actions | ✅ Configurado |
| RodiziosDaqui | DEV | ✅ Running | ❌ Sem pipeline | ✅ Configurado |
| Q1Food | DEV | ❌ Unhealthy | ❌ Sem pipeline | ✅ Configurado |
| Obramarket | DEV | ❌ Unhealthy | ❌ Sem pipeline | ❌ Não configurado |
---
## GoHorseJobs DEV
### URLs
| Serviço | URL |
|---------|-----|
| Frontend | https://local.gohorsejobs.com |
| Backend | https://api-local.gohorsejobs.com |
| Backoffice | https://b-local.gohorsejobs.com |
| Seeder | sslip.io (interno) |
### CI/CD
| Workflow | Status | Trigger |
|----------|--------|---------|
| Deploy to Coolify DEV | ✅ Ativo | Push dev |
| Validate RSA and Run Migrations | ❌ Falhando | Push dev |
| Deploy Backend (Dev) | ✅ Ativo | Manual |
### Webhooks (Coolify)
| Serviço | UUID | Secret |
|---------|------|--------|
| Backend | `iw4sow8s0kkg4cccsk08gsoo` | `oRTKAwXgHrIzIcXdVqa7BXPkzXVQAhwS` |
| Frontend | `ao8g40scws0w4cgo8coc8o40` | `H3CfQuM9Jnpv9Tl37WKupEMlaaIWFlU5` |
| Backoffice | `hg48wkw4wggwsswcwc8sooo4` | `wvREqj94RRX60113cYb2nMCZf0pmg662` |
| Seeder | `q4w48gos8cgssso00o8w8gck` | `sa08PAj2RwyjW5VNmqKX0Ac9s7zIMyH2` |
### Repositórios
- GitHub: https://github.com/rede5/gohorsejobs
- Forgejo (mirror): https://pipe.gohorsejobs.com/bohessefm/gohorsejobs
- Branch: `dev`
---
## RodiziosDaqui DEV
### URLs
| Serviço | URL |
|---------|-----|
| Frontend | http://rodiziosdaqui.rede5.com.br |
| Backend | http://api.rodiziosdaqui.rede5.com.br |
| Backoffice | http://backoffice.rodiziosdaqui.rede5.com.br |
| Seeder | http://seeder.rodiziosdaqui.rede5.com.br |
### CI/CD
⚠️ **Sem pipeline configurado** - Deploy apenas via webhook manual
### Webhooks (Coolify)
| Serviço | UUID | Secret |
|---------|------|--------|
| Backend | `ecso4woo4woo0ks84888k8ck` | `ElwDbKAUgKRvKPJPCqig1v3IGc31cZvo` |
| Frontend | `l4g4cc884gc04kg8s480gs48` | `Rlz5bQyKJABIDWSZyEn30H1jvFMDF98k` |
| Backoffice | `s4kskw08400wcw8g40ossggo` | `jDMmawqSg1ObcrlSt85HZKvrnzUPwBpt` |
| Seeder | `ko0kkw4kw8g80scksg8sk0wc` | `7sCTpRSYtxsrAwFGBW5XCyiF4SR22mcn` |
### Repositório
- GitHub: https://github.com/rede5/rodiziosdaqui
- Branch: `dev`
### Banco de Dados
- PostgreSQL interno (Coolify): `rodiziosdaqui-dev`
- PostgreSQL externo (Absam): `db-60604.dc-us-1.absamcloud.com:11985`
### Ações Necessárias
1. Criar GitHub Actions workflow para deploy automático
2. Configurar webhooks no GitHub para disparar Coolify
---
## Q1Food DEV
### URLs
| Serviço | URL | Status |
|---------|-----|:------:|
| Frontend | http://dev.q1food.com | ❌ unhealthy |
| Backend | http://api-dev.q1food.com | ❌ unhealthy |
### CI/CD
❌ **Sem pipeline configurado**
### Webhooks (Coolify)
| Serviço | UUID | Secret |
|---------|------|--------|
| Backend | `eosgwscc4g044c884k0ws4gc` | `d66bac276faa04631124559d77199d0e` |
| Frontend | `g8w440g0w0oowo8skss440wk` | `c512dc54933829d1cde381e2c9bf394a` |
### Repositórios
- Backend: https://github.com/rede5/food-backend
- Frontend: https://github.com/rede5/food-frontend
- Branch: `dev`
### Ações Necessárias
1. Verificar erro no container (exited:unhealthy)
2. Criar GitHub Actions workflow
3. Configurar webhooks no GitHub
---
## Obramarket DEV
### Status
⚠️ **CRÍTICO** - 4 instâncias duplicadas, todas unhealthy
### URLs
| Serviço | URL | Status |
|---------|-----|--------|
| Backend (1) | sslip.io | ❌ exited:unhealthy |
| Backend (2) | sslip.io | ❌ exited:unhealthy |
| Backend (3) | sslip.io | ❌ exited:unhealthy |
| Backend (4) | sslip.io | ❌ exited:unhealthy |
### CI/CD
❌ **Sem pipeline configurado**
### Webhooks
❌ **Não configurado**
### Ações Necessárias
1. Remover instâncias duplicadas (manter apenas 1)
2. Configurar domínio próprio
3. Configurar webhook para deploy automático
4. Verificar erro no container
### Repositório
- GitHub: https://github.com/rede5/obramarket
- Branch: `main`
---
## Bancos de Dados (Coolify)
| Database | UUID | Status | Tipo |
|----------|------|:------:|------|
| gohorsejobs-dev | `bgws48os8wgwk08o48wg8k80` | ✅ healthy | PostgreSQL 16 |
| rodiziosdaqui-dev | `e8ogwk008c8s8sok8wc00ow0` | ✅ healthy | PostgreSQL 16 |
| vaultwarden | `sg0co4s44cs48o4kc4cgg844` | ✅ healthy | PostgreSQL 16 |
---
## Webhooks URL
Configurar no GitHub (Settings → Webhooks):
```
https://redbull.rede5.com.br/api/v1/webhooks/deploy?uuid={UUID}
```
Content-Type: `application/json`
Secret: webhook secret da aplicação
Events: `Push events`
---
## Links Úteis
- Coolify Dashboard: https://redbull.rede5.com.br
- Coolify API: https://redbull.rede5.com.br/api/v1
- GitHub Token: `~/.ssh/github-token`
- Coolify Token: `~/.ssh/coolify-redbull-token`
- Forgejo: https://pipe.gohorsejobs.com

View file

@ -1,208 +1,93 @@
# Inventário de Serviços Redbull # Inventário de Serviços Redbull
> Última atualização: 2026-02-28 — Limpeza de duplicatas + fixes de repo + deploy q1food > Última atualização: 2026-02-18
---
## Visão Geral ## Visão Geral
Servidor Redbull (`185.194.141.70`) gerenciado pelo Coolify v4. Proxy reverso: Traefik. Este documento apresenta o inventário completo de serviços hospedados no servidor Redbull (185.194.141.70), utilizando a plataforma Coolify como camada de gerenciamento de aplicações baseadas em contêineres.
**SSH:** Conexão funciona via IP diretamente (`ssh redbull` → aponta para `185.194.141.70:22`). ## Tabela de Aplicações
> ⚠️ `redbull.rede5.com.br` está atrás do Cloudflare — porta 22 bloqueada. SSH usa IP direto.
--- ### GoHorse Jobs (DEV)
## Aplicações — Status Completo | Aplicação | UUID | Porta | Domínio | Status |
|-----------|------|:-----:|---------|:------:|
### Q1 Agenda (DEV / HML) | **gohorsejobs-backend-dev** | `iw4sow8s0kkg4cccsk08gsoo` | 8521 | coolify-dev.gohorsejobs.com | ✅ Running |
| **gohorsejobs-frontend-dev** | `ao8g40scws0w4cgo8coc8o40` | 3000 | dev.gohorsejobs.com | ✅ Running |
| Aplicação | UUID | Domínio | Branch | CI/CD | Status | | **gohorsejobs-backoffice-dev** | `hg48wkw4wggwsswcwc8sooo4` | 3001 | backoffice-dev.gohorsejobs.com | ✅ Running |
|-----------|------|---------|--------|-------|:------:| | **gohorsejobs-seeder-dev** | `q4w48gos8cgssso00o8w8gck` | 8080 | seeder-dev.gohorsejobs.com | ✅ Running |
| **backend-dev** | `fk00s0cg8kgc0w4csswskgkw` | https://api-dev.q1agenda.com.br | dev | ❌ Sem webhook | ❌ Exited (aguardando envs) |
| **backend-hml** | `lkoc4coo48k0sow4c00gwo8k` | https://api-hml.q1agenda.com.br | hml | ❌ Sem webhook | ❌ Exited (aguardando envs) |
| **frontend-dev** | `jgook0gkss4cco0484co0s4o` | https://dev.q1agenda.com.br | main | ❌ Sem webhook | ✅ Running |
| **frontend-hml** | `lg4k0gk4gsog8ckcs8sw4cwc` | https://hml.q1agenda.com.br | main | ❌ Sem webhook | ✅ Running |
**Repo backend:** `git@github.com:rede5/q1agenda-backend.git` (branch `dev`/`hml`)
**Repo frontend:** `git@github.com:rede5/q1-agenda-frontend.git`
**Fix 2026-02-28:** Repo URL corrigido (`q1-agenda-backend` → `q1agenda-backend`), duplicata sslip deletada, envs Appwrite base adicionados.
**Pendente:** `APPWRITE_PROJECT_ID`, `APPWRITE_API_KEY`, `SECURITY_API_KEY`, `JWT_SECRET` — preencher no Coolify Dashboard antes de deployar.
---
### GoHorse Jobs (LOCAL / DEV)
| Aplicação | UUID | Domínio | Branch | CI/CD | Status |
|-----------|------|---------|--------|-------|:------:|
| **gohorsejobs-backend-local** | `iw4sow8s0kkg4cccsk08gsoo` | https://test2.q1agenda.com.br | dev | ✅ GitHub webhook | ✅ Running |
| **gohorsejobs-frontend-local** | `ao8g40scws0w4cgo8coc8o40` | https://local.gohorsejobs.com | dev | ✅ GitHub webhook | ✅ Running |
| **gohorsejobs-backoffice-local** | `hg48wkw4wggwsswcwc8sooo4` | https://b-local.gohorsejobs.com | dev | ✅ GitHub webhook | ✅ Running |
| **gohorsejobs-seeder-local** | `q4w48gos8cgssso00o8w8gck` | https://s-local.gohorsejobs.com | dev | ✅ GitHub webhook | ✅ Healthy |
**Repo:** `git@github.com:rede5/gohorsejobs.git`
**Fix 2026-02-28:** `gohorsejobs-frontend-dev` (sslip.io, sem domínio, sem envs) deletada — duplicata da `frontend-local`.
**Nota:** `gohorsejobs-backend-local` aponta para `test2.q1agenda.com.br` — revisar domínio.
---
### Rodizios Daqui (DEV) ### Rodizios Daqui (DEV)
| Aplicação | UUID | Domínio | Branch | CI/CD | Status | | Aplicação | UUID | Porta | Domínio | Status |
|-----------|------|---------|--------|-------|:------:| |-----------|------|:-----:|---------|:------:|
| **rodiziosdaqui-backend-dev** | `ecso4woo4woo0ks84888k8ck` | http://api.rodiziosdaqui.rede5.com.br | dev | ✅ GitHub webhook | ✅ Running | | **rodiziosdaqui-backend-dev** | `ecso4woo4woo0ks84888k8ck` | 8080 | api.rodiziosdaqui.rede5.com.br | ✅ Running |
| **rodiziosdaqui-frontend-dev** | `l4g4cc884gc04kg8s480gs48` | http://rodiziosdaqui.rede5.com.br | dev | ✅ GitHub webhook | ✅ Running | | **rodiziosdaqui-frontend-dev** | `l4g4cc884gc04kg8s480gs48` | 3000 | rodiziosdaqui.rede5.com.br | ✅ Running |
| **rodiziosdaqui-backoffice-dev** | `s4kskw08400wcw8g40ossggo` | http://backoffice.rodiziosdaqui.rede5.com.br | dev | ✅ GitHub webhook | ✅ Running | | **rodiziosdaqui-backoffice-dev** | `s4kskw08400wcw8g40ossggo` | 8082 | backoffice.rodiziosdaqui.rede5.com.br | ✅ Running |
| **rodiziosdaqui-seeder-dev** | `ko0kkw4kw8g80scksg8sk0wc` | http://seeder.rodiziosdaqui.rede5.com.br | dev | ✅ GitHub webhook | ✅ Healthy | | **rodiziosdaqui-seeder-dev** | `ko0kkw4kw8g80scksg8sk0wc` | 8081 | seeder.rodiziosdaqui.rede5.com.br | ✅ Healthy |
**Repo:** `git@github.com:rede5/rodiziosdaqui.git` ### Q1Food (DEV)
**Projeto mais completo e estável** — todos rodando com CI/CD configurado.
--- | Aplicação | UUID | Porta | Domínio | Status |
|-----------|------|:-----:|---------|:------:|
| **q1food-backend-dev** | `eosgwscc4g044c884k0ws4gc` | 8000 | api-dev.q1food.com | ❌ Unhealthy |
| **q1food-frontend-dev** | `g8w440g0w0oowo8skss440wk` | 3000 | dev.q1food.com | ❌ Unhealthy |
### Q1 Food (DEV) ### Obramarket (DEV)
| Aplicação | UUID | Domínio | Branch | CI/CD | Status | | Aplicação | UUID | Porta | Domínio | Status |
|-----------|------|---------|--------|-------|:------:| |-----------|------|:-----:|---------|:------:|
| **q1food-backend-dev** | `eosgwscc4g044c884k0ws4gc` | http://api-dev.q1food.com | dev | ✅ GitHub webhook | ❌ Exited | | **obramarket-backend-dev** | múltiplos | 8080 | sslip.io | ❌ Unhealthy |
| **q1food-frontend-dev** | `g8w440g0w0oowo8skss440wk` | http://dev.q1food.com | dev | ✅ GitHub webhook | ✅ Running |
**Repos:** `rede5/food-backend.git` / `rede5/food-frontend.git` ## Tabela de Bancos de Dados
**Issues:** Backend parado desde 18/02. CI/CD configurado mas deploy falhou.
--- | Serviço | UUID | Versão | Apps Vinculados | Porta |
|---------|------|:------:|-----------------|:-----:|
| **gohorsejobs-dev** | `bgws48os8wgwk08o48wg8k80` | PostgreSQL 16 | gohorsejobs-* | 5432 |
| **rodiziosdaqui-dev** | `e8ogwk008c8s8sok8wc00ow0` | PostgreSQL 16 | rodiziosdaqui-* | 5432 |
### Sextando (DEV) ## Webhooks GitHub
| Aplicação | UUID | Domínio | Branch | CI/CD | Status | ### GoHorse Jobs
|-----------|------|---------|--------|-------|:------:| | App | Webhook Secret |
| **sextando-backend** | `h8c4sg0sg80c4o8kkcw4ogcg` | https://api-dev.sextando.com.br | dev | ❌ Sem webhook | ❌ Exited | |-----|----------------|
| **sextando-backoffice** | `nsc0gw0og40g0c88soc0o0wc` | https://backoffice-dev.sextando.com.br | dev | ❌ Sem webhook | ❌ Exited | | Backend | `oRTKAwXgHrIzIcXdVqa7BXPkzXVQAhwS` |
| **sextando-frontend** | `vggkcs880o8go0ws84wcggco` | https://dev.sextando.com.br | dev | ❌ Sem webhook | ✅ Running | | Frontend | `H3CfQuM9Jnpv9Tl37WKupEMlaaIWFlU5` |
| Backoffice | `wvREqj94RRX60113cYb2nMCZf0pmg662` |
| Seeder | `sa08PAj2RwyjW5VNmqKX0Ac9s7zIMyH2` |
**Repo:** `git@github.com:rede5/sextando.git` ### Rodizios Daqui
**Build:** Frontend usa `nixpacks`, backend/backoffice usam `dockerfile` | App | Webhook Secret |
**Issues:** Nenhum dos 3 tem webhook CI/CD. Backend e backoffice parados. |-----|----------------|
| Backend | `ElwDbKAUgKRvKPJPCqig1v3IGc31cZvo` |
| Frontend | `Rlz5bQyKJABIDWSZyEn30H1jvFMDF98k` |
| Backoffice | `jDMmawqSg1ObcrlSt85HZKvrnzUPwBpt` |
| Seeder | `7sCTpRSYtxsrAwFGBW5XCyiF4SR22mcn` |
--- ### Q1Food
| App | Webhook Secret |
|-----|----------------|
| Backend | `d66bac276faa04631124559d77199d0e` |
| Frontend | `c512dc54933829d1cde381e2c9bf394a` |
### ObraMarket (DEV) ## Containers Docker Ativos
| Aplicação | UUID | Domínio | Branch | CI/CD | Status | | Container | Imagem | Porta | Notas |
|-----------|------|---------|--------|-------|:------:| |-----------|--------|:-----:|-------|
| **obramarket-backend-dev** | `ws08owk8ocog0gswg8ogo8c4` | sslip.io (sem domínio próprio) | main | ❌ Sem webhook | ❌ Exited | | coolify | ghcr.io/coollabsio/coolify:4.0.0-beta.463 | 8000 | Painel Coolify |
| coolify-db | postgres:15-alpine | 5432 | Banco Coolify |
**Repo:** `git@github.com:rede5/obramarket.git` | coolify-redis | redis:7-alpine | 6379 | Cache Coolify |
**Fix 2026-02-28:** 3 duplicatas deletadas (forgejo, IP interno, URL inválida). Envs base adicionados (SERVER_PORT, DB_PORT, etc.). | coolify-realtime | soketi | 6001 | WebSocket |
**Pendente:** `DB_HOST`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`, `JWT_SECRET`, `PEPPER_SECRET`, `ADMIN_EMAIL`, `ADMIN_PASSWORD`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` — preencher no Coolify + configurar domínio próprio. | coolify-sentinel | coolify-sentinel | 8888 | Monitoramento |
| coolify-proxy | traefik:v3.6 | 80/443 | Proxy reverso |
---
## Resumo de Status
| Projeto | Rodando | Parado | CI/CD | Apps Total |
|---------|:-------:|:------:|-------|:----------:|
| Q1Agenda | 2 | 2 | ❌ Nenhum | 4 |
| GoHorse Jobs | 4 | 0 | ✅ Todos | 4 |
| Rodizios Daqui | 4 | 0 | ✅ Todos | 4 |
| Q1Food | 1 | 1 🔄 | ✅ Ambos | 2 |
| Sextando | 3 | 0 | ❌ Nenhum | 3 |
| ObraMarket | 0 | 1 | ❌ Nenhum | 1 |
| Q1Total | 1 | 0 | ✅ | 1 |
| Q1Site | 1 | 0 | ✅ | 1 |
| SaveInMed | 2 | 0 | ✅ | 2 |
| **Total** | **18** | **4** | | **22** |
> 🔄 q1food-backend em deploy após refactor Supabase → PostgreSQL (2026-02-28)
---
## Bancos de Dados (Databases)
| Serviço | UUID | DB | Apps Vinculados | Status |
|---------|------|----|-----------------|:------:|
| **gohorsejobs-dev** | `bgws48os8wgwk08o48wg8k80` | PostgreSQL 16 | gohorsejobs-* | ✅ Healthy (10d) |
| **rodiziosdaqui-dev** | `e8ogwk008c8s8sok8wc00ow0` | PostgreSQL 16 | rodiziosdaqui-* | ✅ Healthy (10d) |
| **q1food-dev** | `vskkccs8880cg0ggckccsgww` | PostgreSQL | q1food-* | ✅ Healthy (8d) |
| **vaultwarden-db** | `sg0co4s44cs48o4kc4cgg844` | PostgreSQL | Vaultwarden | ✅ Healthy (8d) |
| **bookstack-db** | `mariadb-ogs4okgoosk48cswoo0s4k84` | MariaDB | BookStack | ✅ Up (3d) |
---
## Serviços (Coolify Services)
| Serviço | Domínio | Status |
|---------|---------|:------:|
| **BookStack** (`ogs4okgoosk48cswoo0s4k84`) | https://docs.rede5.com.br | ✅ Running (3d) |
| **Vaultwarden** (`h8wg0gogw44swosgk8wgc8cs`) | https://vault.rede5.com.br | ✅ Healthy |
| **Forgejo** (`zskgc8sck4w8g8w0o4w44o40`) | pipe.gohorsejobs.com | ⚠️ API=exited, Docker=Up 9d |
| **Supabase** (`hk0ogck8w0co0skc0c8g0ss0`) | - | ❌ Exited |
---
## Infraestrutura Coolify (containers internos)
| Container | Status |
|-----------|:------:|
| coolify | ✅ Up 9d (healthy) |
| coolify-proxy (Traefik) | ✅ Up 9d (healthy) |
| coolify-db | ✅ Up 10d (healthy) |
| coolify-redis | ✅ Up 10d (healthy) |
| coolify-realtime | ✅ Up 10d (healthy) |
| coolify-sentinel | ✅ Up 20h (healthy) |
---
## Conectividade SSH
| Método | Status | Observação |
|--------|:------:|------------|
| `ssh redbull` (via IP `185.194.141.70:22`) | ✅ OK | Funciona |
| `ssh root@redbull.rede5.com.br` (port 22) | ❌ Timeout | Cloudflare bloqueia porta 22 |
| Coolify API (`https://redbull.rede5.com.br`) | ✅ OK | Via Traefik HTTPS |
| Coolify API (`http://185.194.141.70:8000`) | ✅ OK | Acesso direto |
**SSH Config:** `~/.ssh/config` usa `HostName 185.194.141.70` (IP direto).
---
## Webhooks CI/CD
### GoHorse Jobs (`rede5/gohorsejobs` → branch `dev`)
| App | Webhook URL |
|-----|------------|
| Backend | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=iw4sow8s0kkg4cccsk08gsoo` |
| Frontend | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=ao8g40scws0w4cgo8coc8o40` |
| Backoffice | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=hg48wkw4wggwsswcwc8sooo4` |
| Seeder | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=q4w48gos8cgssso00o8w8gck` |
### Rodizios Daqui (`rede5/rodiziosdaqui` → branch `dev`)
| App | Webhook URL |
|-----|------------|
| Backend | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=ecso4woo4woo0ks84888k8ck` |
| Backoffice | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=s4kskw08400wcw8g40ossggo` |
| Frontend | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=l4g4cc884gc04kg8s480gs48` |
| Seeder | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=ko0kkw4kw8g80scksg8sk0wc` |
### Q1Food (`rede5/food-*` → branch `dev`)
| App | Webhook URL |
|-----|------------|
| Backend | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=eosgwscc4g044c884k0ws4gc` |
| Frontend | `https://redbull.rede5.com.br/api/v1/webhook/github?uuid=g8w440g0w0oowo8skss440wk` |
> **Secrets dos webhooks:** ver `services_inventory.md` seção anterior ou Coolify Dashboard.
---
## Portas do Sistema ## Portas do Sistema
| Porta | Serviço | | Porta | Serviço |
|:-----:|---------| |:-----:|---------|
| 22 | SSH (acesso via IP direto) | | 22 | SSH |
| 80 | Traefik HTTP | | 80 | Traefik (HTTP) |
| 443 | Traefik HTTPS | | 443 | Traefik (HTTPS) |
| 8000 | Coolify Dashboard | | 8000 | Coolify Dashboard |
| 22222 | Forgejo SSH | | 22222 | Forgejo SSH |
| 3000 | Frontends (Next.js) | | 3000 | Frontends (Next.js) |
@ -211,96 +96,72 @@ Servidor Redbull (`185.194.141.70`) gerenciado pelo Coolify v4. Proxy reverso: T
| 8081 | Seeders | | 8081 | Seeders |
| 8521 | GoHorse Backend | | 8521 | GoHorse Backend |
---
## Fixes Aplicados
### 2026-02-28
| App | Problema | Fix | Status |
|-----|----------|-----|:------:|
| **q1food-backend** | Migração Supabase → PostgreSQL incompleta (app crashava no startup) | 19 arquivos reescritos via GitHub API (branch `dev`): config, database, 7 models, 6 services, migrations/init.sql + Dockerfile uploads dir + fix postgres:// → ✅ Running | ✅ OK |
| **q1agenda-backend** (dev+hml) | Repo URL errado (`q1-agenda-backend` → `q1agenda-backend`), duplicata sslip.io, sem envs | PATCH Coolify repo URL, branch dev/hml, DELETE duplicata, envs Appwrite base adicionados | ⚠️ Aguarda credenciais |
| **obramarket-backend** | 4 duplicatas com repos inconsistentes (forgejo, IP, inválido, github) | DELETE 3 duplicatas, envs base adicionados | ⚠️ Aguarda credenciais DB |
| **gohorsejobs-frontend-dev** | Duplicata sem domínio/envs (sslip.io) da frontend-local | DELETE do Coolify | ✅ Limpo |
### 2026-02-26
| App | Problema | Fix | Status |
|-----|----------|-----|:------:|
| **q1food-backend** | Submodulo `.gitmodules` auto-referenciando o próprio repo via HTTPS | Deletado `.gitmodules` via GitHub API (commit `432e07d`) | ✅ Build OK |
| **sextando-backend** | `schema.sql` não copiado para container final (multi-stage) | Adicionado `COPY --from=builder /app/internal/database/schema.sql` no Dockerfile (commit `61d6b98`) | ✅ UP |
| **sextando-backend** | `base_directory=/` causava contexto errado (Go files em `/backend/`) | PATCH Coolify: `base_directory=/backend`, `dockerfile=/Dockerfile` | ✅ Fixado |
| **sextando-backoffice** | `base_directory=/` contexto errado (`prisma/` não encontrado) | PATCH Coolify: `base_directory=/backoffice`, `dockerfile=/Containerfile` | ✅ Fixado |
| **sextando-backoffice** | Containerfile usava `npm ci` sem `package-lock.json` (projeto usa pnpm) | Trocado para `npm install` (commit `84ca0d7`) | ✅ Build OK |
| **sextando-backoffice** | `dist/main` não encontrado — `tsconfig.json` sem `rootDir` → saída em `dist/src/main`. Prisma sem `DATABASE_URL` | CMD → `node dist/src/main` + `openssl` no final stage + `DATABASE_URL` configurada | ✅ UP |
## Issues Pendentes (Ação Manual Necessária)
| # | Problema | Severidade | Ação |
|---|----------|:----------:|------|
| 1 | **q1agenda-backend** (dev+hml): faltam credenciais Appwrite | 🔴 Alta | Coolify → App → Envs: `APPWRITE_PROJECT_ID`, `APPWRITE_API_KEY`, `SECURITY_API_KEY`, `JWT_SECRET` → Deploy |
| 2 | **obramarket-backend**: faltam credenciais DB + S3 + JWT | 🔴 Alta | Coolify → App → Envs: `DB_HOST/USER/PASSWORD/NAME`, `JWT_SECRET`, `PEPPER_SECRET`, `AWS_ACCESS_KEY_ID/SECRET` → Deploy |
| 3 | **obramarket-backend**: sem domínio próprio (sslip.io) | 🟡 Média | Coolify UI → App → Domains → adicionar `api-dev.obramarket.com.br` |
| 4 | **Sextando: nenhum app tem webhook CI/CD** | 🟡 Média | Configurar webhook GitHub para todos (ver URLs abaixo) |
| 5 | **SSH hostname bloqueado** — porta 22 inacessível via `redbull.rede5.com.br` | 🟢 Baixa | Cloudflare bloqueia porta 22 — SSH config usa IP `185.194.141.70` |
| 6 | **gohorsejobs-backend-local** usa domínio `test2.q1agenda.com.br` | 🟢 Baixa | Revisar domínio para `api-local.gohorsejobs.com` |
### q1agenda-backend — Envs pendentes
```bash
APPWRITE_PROJECT_ID=<id_do_projeto_appwrite>
APPWRITE_API_KEY=<api_key_server>
SECURITY_API_KEY=<chave_interna>
JWT_SECRET=<segredo_jwt>
```
### obramarket-backend — Envs pendentes
```bash
DB_HOST=<host_postgres>
DB_USER=<usuario>
DB_PASSWORD=<senha>
DB_NAME=obramarket_dev
JWT_SECRET=<segredo>
PEPPER_SECRET=<pepper>
ADMIN_EMAIL=admin@obramarket.com.br
ADMIN_PASSWORD=<senha_inicial>
AWS_ACCESS_KEY_ID=<key>
AWS_SECRET_ACCESS_KEY=<secret>
```
### Sextando — Webhooks CI/CD a configurar no GitHub
```
# Repositório: github.com/rede5/sextando → Settings → Webhooks
Backend URL: https://redbull.rede5.com.br/api/v1/webhook/github?uuid=h8c4sg0sg80c4o8kkcw4ogcg
Backoffice URL: https://redbull.rede5.com.br/api/v1/webhook/github?uuid=nsc0gw0og40g0c88soc0o0wc
Frontend URL: https://redbull.rede5.com.br/api/v1/webhook/github?uuid=vggkcs880o8go0ws84wcggco
```
---
## Comandos Rápidos ## Comandos Rápidos
### SSH Access
```bash ```bash
# SSH (usar IP direto)
ssh redbull ssh redbull
# Ver todos os containers
ssh redbull "docker ps --format 'table {{.Names}}\t{{.Status}}'"
# Ver logs de um app
ssh redbull "docker logs <container_name> --tail 50"
# Coolify API
TOKEN=$(cat ~/.ssh/coolify-redbull-token)
URL="https://redbull.rede5.com.br/api/v1"
# Listar apps com status
curl -s -H "Authorization: Bearer $TOKEN" "$URL/applications" | \
node -e "const c=[]; process.stdin.on('data',d=>c.push(d)); process.stdin.on('end',()=>{ JSON.parse(c.join('')).forEach(a=>console.log(a.status+' | '+a.name+' | '+a.fqdn)); })"
# Deploy por UUID
curl -s -H "Authorization: Bearer $TOKEN" "$URL/deploy?uuid=<UUID>"
# Reiniciar app
curl -s -H "Authorization: Bearer $TOKEN" "$URL/applications/<UUID>/restart"
``` ```
### Check All Containers
```bash
ssh redbull "docker ps -a"
```
### Health Check via API
```bash
TOKEN="2|tjaxz6z6osQUz6whMvNMw23kcBqgToQAvMjSLos347164da9"
curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/applications"
```
### Deploy Application
```bash
# Via UUID
TOKEN="2|tjaxz6z6osQUz6whMvNMw23kcBqgToQAvMjSLos347164da9"
curl -s -X GET -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/deploy?uuid={UUID}"
```
### View Logs
```bash
ssh redbull "docker logs {container_name}"
```
## Script de Health Check Automático
Execute no terminal local:
```bash
#!/bin/bash
echo "=== Redbull Health Check ==="
echo ""
# Check containers
echo "📦 Containers:"
ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no redbull "docker ps --format '{{.Names}}:{{.Status}}'" 2>/dev/null | while read line; do
if echo "$line" | grep -q "healthy\|Up"; then
echo " ✅ $line"
else
echo " ❌ $line"
fi
done
echo ""
echo "🌐 Aplicações Coolify:"
TOKEN="2|tjaxz6z6osQUz6whMvNMw23kcBqgToQAvMjSLos347164da9"
curl -s -H "Authorization: Bearer $TOKEN" "https://redbull.rede5.com.br/api/v1/applications" 2>/dev/null | python -c "
import json,sys
try:
d = json.load(sys.stdin)
for a in d:
status = a.get('status','unknown')
icon = '✅' if 'running' in status else '❌'
print(f' {icon} {a[\"name\"]}: {status}')
except: print(' Erro ao buscar dados')
"
```
## Issues Conhecidos
1. ~~gohorsejobs-seeder-dev~~ - ✅ CORRIGIDO
2. ~~gohorsejobs-frontend-dev~~ - ✅ CORRIGIDO (erro de tipagem TypeScript)
3. SSL: Domínios ainda sem HTTPS configurado
4. S3: Rodiziosdaqui backend sem S3 configurado (uploads desabilitados)