feat: comprehensive code review fixes — security, performance, quality
Backend security: - Reject Gitea webhooks when webhook_secret is empty (was silently skipping) - Add slowapi rate limiting on login (5/min) and setup (3/min) endpoints - Add CORS middleware with configurable origins - Mask telegram_webhook_secret in settings API response - Protect system-owned command template configs from regular user modification - Increase minimum password length to 8 characters Backend performance: - Batch queries in _resolve_command_context (3 queries instead of 3N) - Concurrent album fetching with asyncio.gather in immich commands - Singleton Jinja2 SandboxedEnvironment (reuse instead of per-render creation) - TTLCache for rate limits (bounded memory, auto-eviction) - Optional aiohttp session reuse in send_reply/send_media_group Backend code quality: - Extract dispatch_helpers.py (shared link_data loading + event filtering) - Extract database/seeds.py from main.py (490 lines → dedicated module) - Split immich_handler.py (415 lines) into commands/immich/ subpackage - Replace bare except blocks with logged warnings - Add per-provider config validation (Pydantic models) - Truncate command input to 512 chars - Expose usage_* and desc_* slots in capabilities and variables API Frontend security: - CSS.escape() for user-controlled querySelector in highlight.ts - Client-side password min 8 chars validation on setup and password change Frontend code quality: - Replace any types with proper interfaces across top files - Decompose targets/+page.svelte into TargetForm + ReceiverSection - Fix $derived.by usage, $state mutation patterns - Add console.warn to empty catch blocks Frontend UX: - Auth redirect via goto() with "Redirecting..." state - Platform-aware Ctrl/Cmd K keyboard hint - Remove stat-card hover transform Frontend accessibility: - Modal: role=dialog, aria-modal, focus trap, restore focus - EntitySelect/IconGridSelect: listbox/option roles, aria-selected/disabled
This commit is contained in:
@@ -40,32 +40,34 @@
|
||||
}
|
||||
|
||||
/** All searchable entity groups. */
|
||||
const GROUPS = [
|
||||
{ key: 'providers', label: 'nav.providers', icon: 'mdiServer', href: '/providers',
|
||||
mapFn: (e: any) => ({ detail: e.type, icon: e.icon || 'mdiServer' }) },
|
||||
{ key: 'notification_trackers', label: 'nav.notification', icon: 'mdiRadar', href: '/notification-trackers',
|
||||
mapFn: (e: any) => ({ detail: e.enabled ? 'enabled' : 'disabled', icon: e.icon || 'mdiRadar' }) },
|
||||
{ key: 'tracking_configs', label: 'nav.trackingConfigs', icon: 'mdiCog', href: '/tracking-configs',
|
||||
mapFn: (e: any) => ({ detail: e.provider_type, icon: e.icon || 'mdiCog' }) },
|
||||
{ key: 'template_configs', label: 'nav.templateConfigs', icon: 'mdiFileDocumentEdit', href: '/template-configs',
|
||||
mapFn: (e: any) => ({ detail: e.provider_type, icon: e.icon || 'mdiFileDocumentEdit' }) },
|
||||
{ key: 'targets', label: 'nav.targets', icon: 'mdiTarget', href: '/targets',
|
||||
mapFn: (e: any) => ({ detail: e.type, icon: e.icon || 'mdiTarget' }) },
|
||||
{ key: 'telegram_bots', label: 'nav.telegram', icon: 'mdiSendCircle', href: '/bots?tab=telegram',
|
||||
mapFn: (e: any) => ({ detail: `@${e.bot_username || ''}`, icon: e.icon || 'mdiRobot' }) },
|
||||
{ key: 'email_bots', label: 'nav.email', icon: 'mdiEmailOutline', href: '/bots?tab=email',
|
||||
mapFn: (e: any) => ({ detail: e.email || '', icon: e.icon || 'mdiEmailOutline' }) },
|
||||
{ key: 'matrix_bots', label: 'nav.matrix', icon: 'mdiMatrix', href: '/bots?tab=matrix',
|
||||
mapFn: (e: any) => ({ detail: e.display_name || '', icon: e.icon || 'mdiMatrix' }) },
|
||||
{ key: 'command_trackers', label: 'nav.commandTrackers', icon: 'mdiConsoleLine', href: '/command-trackers',
|
||||
mapFn: (e: any) => ({ detail: e.enabled ? 'enabled' : 'disabled', icon: e.icon || 'mdiConsoleLine' }) },
|
||||
{ key: 'command_configs', label: 'nav.commandConfigs', icon: 'mdiCog', href: '/command-configs',
|
||||
mapFn: (e: any) => ({ detail: e.provider_type, icon: e.icon || 'mdiCog' }) },
|
||||
{ key: 'command_template_configs', label: 'nav.cmdTemplateConfigs', icon: 'mdiCodeBracesBox', href: '/command-template-configs',
|
||||
mapFn: (e: any) => ({ detail: e.provider_type, icon: e.icon || 'mdiCodeBracesBox' }) },
|
||||
] as const;
|
||||
type CacheEntity = Record<string, unknown> & { id: number; name: string };
|
||||
|
||||
const cacheMap: Record<string, { items: any[] }> = {
|
||||
const GROUPS: readonly { key: string; label: string; icon: string; href: string; mapFn: (e: CacheEntity) => { detail: string; icon: string } }[] = [
|
||||
{ key: 'providers', label: 'nav.providers', icon: 'mdiServer', href: '/providers',
|
||||
mapFn: (e) => ({ detail: String(e.type || ''), icon: String(e.icon || 'mdiServer') }) },
|
||||
{ key: 'notification_trackers', label: 'nav.notification', icon: 'mdiRadar', href: '/notification-trackers',
|
||||
mapFn: (e) => ({ detail: e.enabled ? 'enabled' : 'disabled', icon: String(e.icon || 'mdiRadar') }) },
|
||||
{ key: 'tracking_configs', label: 'nav.trackingConfigs', icon: 'mdiCog', href: '/tracking-configs',
|
||||
mapFn: (e) => ({ detail: String(e.provider_type || ''), icon: String(e.icon || 'mdiCog') }) },
|
||||
{ key: 'template_configs', label: 'nav.templateConfigs', icon: 'mdiFileDocumentEdit', href: '/template-configs',
|
||||
mapFn: (e) => ({ detail: String(e.provider_type || ''), icon: String(e.icon || 'mdiFileDocumentEdit') }) },
|
||||
{ key: 'targets', label: 'nav.targets', icon: 'mdiTarget', href: '/targets',
|
||||
mapFn: (e) => ({ detail: String(e.type || ''), icon: String(e.icon || 'mdiTarget') }) },
|
||||
{ key: 'telegram_bots', label: 'nav.telegram', icon: 'mdiSendCircle', href: '/bots?tab=telegram',
|
||||
mapFn: (e) => ({ detail: `@${e.bot_username || ''}`, icon: String(e.icon || 'mdiRobot') }) },
|
||||
{ key: 'email_bots', label: 'nav.email', icon: 'mdiEmailOutline', href: '/bots?tab=email',
|
||||
mapFn: (e) => ({ detail: String(e.email || ''), icon: String(e.icon || 'mdiEmailOutline') }) },
|
||||
{ key: 'matrix_bots', label: 'nav.matrix', icon: 'mdiMatrix', href: '/bots?tab=matrix',
|
||||
mapFn: (e) => ({ detail: String(e.display_name || ''), icon: String(e.icon || 'mdiMatrix') }) },
|
||||
{ key: 'command_trackers', label: 'nav.commandTrackers', icon: 'mdiConsoleLine', href: '/command-trackers',
|
||||
mapFn: (e) => ({ detail: e.enabled ? 'enabled' : 'disabled', icon: String(e.icon || 'mdiConsoleLine') }) },
|
||||
{ key: 'command_configs', label: 'nav.commandConfigs', icon: 'mdiCog', href: '/command-configs',
|
||||
mapFn: (e) => ({ detail: String(e.provider_type || ''), icon: String(e.icon || 'mdiCog') }) },
|
||||
{ key: 'command_template_configs', label: 'nav.cmdTemplateConfigs', icon: 'mdiCodeBracesBox', href: '/command-template-configs',
|
||||
mapFn: (e) => ({ detail: String(e.provider_type || ''), icon: String(e.icon || 'mdiCodeBracesBox') }) },
|
||||
];
|
||||
|
||||
const cacheMap = {
|
||||
providers: providersCache,
|
||||
notification_trackers: notificationTrackersCache,
|
||||
tracking_configs: trackingConfigsCache,
|
||||
@@ -77,7 +79,7 @@
|
||||
command_trackers: commandTrackersCache,
|
||||
command_configs: commandConfigsCache,
|
||||
command_template_configs: commandTemplateConfigsCache,
|
||||
};
|
||||
} as unknown as Record<string, { items: { id: number; name: string; [k: string]: unknown }[] }>;
|
||||
|
||||
/** Build flat results from all caches, filtered by query. */
|
||||
const results = $derived.by(() => {
|
||||
|
||||
Reference in New Issue
Block a user