feat: comprehensive code review fixes + receivers-only architecture
Security:
- Refuse startup with default secret_key in production (was just logging)
- Settings endpoint now requires admin role
- Password validation on initial setup
- DOM-based HTML sanitizer replaces regex in template previews
- Add *.log to .gitignore
Performance & reliability:
- Token refresh deduplication prevents race condition on concurrent 401s
- Theme media query listener registered once (no leak)
- IconPicker uses $derived instead of function call per render
- Snackbar uses single-batch state update instead of while loop
- Replace 11 inline hover handlers with CSS :hover in layout
Architecture - receivers-only:
- Delivery endpoints (chat_id, email, url, room_id, topic) now stored
exclusively in TargetReceiver rows, never in target.config
- Migration extracts existing delivery fields to receiver rows
- Notifier and dispatcher remove all config fallbacks
- Frontend targets page shows receivers list per target with
add/remove/toggle/test per receiver
- Single-receiver test endpoint: POST /targets/{id}/receivers/{id}/test
Code quality:
- Extract AuthLayout.svelte from login/setup (150 lines CSS dedup)
- Split telegram-bots page (754→51 lines + 3 tab components)
- Split notification-trackers page (547→432 lines + 4 components)
- Deduplicate _send_reply into shared handler.send_reply()
- Add locale column to template models, replace name-based detection
- Fix delete_notification_tracker dead protection check
- Fix check_telegram_bot query (filter by type, remove bogus OR)
- Add graceful scheduler shutdown in lifespan
- Consistent /bots?tab=telegram URLs across all nav links
i18n:
- Error page, chat actions, target types, provider types internationalized
- All new receiver UI strings in EN + RU
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/i18n';
|
||||
import Modal from '$lib/components/Modal.svelte';
|
||||
import MdiIcon from '$lib/components/MdiIcon.svelte';
|
||||
|
||||
interface Props {
|
||||
linkWarning: { albums: any[]; providerId: number } | null;
|
||||
linkCreating: boolean;
|
||||
onclose: () => void;
|
||||
onautoCreate: () => void;
|
||||
ondismiss: () => void;
|
||||
}
|
||||
|
||||
let { linkWarning, linkCreating, onclose, onautoCreate, ondismiss }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Modal open={linkWarning !== null} title={t('notificationTracker.missingLinksTitle')} onclose={onclose}>
|
||||
{#if linkWarning}
|
||||
<p class="text-sm mb-3" style="color: var(--color-muted-foreground);">
|
||||
{t('notificationTracker.missingLinksDesc')}
|
||||
</p>
|
||||
<div class="space-y-1.5 mb-4 max-h-40 overflow-y-auto">
|
||||
{#each linkWarning.albums as album}
|
||||
<div class="flex items-center justify-between text-sm px-2 py-1.5 rounded bg-[var(--color-muted)]/30">
|
||||
<span class="font-medium">{album.name}</span>
|
||||
<span class="text-xs px-1.5 py-0.5 rounded {album.issue === 'expired' ? 'bg-[var(--color-error-bg)] text-[var(--color-error-fg)]' : album.issue === 'password-protected' ? 'bg-[var(--color-warning-bg)] text-[var(--color-warning-fg)]' : 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]'}">
|
||||
{album.issue === 'expired' ? t('notificationTracker.expired') : album.issue === 'password-protected' ? t('notificationTracker.passwordProtected') : t('notificationTracker.noLink')}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<p class="text-xs mb-4" style="color: var(--color-muted-foreground);">
|
||||
<MdiIcon name="mdiInformation" size={14} /> {t('notificationTracker.linksNote')}
|
||||
</p>
|
||||
<div class="flex items-center gap-2 justify-end">
|
||||
<button onclick={ondismiss}
|
||||
class="px-3 py-1.5 text-sm border border-[var(--color-border)] rounded-md hover:bg-[var(--color-muted)]">
|
||||
{t('notificationTracker.saveWithoutLinks')}
|
||||
</button>
|
||||
{#if linkWarning.albums.some(a => a.issue === 'missing')}
|
||||
<button onclick={onautoCreate} disabled={linkCreating}
|
||||
class="px-3 py-1.5 text-sm bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-md hover:opacity-90 disabled:opacity-50">
|
||||
{linkCreating ? t('common.loading') : t('notificationTracker.createLinks').replace('{count}', String(linkWarning.albums.filter(a => a.issue === 'missing').length))}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</Modal>
|
||||
Reference in New Issue
Block a user