diff --git a/frontend/src/lib/grid-items.ts b/frontend/src/lib/grid-items.ts index dab3ff8..2ab6404 100644 --- a/frontend/src/lib/grid-items.ts +++ b/frontend/src/lib/grid-items.ts @@ -99,4 +99,5 @@ export const previewTargetTypeItems = (): GridItem[] => [ export const providerTypeItems = (): GridItem[] => [ { value: 'immich', icon: 'mdiCamera', label: t('providers.typeImmich') }, + { value: 'gitea', icon: 'mdiGit', label: t('providers.typeGitea') }, ]; diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json index 84e8970..e76bae3 100644 --- a/frontend/src/lib/i18n/en.json +++ b/frontend/src/lib/i18n/en.json @@ -106,11 +106,18 @@ "offline": "Offline", "checking": "Checking...", "typeImmich": "Immich", + "typeGitea": "Gitea", "loadError": "Failed to load providers.", "externalDomain": "External Domain", "optional": "optional", "urlApiKeyRequired": "URL and API Key are required", "externalDomainHint": "Public-facing URL for notification links. Falls back to server URL.", + "webhookSecret": "Webhook Secret", + "webhookSecretKeep": "Webhook Secret (leave empty to keep current)", + "webhookSecretHint": "Shared secret for HMAC-SHA256 signature verification. Set the same secret in Gitea webhook settings.", + "webhookSecretRequired": "Webhook secret is required", + "apiToken": "API Token", + "apiTokenHint": "Optional. Needed for connection testing and repository listing.", "testAndSave": "Test & Save", "saveWithoutTest": "Save without testing" }, @@ -248,6 +255,7 @@ "matrixRoomId": "Room ID", "receivers": "Receivers", "noReceivers": "No receivers yet", + "alreadyAdded": "already added", "addReceiver": "Add Receiver", "receiverAdded": "Receiver added", "receiverDeleted": "Receiver deleted", @@ -348,6 +356,15 @@ "albumRenamed": "Album renamed", "albumDeleted": "Album deleted", "sharingChanged": "Sharing changed", + "push": "Push", + "issueOpened": "Issue opened", + "issueClosed": "Issue closed", + "issueCommented": "Issue commented", + "prOpened": "PR opened", + "prClosed": "PR closed", + "prMerged": "PR merged", + "prCommented": "PR commented", + "releasePublished": "Release published", "trackImages": "Track images", "trackVideos": "Track videos", "favoritesOnly": "Favorites only", diff --git a/frontend/src/lib/i18n/ru.json b/frontend/src/lib/i18n/ru.json index 699dec4..97e0b3d 100644 --- a/frontend/src/lib/i18n/ru.json +++ b/frontend/src/lib/i18n/ru.json @@ -106,11 +106,18 @@ "offline": "Не в сети", "checking": "Проверка...", "typeImmich": "Immich", + "typeGitea": "Gitea", "loadError": "Не удалось загрузить провайдеры.", "externalDomain": "Внешний домен", "optional": "необязательно", "urlApiKeyRequired": "URL и API ключ обязательны", "externalDomainHint": "Публичный URL для ссылок в уведомлениях. По умолчанию используется URL сервера.", + "webhookSecret": "Секрет вебхука", + "webhookSecretKeep": "Секрет вебхука (оставьте пустым для сохранения текущего)", + "webhookSecretHint": "Общий секрет для проверки HMAC-SHA256 подписи. Укажите тот же секрет в настройках вебхука Gitea.", + "webhookSecretRequired": "Секрет вебхука обязателен", + "apiToken": "API токен", + "apiTokenHint": "Необязательно. Нужен для проверки подключения и получения списка репозиториев.", "testAndSave": "Проверить и сохранить", "saveWithoutTest": "Сохранить без проверки" }, @@ -248,6 +255,7 @@ "matrixRoomId": "ID комнаты", "receivers": "Получатели", "noReceivers": "Нет получателей", + "alreadyAdded": "уже добавлен", "addReceiver": "Добавить получателя", "receiverAdded": "Получатель добавлен", "receiverDeleted": "Получатель удалён", @@ -348,6 +356,15 @@ "albumRenamed": "Альбом переименован", "albumDeleted": "Альбом удалён", "sharingChanged": "Изменение доступа", + "push": "Push", + "issueOpened": "Задача создана", + "issueClosed": "Задача закрыта", + "issueCommented": "Комментарий к задаче", + "prOpened": "PR создан", + "prClosed": "PR закрыт", + "prMerged": "PR влит", + "prCommented": "Комментарий к PR", + "releasePublished": "Релиз опубликован", "trackImages": "Фото", "trackVideos": "Видео", "favoritesOnly": "Только избранные", diff --git a/frontend/src/routes/providers/+page.svelte b/frontend/src/routes/providers/+page.svelte index b58e4f4..b88d204 100644 --- a/frontend/src/routes/providers/+page.svelte +++ b/frontend/src/routes/providers/+page.svelte @@ -21,7 +21,7 @@ let providers = $derived(providersCache.items); let showForm = $state(false); let editing = $state(null); - let form = $state({ name: 'Immich', type: 'immich', url: '', api_key: '', external_domain: '', icon: '' }); + let form = $state({ name: 'Immich', type: 'immich', url: '', api_key: '', api_token: '', webhook_secret: '', external_domain: '', icon: '' }); let error = $state(''); let loadError = $state(''); let submitting = $state(false); @@ -48,12 +48,16 @@ } function openNew() { - form = { name: 'Immich', type: 'immich', url: '', api_key: '', external_domain: '', icon: '' }; + form = { name: 'Immich', type: 'immich', url: '', api_key: '', api_token: '', webhook_secret: '', external_domain: '', icon: '' }; editing = null; showForm = true; } function edit(p: any) { const cfg = p.config || {}; - form = { name: p.name, type: p.type, url: cfg.url || '', api_key: '', external_domain: cfg.external_domain || '', icon: p.icon || '' }; + form = { + name: p.name, type: p.type, url: cfg.url || '', + api_key: '', api_token: '', webhook_secret: '', + external_domain: cfg.external_domain || '', icon: p.icon || '', + }; editing = p.id; showForm = true; } @@ -61,12 +65,21 @@ e.preventDefault(); error = ''; submitting = true; try { const config: any = { url: form.url }; - if (form.api_key) config.api_key = form.api_key; - if (form.external_domain) config.external_domain = form.external_domain; + if (form.type === 'immich') { + if (form.api_key) config.api_key = form.api_key; + if (form.external_domain) config.external_domain = form.external_domain; + if (!editing) config.api_key = form.api_key; + } else if (form.type === 'gitea') { + if (form.api_token) config.api_token = form.api_token; + if (form.webhook_secret) config.webhook_secret = form.webhook_secret; + if (!editing && !form.webhook_secret) { + error = t('providers.webhookSecretRequired'); + snackError(error); submitting = false; return; + } + } if (editing) { await api(`/providers/${editing}`, { method: 'PUT', body: JSON.stringify({ name: form.name, icon: form.icon, config }) }); } else { - config.api_key = form.api_key; // required on create await api('/providers', { method: 'POST', body: JSON.stringify({ type: form.type, name: form.name, icon: form.icon, config }) }); } showForm = false; editing = null; providersCache.invalidate(); await load(); @@ -129,8 +142,9 @@
- +
+ {#if form.type === 'immich'}
@@ -139,6 +153,18 @@
+ {:else if form.type === 'gitea'} +
+ + +

{t('providers.webhookSecretHint')}

+
+
+ + +

{t('providers.apiTokenHint')}

+
+ {/if}