feat: add Scheduler provider + multi-provider UX fixes
Scheduler provider: - Virtual provider (no external service) that emits SCHEDULED_MESSAGE events on user-defined intervals or cron expressions - Custom variables stored in tracker filters, flattened into template context - fire_count persists across triggers via tracker state - APScheduler CronTrigger support for cron-mode schedules - Default templates (EN+RU), seeded on startup Multi-provider UX fixes: - Tracking config hides Immich-specific sections (periodic, scheduled, memory, asset display) for non-Immich providers - Command config driven by provider capabilities — hides commands/settings for providers without bot commands - Template config hides empty "Scheduled Messages" group - Test menu on tracker targets is provider-aware (Immich shows all 4 test types, others show only basic) - Removed redundant Test button from tracker card - System-owned tracking configs (user_id=0) seeded for Gitea + Scheduler - Fixed ownership checks to allow system configs in tracker-target links - Capabilities cache shared across template-configs and command-configs - Command tracker bot selector uses EntitySelect instead of raw select - Sample context includes Gitea + Scheduler variables for template preview
This commit is contained in:
@@ -94,7 +94,7 @@ async def create_notification_tracker_target(
|
||||
# Validate config ownership + provider type match
|
||||
if body.tracking_config_id:
|
||||
tc = await session.get(TrackingConfig, body.tracking_config_id)
|
||||
if not tc or tc.user_id != user.id:
|
||||
if not tc or (tc.user_id != user.id and tc.user_id != 0):
|
||||
raise HTTPException(status_code=404, detail="Tracking config not found")
|
||||
if tc.provider_type != provider.type:
|
||||
raise HTTPException(
|
||||
@@ -139,7 +139,7 @@ async def update_notification_tracker_target(
|
||||
# Validate config ownership + provider type match if being changed
|
||||
if "tracking_config_id" in updates and updates["tracking_config_id"]:
|
||||
tc = await session.get(TrackingConfig, updates["tracking_config_id"])
|
||||
if not tc or tc.user_id != user.id:
|
||||
if not tc or (tc.user_id != user.id and tc.user_id != 0):
|
||||
raise HTTPException(status_code=404, detail="Tracking config not found")
|
||||
if tc.provider_type != provider.type:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -97,6 +97,8 @@ async def create_provider(
|
||||
detail=test_result.get("message", "Cannot connect to Gitea"),
|
||||
)
|
||||
|
||||
# Scheduler: no validation needed (virtual provider)
|
||||
|
||||
provider = ServiceProvider(
|
||||
user_id=user.id,
|
||||
type=body.type,
|
||||
@@ -253,6 +255,9 @@ async def test_provider(
|
||||
gitea = make_gitea_provider(http_session, provider)
|
||||
return await gitea.test_connection()
|
||||
|
||||
if provider.type == "scheduler":
|
||||
return {"ok": True, "message": "Virtual provider — always available"}
|
||||
|
||||
return {"ok": False, "message": f"Unknown provider type: {provider.type}"}
|
||||
|
||||
|
||||
|
||||
@@ -101,7 +101,10 @@ async def list_configs(
|
||||
user: User = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
query = select(TrackingConfig).where(TrackingConfig.user_id == user.id)
|
||||
from sqlmodel import or_
|
||||
query = select(TrackingConfig).where(
|
||||
or_(TrackingConfig.user_id == user.id, TrackingConfig.user_id == 0)
|
||||
)
|
||||
if provider_type:
|
||||
query = query.where(TrackingConfig.provider_type == provider_type)
|
||||
result = await session.exec(query)
|
||||
@@ -167,6 +170,6 @@ def _response(c: TrackingConfig) -> dict:
|
||||
|
||||
async def _get(session: AsyncSession, config_id: int, user_id: int) -> TrackingConfig:
|
||||
config = await session.get(TrackingConfig, config_id)
|
||||
if not config or config.user_id != user_id:
|
||||
if not config or (config.user_id != user_id and config.user_id != 0):
|
||||
raise HTTPException(status_code=404, detail="Tracking config not found")
|
||||
return config
|
||||
|
||||
@@ -277,6 +277,8 @@ def _event_allowed_by_tracking_config(event: ServiceEvent, tc: TrackingConfig) -
|
||||
"pr_merged": tc.track_pr_merged,
|
||||
"pr_commented": tc.track_pr_commented,
|
||||
"release_published": tc.track_release_published,
|
||||
# Scheduler events
|
||||
"scheduled_message": tc.track_scheduled_message,
|
||||
# Immich events
|
||||
"assets_added": tc.track_assets_added,
|
||||
"assets_removed": tc.track_assets_removed,
|
||||
|
||||
Reference in New Issue
Block a user