panel-build-and-deploy

Админская панель: запуск и выкладка

duck-obsidian-panel — это frontend-часть админки. Она собирается как static site и отдаётся через Nginx.

Панель сама не запускает shell-команды. Она ходит в backend API, а API уже вызывает gardenctl.

Локальный запуск

git clone git@github.com:leo-mmmmmmm/duck-obsidian-panel.git
cd duck-obsidian-panel
npm ci
npm run dev

Проверка типов:

npm run typecheck

Production build:

npm run build

После static export должен появиться:

out/

Сборка на сервере

cd /home/supervisor/leonid_projects/duck-obsidian-panel
npm ci
npm run build

Выложить static export:

sudo rm -rf /var/www/duck-obsidian-panel
sudo mkdir -p /var/www/duck-obsidian-panel
sudo cp -a out/. /var/www/duck-obsidian-panel/

Nginx для панели

Панель отдаётся как static files, а /api/* проксируется на backend API:

server {
    listen 80;
    server_name admin.obsidians.otc.guru;

    root /var/www/duck-obsidian-panel;
    index index.html;

    auth_basic "Duck Garden Admin";
    auth_basic_user_file /etc/nginx/.htpasswd-duck-admin;

    location /api/ {
        proxy_pass http://127.0.0.1:3017/api/;
        proxy_http_version 1.1;

        proxy_connect_timeout 60s;
        proxy_send_timeout 900s;
        proxy_read_timeout 900s;
        send_timeout 900s;

        proxy_set_header Authorization "Bearer PASTE_DUCK_PANEL_TOKEN_HERE";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /health {
        proxy_pass http://127.0.0.1:3017/health;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Активировать:

sudo ln -sf /etc/nginx/sites-available/admin.obsidians.otc.guru /etc/nginx/sites-enabled/admin.obsidians.otc.guru
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d admin.obsidians.otc.guru

Basic Auth для человека

sudo apt-get install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd-duck-admin leonid

Это защита входа в админку через браузер.

Bearer token между Nginx и API

Backend API читает token из:

/etc/duck-garden-panel.env

Создать token:

TOKEN="$(openssl rand -hex 32)"
echo "$TOKEN"

В 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=

Этот же token должен быть в Nginx:

proxy_set_header Authorization "Bearer long-random-token";

Браузер не должен знать DUCK_PANEL_TOKEN. Его подставляет только Nginx.

Проверить API из сервера

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

Ожидаемо:

{
  "items": []
}

или список gardens.

Service API

Проверить:

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

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

401 Unauthorized

API не получил правильный Bearer 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

Если напрямую работает, а через домен нет — проверить proxy_set_header Authorization в Nginx.

504 Gateway Time-out

Создание garden или rebuild могут идти дольше стандартного timeout.

В location /api/ должны быть длинные timeouts:

proxy_connect_timeout 60s;
proxy_send_timeout 900s;
proxy_read_timeout 900s;
send_timeout 900s;

После изменения:

sudo nginx -t
sudo systemctl reload nginx

Next build падает из-за backup .tsx

Проверить:

find . -name "*.old.tsx" -o -name "*.backup.tsx" -o -name "*.bak.tsx"

Backup-файлы нужно вынести из проекта или удалить.

Дальше: backend API панели.