fix: comprehensive API/UI review — 26 bug fixes and improvements
Backend: - Scheduler lifecycle sync: create/update/delete tracker now syncs APScheduler jobs - Test-periodic/test-memory endpoints render actual Jinja2 templates with sample data - Cascade cleanup on tracker delete (TrackerState removed, EventLog nullified) - Fix user_id=0 FK violation for system-owned TemplateConfig (removed FK constraint) - Fix API key leak: only attach x-api-key header for internal provider URLs - Validate config ownership in tracker_targets create/update - Fix _response() double-emit of created_at in template/tracking configs - Add per-target-link test endpoints (test, test-periodic, test-memory) Frontend: - Fix orphaned provider on test exception in providers/new - Add submitting guard + disabled state to targets save button - Move test buttons from tracker card to per-target-link rows - Fix Svelte 5 async $state reactivity (spread reassignment for all Record mutations) - i18n for dashboard timeAgo and event type badges (EN + RU) - Add required attribute to chat select dropdown in targets - Fix font CSS vars to prioritize imported DM Sans / JetBrains Mono - Standardize empty states with centered icon + text across all 6 list pages - Add stagger-children animation class to all list containers - Fix slide transition duration consistency (200ms everywhere) - Standardize border-radius to rounded-md across all form inputs - Fix providers/new page structure (h2 + mb-8 spacing) - Fix tracker card action row overflow (flex-wrap justify-end) - JinjaEditor dark mode reactivity (recreate editor on theme change) - Add aria-labels to mobile nav items - Make ConfirmModal confirm button label/icon configurable - Remove double error reporting on providers page - Add telegram bot edit functionality (name editing via PUT) - i18n for External Domain label on provider forms Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,18 +44,6 @@ class TelegramBot(SQLModel, table=True):
|
||||
icon: str = Field(default="")
|
||||
bot_username: str = Field(default="")
|
||||
bot_id: int = Field(default=0)
|
||||
commands_config: dict[str, Any] = Field(
|
||||
default_factory=lambda: {
|
||||
"enabled": ["status", "albums", "events", "summary", "latest",
|
||||
"memory", "random", "search", "find", "person",
|
||||
"place", "favorites", "people", "help"],
|
||||
"default_count": 5,
|
||||
"response_mode": "media",
|
||||
"rate_limits": {"search": 30, "find": 30, "default": 10},
|
||||
"locale": "en",
|
||||
},
|
||||
sa_column=Column(JSON),
|
||||
)
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
@@ -134,7 +122,7 @@ class TemplateConfig(SQLModel, table=True):
|
||||
__tablename__ = "template_config"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
user_id: int = Field(foreign_key="user.id")
|
||||
user_id: int = Field(default=0) # 0 = system-owned (no FK to allow sentinel)
|
||||
provider_type: str # Must match provider's type
|
||||
name: str
|
||||
description: str = Field(default="")
|
||||
@@ -158,7 +146,7 @@ class TemplateConfig(SQLModel, table=True):
|
||||
|
||||
|
||||
class NotificationTarget(SQLModel, table=True):
|
||||
"""Where to send notifications. Owns the template config."""
|
||||
"""Where to send notifications. Pure delivery endpoint."""
|
||||
|
||||
__tablename__ = "notification_target"
|
||||
|
||||
@@ -168,12 +156,11 @@ class NotificationTarget(SQLModel, table=True):
|
||||
name: str
|
||||
icon: str = Field(default="")
|
||||
config: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
|
||||
template_config_id: int | None = Field(default=None, foreign_key="template_config.id")
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class Tracker(SQLModel, table=True):
|
||||
"""Watches a provider's collections for changes. Owns the tracking config."""
|
||||
"""Watches a provider's collections for changes."""
|
||||
|
||||
__tablename__ = "tracker"
|
||||
|
||||
@@ -183,12 +170,32 @@ class Tracker(SQLModel, table=True):
|
||||
name: str
|
||||
icon: str = Field(default="")
|
||||
collection_ids: list[str] = Field(default_factory=list, sa_column=Column(JSON))
|
||||
target_ids: list[int] = Field(default_factory=list, sa_column=Column(JSON))
|
||||
tracking_config_id: int | None = Field(default=None, foreign_key="tracking_config.id")
|
||||
scan_interval: int = Field(default=60)
|
||||
batch_duration: int = Field(default=0) # seconds to accumulate events before dispatch (0=immediate)
|
||||
enabled: bool = Field(default=True)
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class TrackerTarget(SQLModel, table=True):
|
||||
"""Junction between Tracker and NotificationTarget with per-link config."""
|
||||
|
||||
__tablename__ = "tracker_target"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
tracker_id: int = Field(foreign_key="tracker.id", index=True)
|
||||
target_id: int = Field(foreign_key="notification_target.id", index=True)
|
||||
tracking_config_id: int | None = Field(
|
||||
default=None, foreign_key="tracking_config.id"
|
||||
)
|
||||
template_config_id: int | None = Field(
|
||||
default=None, foreign_key="template_config.id"
|
||||
)
|
||||
enabled: bool = Field(default=True)
|
||||
quiet_hours_start: str | None = None
|
||||
quiet_hours_end: str | None = None
|
||||
commands_config: dict[str, Any] | None = Field(
|
||||
default=None, sa_column=Column(JSON)
|
||||
)
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
@@ -200,6 +207,8 @@ class TrackerState(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
tracker_id: int = Field(foreign_key="tracker.id")
|
||||
collection_id: str
|
||||
collection_name: str = Field(default="")
|
||||
shared: bool = Field(default=False)
|
||||
asset_ids: list[str] = Field(default_factory=list, sa_column=Column(JSON))
|
||||
pending_asset_ids: list[str] = Field(default_factory=list, sa_column=Column(JSON))
|
||||
last_updated: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
Reference in New Issue
Block a user