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:
@@ -0,0 +1,66 @@
|
||||
"""Search-related Immich bot commands: search, find, person, place."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from ..handler import _render_cmd_template
|
||||
from .common import _format_assets
|
||||
|
||||
|
||||
async def cmd_search(
|
||||
client: Any, args: str, all_album_ids: list[str], count: int,
|
||||
locale: str, response_mode: str,
|
||||
cmd_templates: dict[str, dict[str, str]],
|
||||
) -> str | list[dict[str, Any]]:
|
||||
"""Handle /search command."""
|
||||
if not args:
|
||||
return _render_cmd_template(cmd_templates, "no_results", locale, {"command": "search", "query": ""})
|
||||
assets = await client.search_smart(args, album_ids=all_album_ids, limit=count)
|
||||
return _format_assets(assets, "search", args, locale, response_mode, client, cmd_templates)
|
||||
|
||||
|
||||
async def cmd_find(
|
||||
client: Any, args: str, all_album_ids: list[str], count: int,
|
||||
locale: str, response_mode: str,
|
||||
cmd_templates: dict[str, dict[str, str]],
|
||||
) -> str | list[dict[str, Any]]:
|
||||
"""Handle /find command."""
|
||||
if not args:
|
||||
return _render_cmd_template(cmd_templates, "no_results", locale, {"command": "find", "query": ""})
|
||||
assets = await client.search_metadata(args, album_ids=all_album_ids, limit=count)
|
||||
return _format_assets(assets, "find", args, locale, response_mode, client, cmd_templates)
|
||||
|
||||
|
||||
async def cmd_person(
|
||||
client: Any, args: str, count: int,
|
||||
locale: str, response_mode: str,
|
||||
cmd_templates: dict[str, dict[str, str]],
|
||||
) -> str | list[dict[str, Any]]:
|
||||
"""Handle /person command."""
|
||||
if not args:
|
||||
return _render_cmd_template(cmd_templates, "no_results", locale, {"command": "person", "query": ""})
|
||||
people = await client.get_people()
|
||||
person_id = None
|
||||
for pid, pname in people.items():
|
||||
if args.lower() in pname.lower():
|
||||
person_id = pid
|
||||
break
|
||||
if not person_id:
|
||||
return _render_cmd_template(cmd_templates, "no_results", locale, {"command": "person", "query": args})
|
||||
assets = await client.search_by_person(person_id, limit=count)
|
||||
return _format_assets(assets, "person", args, locale, response_mode, client, cmd_templates)
|
||||
|
||||
|
||||
async def cmd_place(
|
||||
client: Any, args: str, all_album_ids: list[str], count: int,
|
||||
locale: str, response_mode: str,
|
||||
cmd_templates: dict[str, dict[str, str]],
|
||||
) -> str | list[dict[str, Any]]:
|
||||
"""Handle /place command."""
|
||||
if not args:
|
||||
return _render_cmd_template(cmd_templates, "no_results", locale, {"command": "place", "query": ""})
|
||||
assets = await client.search_smart(
|
||||
f"photos taken in {args}", album_ids=all_album_ids, limit=count
|
||||
)
|
||||
return _format_assets(assets, "place", args, locale, response_mode, client, cmd_templates)
|
||||
Reference in New Issue
Block a user