panel-api

Backend API панели

Backend API живёт в репозитории duck-obsidian:

/home/supervisor/leonid_projects/duck-obsidian/.deploy/api

Он слушает:

127.0.0.1:3017

Панель duck-obsidian-panel ходит в него через Nginx /api/*.

Зачем нужен API

Браузер не должен запускать shell-команды напрямую.

Поэтому схема такая:

browser UI
  -> nginx /api/*
  -> backend API 127.0.0.1:3017
  -> gardenctl/systemctl/docker/nginx/certbot

Env-файл

/etc/duck-garden-panel.env

Пример:

PORT=3017
DUCK_PANEL_TOKEN=long-random-token
DUCK_RUNNER_DIR=/home/supervisor/leonid_projects/duck-obsidian/.deploy
DUCK_GARDENS_BASE_DIR=/home/supervisor/leonid_projects/obsidians
DUCK_GARDEN_DOMAIN_ROOT=obsidians.otc.guru
DUCK_GARDEN_GITHUB_OWNER=leo-mmmmmmm
DUCK_GARDEN_ENGINE_VERSION=1.0.18
DUCK_GARDEN_PORT_START=8089
DUCK_GARDEN_PORT_END=8999
DUCK_CERTBOT_EMAIL=leo.mikhaylov.2000@gmail.com
DUCK_GARDEN_AUTO_HTTPS=true
DUCK_GITHUB_TOKEN=

Проверить service

sudo systemctl status duck-garden-panel-api --no-pager
sudo journalctl -u duck-garden-panel-api -n 100 --no-pager

Перезапустить:

sudo systemctl restart duck-garden-panel-api

Healthcheck

curl -s http://127.0.0.1:3017/health

Ожидаемо:

{"ok":true}

Авторизованный запрос

TOKEN="$(sudo awk -F= '$1=="DUCK_PANEL_TOKEN"{print $2}' /etc/duck-garden-panel.env)"

curl -s \
  -H "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:3017/api/gardens

Основные endpoints

GET    /api/gardens
POST   /api/gardens
PATCH  /api/gardens/:slug
POST   /api/gardens/:slug/enable
POST   /api/gardens/:slug/disable
POST   /api/gardens/:slug/rebuild
POST   /api/gardens/:slug/https
DELETE /api/gardens/:slug
GET    /api/system/doctor
GET    /health

Access-control endpoints:

GET    /api/gardens/:slug/access
PATCH  /api/gardens/:slug/access
POST   /api/gardens/:slug/access/users
POST   /api/gardens/:slug/access/users/:userId/password
PATCH  /api/gardens/:slug/access/users/:userId
DELETE /api/gardens/:slug/access/users/:userId

GitHub repo check:

GET /api/github/repos/:owner/:repo

Что делает POST /api/gardens

Payload:

{
  "name": "Test Garden",
  "slug": "test-garden",
  "repoName": "test-garden",
  "accentColor": "#f5c542"
}

API:

1. Валидирует slug и accentColor.
2. Выбирает свободный port из диапазона.
3. При наличии DUCK_GITHUB_TOKEN создаёт GitHub repo, если его нет.
4. Запускает gardenctl create.
5. Сохраняет garden в catalog.
6. Если переданы name/accentColor/logoSvg, обновляет garden.config.json и запускает rebuild.

Где хранится catalog

/home/supervisor/leonid_projects/obsidians/.catalog/gardens.json
/home/supervisor/leonid_projects/obsidians/.catalog/access.json

Если catalog пустой, API умеет собрать список gardens из файловой системы в /home/supervisor/leonid_projects/obsidians.

Enable / disable

Enable:

POST /api/gardens/:slug/enable

Делает:

sudo systemctl enable --now <slug>-pull.timer
docker compose up -d caddy

Disable:

POST /api/gardens/:slug/disable

Делает:

sudo systemctl disable --now <slug>-pull.timer
docker compose stop caddy

Rebuild

POST /api/gardens/:slug/rebuild

Делает:

cd /home/supervisor/leonid_projects/duck-obsidian/.deploy
./gardenctl rebuild <slug>

HTTPS

POST /api/gardens/:slug/https

Делает:

sudo certbot --nginx -d <domain> --non-interactive --agree-tos

Частые проблемы

DUCK_PANEL_TOKEN is not set on server

В /etc/duck-garden-panel.env не задан DUCK_PANEL_TOKEN или service не перечитал env.

sudo systemctl restart duck-garden-panel-api
sudo journalctl -u duck-garden-panel-api -n 100 --no-pager

401 Unauthorized

Nginx не прокинул правильный Bearer token.

Проверить token напрямую:

TOKEN="$(sudo awk -F= '$1=="DUCK_PANEL_TOKEN"{print $2}' /etc/duck-garden-panel.env)"
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:3017/api/gardens

Если напрямую работает, проверять nginx config панели.

Нет свободных ports

API ищет свободный port в диапазоне:

DUCK_GARDEN_PORT_START=8089
DUCK_GARDEN_PORT_END=8999

Проверить занятые порты:

sudo ss -ltnp