Files
germanandClaude Opus 4.8 3f1c5dbd7b feat(bot): сайт-адаптер единого бота флота + фикс redirect на 1nika.ru
Реализован контракт /api/bot/* из servaki-webhub-bot/SPEC.md:
- GET  /api/bot/health (публичный) -> {ok, site}
- GET  /api/bot/publications?status=&limit= -> список works
- POST /api/bot/publications {title,body,media[]} -> черновик works -> {id}
- POST /api/bot/publications/:id/publish -> published -> {id,url}
- DELETE /api/bot/publications/:id -> удаление (media cascade)
- POST /api/bot/materials (multipart file) -> media -> {id,url}

Аутентификация Authorization: Bearer <BOT_API_TOKEN> (middleware/botAuth,
сравнение постоянного времени; 503 без токена в env, 401 при несовпадении).
Маппинг: публикация<->works, материал<->media. Миграция bot_adapter.sql
разрешает media.type='file'. multer 2.x.

Фикс Яндекс-redirect и домена: nika-gallery.ru -> 1nika.ru в .env.example
(YANDEX_REDIRECT_URI, FRONTEND_URL, SITE_URL, NEXT_PUBLIC_*, SMTP_USER) и
nginx server_name. Секреты не в git (client_id/secret - на Я.Диске).

Добавлен docs/DEPLOY.md (VPS runbook: DNS, .env, SSL, миграции, бот).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 23:24:20 +03:00

7.0 KiB
Raw Permalink Blame History

Деплой nika-gallery (1nika.ru)

Полный runbook подъёма сайта Ники на VPS: Docker Compose (backend Express + Next.js фронт + Postgres 16 + nginx), вход через Яндекс ID и подключение к единому боту флота (@servaki_online_bot) через сайт-адаптер /api/bot/*.

⚠️ Живой VPS-деплой — решение владельца (выбор ноды и DNS). Этот документ — команды и чек-лист; агент репо готовит, но на боевой узел не раскатывает.


0. Что уже готово в репо

  • docker-compose.yml — db / backend / frontend / nginx.
  • db/schema.sql (+ db/migrations/club_access.sql, db/migrations/bot_adapter.sql).
  • Вход через Яндекс ID (backend/src/routes/oauth.js), redirect на 1nika.ru.
  • Сайт-адаптер бота (backend/src/routes/bot.js, middleware/botAuth.js).
  • nginx для 1nika.ru / www.1nika.ru.

1. DNS

На домен 1nika.ru (регистратор/DNS владельца) добавить A-записи на IP выбранной ноды:

1nika.ru.        A   <VPS_IP>
www.1nika.ru.    A   <VPS_IP>

Дождаться распространения (dig +short 1nika.ru должен вернуть <VPS_IP>).


2. Подготовка сервера

Нужны: Docker + docker compose plugin, открытые порты 80/443.

git clone http://192.168.1.120/german/nika-gallery.git /opt/nika-gallery
cd /opt/nika-gallery
git checkout main        # либо feat/bot-adapter до мержа
cp .env.example .env

3. Заполнить .env (боевые значения — только на сервере, НЕ в git)

Переменная Значение Откуда
POSTGRES_PASSWORD сильный пароль сгенерировать
DATABASE_URL postgresql://postgres:<POSTGRES_PASSWORD>@db:5432/nika_gallery собрать
JWT_SECRET / JWT_REFRESH_SECRET openssl rand -hex 32 (каждый свой) сгенерировать
YANDEX_CLIENT_ID 1f64f6ebcd9c45f9af78987204af8e36 Я.Диск YANDEX-OAUTH-APPS.secret.txt [NIKA ART]
YANDEX_CLIENT_SECRET a2b737fc… там же
YANDEX_REDIRECT_URI https://1nika.ru/api/auth/yandex/callback (уже в .env.example)
FRONTEND_URL / SITE_URL https://1nika.ru —
BOT_API_TOKEN openssl rand -hex 32 сгенерировать; тот же токен → в реестр бота (sites.api_token) + Я.Диск
BOT_SITE_KEY nika —
NEXT_PUBLIC_API_URL https://1nika.ru/api —
OWNER_EMAIL, SMTP_*, TELEGRAM_* по факту владелец

⚠️ Секреты не коммитить. В git — только .env.example с пустыми полями. YANDEX_REDIRECT_URI должен точно совпадать со значением в кабинете oauth.yandex.ru (приложение NIKA ART) — иначе Яндекс вернёт redirect_uri_mismatch.


4. SSL-сертификат (Let's Encrypt)

nginx ждёт nginx/certs/fullchain.pem и nginx/certs/privkey.pem. Выпустить certbot'ом (standalone до старта nginx, либо webroot):

# порт 80 должен быть свободен
docker run --rm -p 80:80 \
  -v /opt/nika-gallery/letsencrypt:/etc/letsencrypt \
  certbot/certbot certonly --standalone \
  -d 1nika.ru -d www.1nika.ru --agree-tos -m <OWNER_EMAIL> --no-eff-email

mkdir -p /opt/nika-gallery/nginx/certs
cp /opt/nika-gallery/letsencrypt/live/1nika.ru/fullchain.pem nginx/certs/
cp /opt/nika-gallery/letsencrypt/live/1nika.ru/privkey.pem  nginx/certs/

Продление — cron/systemd-timer с certbot renew + копированием в nginx/certs/ и docker compose exec nginx nginx -s reload.


5. Старт и миграции

db/schema.sql и db/seed.sql применяются автоматически при первой инициализации тома Postgres (см. docker-entrypoint-initdb.d в compose). Миграции из db/migrations/ надо применить вручную (они аддитивны и идемпотентны).

docker compose build
docker compose up -d db
# дождаться healthy:
docker compose exec db pg_isready -U postgres

# Миграции (клуб + Яндекс ID, затем адаптер бота):
docker compose exec -T db psql -U postgres -d nika_gallery < db/migrations/club_access.sql
docker compose exec -T db psql -U postgres -d nika_gallery < db/migrations/bot_adapter.sql

docker compose up -d          # поднять backend + frontend + nginx

Назначить владельца (Ника) ролью owner:

docker compose exec -T db psql -U postgres -d nika_gallery \
  -c "UPDATE users SET role='owner' WHERE email='<nika_email>';"

6. Проверка

curl -s https://1nika.ru/api/health                     # {status:ok}
curl -s https://1nika.ru/api/bot/health                 # {ok:true, site:"nika"}
curl -s -o /dev/null -w '%{http_code}\n' \
  https://1nika.ru/api/bot/publications                 # 401 (без токена)
curl -s -H "Authorization: Bearer $BOT_API_TOKEN" \
  https://1nika.ru/api/bot/publications                 # [] или список

Вход через Яндекс: открыть https://1nika.ru/api/auth/yandex → авторизация → редирект на https://1nika.ru/club#token=….


7. Подключение к боту флота

После деплоя владелец в реестре бота (sites на finik, /var/lib/webhub-bot/state.db) выставляет для site_key='nika':

  • base_url = https://1nika.ru
  • api_token = <BOT_API_TOKEN из .env>
  • status = live (сейчас soon).

Бот дергает /api/bot/* с заголовком Authorization: Bearer <api_token>. Контракт — servaki-webhub-bot/SPEC.md.


8. Обновления

cd /opt/nika-gallery && git pull && docker compose build && docker compose up -d

(автоматизировано в .gitea/workflows/deploy.yml при push в main; требует секретов раннера DEPLOY_HOST, DEPLOY_KEY).

Приложение: карта ручек адаптера

Метод Путь Auth Ника
GET /api/bot/health — {ok,site}
GET /api/bot/publications?status=&limit= Bearer список works
POST /api/bot/publications {title,body,media[]} Bearer works(draft) → {id}
POST /api/bot/publications/:id/publish Bearer works.status=published → {id,url}
DELETE /api/bot/publications/:id Bearer delete works (+media cascade)
POST /api/bot/materials (multipart file) Bearer media → {id,url}