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:
2026-03-22 15:50:51 +03:00
parent 6d28cfb8d8
commit 0562f78b35
30 changed files with 688 additions and 56 deletions
+17
View File
@@ -53,6 +53,23 @@ export const commandTemplateConfigsCache = createEntityCache<CommandTemplateConf
/** Command trackers — used by Command Trackers page. */
export const commandTrackersCache = createEntityCache<CommandTracker>('/command-trackers');
/** Provider capabilities — used by Template Configs, Command Configs. */
export const capabilitiesCache = (() => {
let data = $state<Record<string, any>>({});
let fetchedAt = $state(0);
const TTL = 60_000; // 1 minute
return {
get items() { return data; },
async fetch(force = false): Promise<Record<string, any>> {
if (!force && Object.keys(data).length > 0 && Date.now() - fetchedAt < TTL) return data;
const { api } = await import('$lib/api');
data = await api('/providers/capabilities');
fetchedAt = Date.now();
return data;
},
};
})();
/**
* All caches keyed by entity type — for search palette and crosslink resolution.
*/