feat(webhook): per-project and per-site webhook URLs
Build / build (push) Successful in 10m25s

Replace the single global webhook secret with entity-scoped secrets stored
on each project and static site. Webhook-driven project autocreate is
removed — projects must exist before their URL can trigger deploys.

Also wires static-site webhooks (sync_trigger=push|tag), turning the
previously inert "push" trigger into a functional one: POST the site's
webhook URL from a Git provider and Tinyforge re-syncs on matching refs.

- Adds webhook_secret columns + unique indexes to projects and static_sites
- Per-entity GET/regenerate endpoints under /api/projects/{id}/webhook
  and /api/sites/{id}/webhook (admin-only)
- Removes /api/settings/webhook-url and the global webhook panel
- Reusable WebhookPanel Svelte component on both detail pages, i18n in en/ru
- Tests for matcher (siteRefMatches, ParseImageRef) and handler (project
  match/mismatch/404 and site push/manual/branch-skip)
This commit is contained in:
2026-04-23 15:18:19 +03:00
parent e08acf5c0e
commit 0632f512e6
21 changed files with 1119 additions and 363 deletions
+20 -1
View File
@@ -74,6 +74,8 @@
"noMatchingProjects": "Проекты не найдены."
},
"projectDetail": {
"webhookTitle": "Webhook проекта",
"webhookDesc": "Отправьте POST с image-ссылкой на этот URL из CI — и Tinyforge запустит деплой. Стейдж выбирается по tag_pattern.",
"deleteProject": "Удалить проект",
"envVars": "Переменные окружения",
"volumes": "Тома",
@@ -570,6 +572,8 @@
"lastChecked": "Последняя проверка"
},
"sites": {
"webhookTitle": "Webhook сайта",
"webhookDesc": "Укажите этот URL в push-вебхуке Git-провайдера. Tinyforge пересинхронизирует сайт при подходящей ref-ссылке (ветка для push, шаблон тега для tag). Пустое тело запускает синхронизацию безусловно.",
"title": "Статические сайты",
"addSite": "Новый сайт",
"newSite": "Новый статический сайт",
@@ -1099,7 +1103,22 @@
"title": "Интеграции",
"outgoing": "Исходящие уведомления",
"outgoingDesc": "Куда Tinyforge отправляет события деплоев и алертов. Укажите webhook-URL (Apprise, Discord, Slack, свой обработчик).",
"incoming": "Входящий вебхук"
"incoming": "Входящие вебхуки",
"incomingMovedDesc": "Входящие вебхуки теперь привязаны к конкретному проекту или сайту. Откройте страницу проекта или статического сайта, чтобы увидеть и перегенерировать URL."
},
"webhookPanel": {
"copy": "Копировать",
"copied": "Webhook-URL скопирован в буфер обмена",
"copyFailed": "Не удалось скопировать",
"noUrl": "Webhook-URL не настроен",
"loadFailed": "Не удалось загрузить webhook-URL",
"regenerate": "Перегенерировать URL",
"regenerated": "Webhook-URL перегенерирован",
"regenerateFailed": "Не удалось перегенерировать webhook-URL",
"regenerateWarning": "Перегенерация инвалидирует текущий URL. Обновите CI-пайплайны и Git-вебхуки, использующие его.",
"confirmRegenerate": "Заменить текущий URL?",
"confirmYes": "Перегенерировать",
"confirmNo": "Отмена"
},
"settingsMaintenance": {
"title": "Обслуживание",