734e5c9340
- Remove top paginator from dashboard events, keep only bottom - Fix test message locale: pass UI locale to email/matrix bot tests - Convert webhook auth mode from text input to icon grid selector - Generate secure UUID tokens for webhook URLs instead of sequential IDs - Move Recent Payloads into per-provider expandable container (lazy-loaded) - Make template config languages dynamic via app settings instead of hardcoded - Change default dev port to 5175
103 lines
3.7 KiB
Svelte
103 lines
3.7 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { api } from '$lib/api';
|
|
import { t } from '$lib/i18n';
|
|
import PageHeader from '$lib/components/PageHeader.svelte';
|
|
import Card from '$lib/components/Card.svelte';
|
|
import Loading from '$lib/components/Loading.svelte';
|
|
import MdiIcon from '$lib/components/MdiIcon.svelte';
|
|
import Hint from '$lib/components/Hint.svelte';
|
|
import ErrorBanner from '$lib/components/ErrorBanner.svelte';
|
|
import Button from '$lib/components/Button.svelte';
|
|
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
|
|
|
let loaded = $state(false);
|
|
let saving = $state(false);
|
|
let error = $state('');
|
|
let settings = $state({
|
|
external_url: '',
|
|
telegram_webhook_secret: '',
|
|
telegram_cache_ttl_hours: '48',
|
|
supported_locales: 'en,ru',
|
|
});
|
|
|
|
onMount(async () => {
|
|
try {
|
|
settings = await api('/settings');
|
|
} catch (err: any) { error = err.message; snackError(err.message); }
|
|
finally { loaded = true; }
|
|
});
|
|
|
|
async function save() {
|
|
saving = true; error = '';
|
|
try {
|
|
settings = await api('/settings', { method: 'PUT', body: JSON.stringify(settings) });
|
|
snackSuccess(t('settings.saved'));
|
|
} catch (err: any) { error = err.message; snackError(err.message); }
|
|
saving = false;
|
|
}
|
|
</script>
|
|
|
|
<PageHeader title={t('settings.title')} description={t('settings.description')} />
|
|
|
|
{#if !loaded}
|
|
<Loading />
|
|
{:else}
|
|
<ErrorBanner message={error} />
|
|
<div class="space-y-6">
|
|
<!-- General section -->
|
|
<Card>
|
|
<h3 class="text-sm font-semibold mb-4 flex items-center gap-2">
|
|
<MdiIcon name="mdiCog" size={18} />
|
|
{t('settings.general')}
|
|
</h3>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">{t('settings.externalUrl')}<Hint text={t('settings.externalUrlHint')} /></label>
|
|
<input bind:value={settings.external_url} placeholder="https://notify.example.com"
|
|
class="w-full max-w-md px-3 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)] font-mono" />
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
<!-- Telegram section -->
|
|
<Card>
|
|
<h3 class="text-sm font-semibold mb-4 flex items-center gap-2">
|
|
<MdiIcon name="mdiSend" size={18} />
|
|
{t('settings.telegram')}
|
|
</h3>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">{t('settings.webhookSecret')}<Hint text={t('settings.webhookSecretHint')} /></label>
|
|
<input bind:value={settings.telegram_webhook_secret} type="password" placeholder={t('providers.optional')}
|
|
class="w-full px-3 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)] font-mono" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">{t('settings.cacheTtl')}<Hint text={t('settings.cacheTtlHint')} /></label>
|
|
<input bind:value={settings.telegram_cache_ttl_hours} type="number" min="1" max="720"
|
|
class="w-full px-3 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)]" />
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
<!-- Locales section -->
|
|
<Card>
|
|
<h3 class="text-sm font-semibold mb-4 flex items-center gap-2">
|
|
<MdiIcon name="mdiTranslate" size={18} />
|
|
{t('settings.locales')}
|
|
</h3>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<label class="block text-xs font-medium mb-1">{t('settings.supportedLocales')}<Hint text={t('settings.supportedLocalesHint')} /></label>
|
|
<input bind:value={settings.supported_locales} placeholder="en,ru,de,fr"
|
|
class="w-full max-w-md px-3 py-1.5 text-sm border border-[var(--color-border)] rounded-md bg-[var(--color-background)] font-mono" />
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
<Button onclick={save} disabled={saving}>
|
|
{saving ? t('common.loading') : t('common.save')}
|
|
</Button>
|
|
</div>
|
|
{/if}
|