faef9b47dc
Dify foi removido 06-03-2026. Skills brainstorm/discover ainda referenciam-no no corpo. Bump v1.2 + nota top-of-file. Reescrita workflow para próxima sessão. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
355 lines
13 KiB
Markdown
355 lines
13 KiB
Markdown
---
|
|
name: easypanel-api
|
|
description: Referência completa da API tRPC oficial do EasyPanel — todos os endpoints para projectos, serviços, deploy e configurações.
|
|
---
|
|
|
|
# /easypanel-api - API Oficial EasyPanel
|
|
|
|
Referência da API tRPC do EasyPanel. Baseado em engenharia reversa do SDK oficial.
|
|
|
|
---
|
|
|
|
## Autenticação
|
|
|
|
```bash
|
|
TOKEN=$(cat /etc/easypanel/.api-token)
|
|
# Header obrigatório em todos os pedidos:
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
**Base URL:** `http://localhost:3000/api/trpc/`
|
|
|
|
Usar sempre via SSH localhost. Nunca expor externamente.
|
|
|
|
---
|
|
|
|
## Formato de Pedidos
|
|
|
|
| Método | Quando usar | Formato |
|
|
|--------|-------------|---------|
|
|
| GET | Leitura (list, inspect, get) | `?input=URL_ENCODED_JSON` |
|
|
| POST | Escrita (create, update, deploy, destroy) | `-d '{"json":{...}}'` com `Content-Type: application/json` |
|
|
|
|
```bash
|
|
# GET
|
|
curl -s "http://localhost:3000/api/trpc/ENDPOINT?input=URL_ENCODED_JSON" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
# POST
|
|
curl -s -X POST "http://localhost:3000/api/trpc/ENDPOINT" \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"json":{...}}'
|
|
```
|
|
|
|
---
|
|
|
|
## Projects API
|
|
|
|
| Endpoint | Tipo | Descrição |
|
|
|----------|------|-----------|
|
|
| `projects.listProjects` | GET | Listar todos os projectos |
|
|
| `projects.listProjectsAndServices` | GET | Projectos + todos os serviços |
|
|
| `projects.createProject` | POST | Criar projecto (`name`) |
|
|
| `projects.destroyProject` | POST | Destruir projecto (`projectName`) |
|
|
| `projects.inspectProject` | GET | Inspecionar projecto (`projectName`) |
|
|
|
|
```bash
|
|
# Listar projectos (teste rápido de autenticação)
|
|
curl -s "http://localhost:3000/api/trpc/projects.listProjects" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
# Criar projecto
|
|
curl -s -X POST "http://localhost:3000/api/trpc/projects.createProject" \
|
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
-d '{"json":{"name":"novo-projecto"}}'
|
|
```
|
|
|
|
---
|
|
|
|
## Services API
|
|
|
|
Endpoints completos com exemplos curl: ver [references/services-api.md](references/services-api.md)
|
|
|
|
**Verificados 12-03-2026** (engenharia reversa do backend.js minificado):
|
|
|
|
| Endpoint | Tipo | Descrição |
|
|
|----------|------|-----------|
|
|
| `services.app.createService` | POST | Criar serviço app |
|
|
| `services.postgres.createService` | POST | Criar PostgreSQL |
|
|
| `services.mysql.createService` | POST | Criar MySQL |
|
|
| `services.redis.createService` | POST | Criar Redis |
|
|
| `services.app.inspectService` | GET | Inspecionar serviço |
|
|
| `services.app.deployService` | POST | Fazer deploy |
|
|
| `services.app.stopService` | POST | Parar serviço |
|
|
| `services.app.startService` | POST | Iniciar serviço |
|
|
| `services.app.restartService` | POST | Reiniciar serviço |
|
|
| `services.app.destroyService` | POST | Destruir serviço |
|
|
|
|
**Endpoints que NAO existem** (removidos da versao anterior):
|
|
`enableService`, `disableService` — usar `startService`/`stopService` em vez disso.
|
|
|
|
**Parâmetros obrigatórios:** `projectName` + `serviceName` em todos os endpoints de serviços.
|
|
|
|
**Regra Descomplicar:** `projectName:"descomplicar"` para serviços próprios.
|
|
|
|
---
|
|
|
|
## Service Configuration API
|
|
|
|
Endpoints completos com exemplos curl: ver [references/service-config-api.md](references/service-config-api.md)
|
|
|
|
**Verificados 12-03-2026:**
|
|
|
|
| Endpoint | Descrição | Verificado |
|
|
|----------|-----------|:----------:|
|
|
| `services.app.updateSourceGithub` | Source GitHub (`owner`, `repo`, `ref`, `path`) | Sim |
|
|
| `services.app.updateSourceGit` | Source Git custom (`repo`, `ref`, `path`) — params no nivel raiz, NAO dentro de `source` | Sim |
|
|
| `services.app.updateSourceImage` | Source Docker image (`image`) | Sim |
|
|
| `services.app.updateEnv` | Variáveis de ambiente (`env` como string multi-linha) | Sim |
|
|
| `services.app.updateBuild` | Build config (`type`, `buildCommand`, `startCommand`) | Sim |
|
|
| `services.app.updateResources` | CPU/RAM (`memoryLimit`, `cpuLimit`, etc.) | Sim |
|
|
| `services.app.updateAdvanced` | Deploy avançado (`replicas`, `command`, `zeroDowntime`) | Sim |
|
|
| `services.app.updateRedirects` | Redireccionamentos HTTP | Sim |
|
|
| `services.app.updateBasicAuth` | Autenticacao basica | Sim |
|
|
| `services.app.updateMaintenance` | Modo manutencao | Sim |
|
|
|
|
**Endpoints que NAO existem na versao instalada:**
|
|
- ~~`services.app.updateDomains`~~ — dominios so via UI do EasyPanel
|
|
- ~~`services.app.updateMounts`~~ — nao encontrado
|
|
- ~~`services.app.updatePorts`~~ — nao encontrado
|
|
|
|
---
|
|
|
|
## Domains API (Verificado 12-03-2026)
|
|
|
|
Os dominios NAO sao geridos por `services.app.*` mas sim pelo namespace `domains.*`.
|
|
|
|
| Endpoint | Tipo | Descricao |
|
|
|----------|------|-----------|
|
|
| `domains.listDomains` | GET | Listar dominios do projecto (`projectName`) |
|
|
| `domains.createDomain` | POST | Criar dominio |
|
|
| `domains.updateDomain` | POST | Actualizar dominio |
|
|
| `domains.deleteDomain` | POST | Remover dominio |
|
|
|
|
```bash
|
|
# Listar dominios
|
|
INPUT='{"json":{"projectName":"descomplicar"}}'
|
|
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$INPUT'))")
|
|
curl -s "http://localhost:3000/api/trpc/domains.listDomains?input=$ENCODED" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
# Criar dominio para servico
|
|
curl -s -X POST "http://localhost:3000/api/trpc/domains.createDomain" \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"json":{
|
|
"id":"meu-dominio-id",
|
|
"https":true,
|
|
"host":"app.descomplicar.pt",
|
|
"path":"/",
|
|
"middlewares":[],
|
|
"certificateResolver":"letsencrypt",
|
|
"wildcard":false,
|
|
"destinationType":"service",
|
|
"serviceDestination":{
|
|
"protocol":"http",
|
|
"port":3000,
|
|
"path":"/",
|
|
"projectName":"descomplicar",
|
|
"serviceName":"meu-servico"
|
|
}
|
|
}}'
|
|
```
|
|
|
|
**Nota:** O `id` pode ser qualquer string unica (cuid ou slug). O `certificateResolver` deve ser `"letsencrypt"` para HTTPS com certificado automatico, ou `""` para dominios internos.
|
|
|
|
---
|
|
|
|
## Monitor API
|
|
|
|
```bash
|
|
# Stats do sistema
|
|
curl -s "http://localhost:3000/api/trpc/monitor.getSystemStats" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
# Stats de serviço (URL encode necessário)
|
|
# input: {"json":{"projectName":"descomplicar","serviceName":"api"}}
|
|
curl -s "http://localhost:3000/api/trpc/monitor.getServiceStats?input=..." \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
# Stats Docker tasks
|
|
curl -s "http://localhost:3000/api/trpc/monitor.getDockerTaskStats" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
## Logs API
|
|
|
|
```bash
|
|
# Logs de serviço (tail=100)
|
|
# input: {"json":{"projectName":"descomplicar","serviceName":"api","tail":100}}
|
|
curl -s "http://localhost:3000/api/trpc/logs.getServiceLogs?input=..." \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
## Settings API
|
|
|
|
| Endpoint | Tipo | Descrição |
|
|
|----------|------|-----------|
|
|
| `settings.getServerIp` | GET | IP do servidor |
|
|
| `settings.getPanelDomain` | GET | Domínio do painel |
|
|
| `settings.setPanelDomain` | POST | Definir domínio (`domain`) |
|
|
| `settings.getLetsEncryptEmail` | GET | Email Let's Encrypt |
|
|
| `settings.setLetsEncryptEmail` | POST | Definir email (`email`) |
|
|
| `settings.restartTraefik` | POST | Reiniciar Traefik |
|
|
| `settings.restartEasypanel` | POST | Reiniciar EasyPanel |
|
|
| `settings.pruneDockerImages` | POST | Limpar imagens Docker |
|
|
|
|
---
|
|
|
|
## URL Encoding para GET
|
|
|
|
```bash
|
|
INPUT='{"json":{"projectName":"descomplicar","serviceName":"api"}}'
|
|
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$INPUT'))")
|
|
curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=$ENCODED" \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
## Wrapper Script
|
|
|
|
Criar `/usr/local/bin/easypanel-api`:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
TOKEN=$(cat /etc/easypanel/.api-token)
|
|
BASE="http://localhost:3000/api/trpc"
|
|
|
|
if [ -z "$2" ]; then
|
|
curl -s "$BASE/$1" -H "Authorization: Bearer $TOKEN"
|
|
else
|
|
curl -s -X POST "$BASE/$1" \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$2"
|
|
fi
|
|
```
|
|
|
|
Uso: `easypanel-api projects.listProjects`
|
|
|
|
---
|
|
|
|
## Checklist de Uso
|
|
|
|
- [ ] SSH para servidor `easy`
|
|
- [ ] `TOKEN=$(cat /etc/easypanel/.api-token)`
|
|
- [ ] Testar: `curl -s ".../projects.listProjects" -H "Authorization: Bearer $TOKEN"`
|
|
- [ ] Usar endpoints conforme documentado
|
|
- [ ] Validar resposta em `result.data.json`
|
|
|
|
---
|
|
|
|
## Limitações de Mounts
|
|
|
|
O EasyPanel usa Docker Swarm internamente. Isto impõe limitações nos mounts que diferem do Docker standalone.
|
|
|
|
### Tipos de mount suportados
|
|
|
|
| Tipo | API `type` | Comportamento | Limitações |
|
|
|------|-----------|---------------|------------|
|
|
| **Volume mount** | `volume` | Volume gerido pelo Docker Swarm | Funciona bem. Dados persistem no nó. Sem acesso directo ao filesystem do host |
|
|
| **Bind mount** | `bind` | Monta directório do host no container | **Limitado no Swarm** — só funciona se o serviço estiver fixo a um nó específico |
|
|
| **File mount** | `file` | Monta ficheiro individual (config) | Funciona. Ideal para ficheiros de configuração |
|
|
|
|
### Limitações críticas
|
|
|
|
1. **Bind mounts no Swarm** — O Docker Swarm não garante que o container corra sempre no mesmo nó. Um bind mount (`type: "bind"`) aponta para um path no host, mas se o container migrar para outro nó, esse path pode não existir. O EasyPanel mitiga isto parcialmente por correr num único nó, mas a limitação arquitectural mantém-se.
|
|
|
|
2. **Sem suporte nativo a NFS/CIFS** — O EasyPanel nao suporta drivers de volume remotos (NFS, CIFS, etc.) directamente via API. Para volumes partilhados, e necessario criar o volume Docker manualmente com o driver adequado e depois referencia-lo.
|
|
|
|
3. **Permissões** — Volumes criados via API pertencem a `root:root` por defeito. Containers que correm com utilizador não-root podem ter problemas de permissões. Workaround: usar `command` no `updateAdvanced` para corrigir permissões no arranque.
|
|
|
|
4. **Substituicao total** — Mounts sao geridos via UI do EasyPanel. O endpoint `updateMounts` NAO foi encontrado na versao instalada. Configurar mounts directamente na interface web.
|
|
|
|
### Workarounds conhecidos
|
|
|
|
```bash
|
|
# 1. Obter mounts actuais antes de adicionar novo
|
|
INSPECT=$(curl -s "http://localhost:3000/api/trpc/services.app.inspectService?input=..." \
|
|
-H "Authorization: Bearer $TOKEN")
|
|
# Extrair mounts existentes do JSON e adicionar o novo
|
|
|
|
# 2. Volume com permissões corrigidas — usar initCommand
|
|
curl -s -X POST "http://localhost:3000/api/trpc/services.app.updateAdvanced" \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"json":{
|
|
"projectName":"descomplicar",
|
|
"serviceName":"minha-api",
|
|
"deploy":{
|
|
"command":"chown -R 1000:1000 /app/data && node dist/index.js"
|
|
}
|
|
}}'
|
|
|
|
# 3. Volume Docker manual com driver NFS (fora da API)
|
|
docker volume create --driver local \
|
|
--opt type=nfs \
|
|
--opt o=addr=192.168.1.10,rw \
|
|
--opt device=:/export/data \
|
|
vol-nfs-data
|
|
# Depois referenciar na API:
|
|
# {"type":"volume","name":"vol-nfs-data","mountPath":"/app/data"}
|
|
```
|
|
|
|
### Recomendações
|
|
|
|
- **Preferir `volume`** sobre `bind` — mais portável e compatível com Swarm
|
|
- **Usar `file`** para configs individuais (nginx.conf, .env, etc.)
|
|
- **Inspeccionar antes de actualizar** — a API substitui a lista inteira de mounts
|
|
- **Documentar volumes manuais** — volumes criados fora da API não aparecem no painel EasyPanel até serem referenciados por um serviço
|
|
|
|
---
|
|
|
|
## Seguranca
|
|
|
|
**ATENCAO:** `services.app.inspectService` retorna TODAS as variaveis de ambiente em texto limpo, incluindo passwords, tokens API e credenciais de base de dados. Nunca incluir output bruto de `inspectService` em reports, logs ou comentarios. Sanitizar sempre antes de mostrar ao utilizador.
|
|
|
|
Campos sensiveis tipicos no `env`: `DB_PASS`, `API_TOKEN`, `SERVER_PASS`, `GATEWAY_PASS`, `EASYPANEL_API_TOKEN`.
|
|
|
|
## Anti-Patterns
|
|
|
|
| Anti-Pattern | Risco | Alternativa |
|
|
|--------------|-------|-------------|
|
|
| Expor API externamente | Seguranca critica | Usar apenas via SSH localhost |
|
|
| Hardcode token | Leak de credenciais | Usar `/etc/easypanel/.api-token` |
|
|
| Nao validar response | Erros silenciosos | Verificar `result.data.json` |
|
|
| POST sem `Content-Type` | Request falha | Sempre incluir header |
|
|
| Mostrar output de inspectService | Expoe passwords em texto limpo | Sanitizar env vars |
|
|
|
|
---
|
|
|
|
## Ficheiros de Referência
|
|
|
|
| Ficheiro | Conteúdo |
|
|
|----------|----------|
|
|
| [references/services-api.md](references/services-api.md) | Endpoints de servicos com curl completo (create, inspect, deploy, stop/start/restart, destroy) |
|
|
| [references/service-config-api.md](references/service-config-api.md) | Endpoints de configuracao (source, env, resources, build, deploy, domains, backup BD) |
|
|
|
|
---
|
|
|
|
## Healing Log
|
|
|
|
Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.
|
|
|
|
```jsonl
|
|
{"date":"","issue":"","fix":"","source":"user|auto"}
|
|
```
|
|
|
|
*Adicionar nova linha após cada erro corrigido.*
|