SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
11 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
2.8 KB · 68 lines typescript
Raw Blame History
1import type { Database } from '@rareindex/database';2import { logger } from '@rareindex/shared';3import { runAlerts, runTargets } from './alerts.js';4import { runPortfolioSnapshots } from './snapshots.js';5import { runBadges } from './badges.js';6import { runDigests } from './digest.js';7import { runMaintenance } from './maintenance.js';8import { runSavedSearches } from './saved-searches.js';910export { runAlerts, runTargets, runPortfolioSnapshots, runBadges, runDigests, runMaintenance, runSavedSearches };1112const log = logger.child({ job: 'account' });1314export interface AccountJobsResult {15  alerts: { evaluated: number; triggered: number };16  targets: number;17  snapshots: number | null;18  badges: number | null;19  digests: number | null;20  savedSearches: number | null;21  purged: number | null;22}2324let lastAlertRun: Date | null = null;25let lastDailyKey: string | null = null;2627/**28 * Entry point for the pipeline scheduler (agent D): call every 5–15 minutes.29 *30 *   import { runAccountJobs } from '../account/index.ts';31 *   await runAccountJobs(db);            // alerts + targets each call; daily jobs once per UTC day32 *   await runAccountJobs(db, { daily: true }); // force the daily set (snapshots, badges, digests, saved searches, purge)33 *34 * Alerts use the time of the previous call as `since` (first call: last hour). All functions are35 * idempotent per day and safe to re-run.36 */37export async function runAccountJobs(db: Database, opts: { daily?: boolean; now?: Date } = {}): Promise<AccountJobsResult> {38  const now = opts.now ?? new Date();39  const result: AccountJobsResult = { alerts: { evaluated: 0, triggered: 0 }, targets: 0, snapshots: null, badges: null, digests: null, savedSearches: null, purged: null };40  try {41    result.alerts = await runAlerts(db, { since: lastAlertRun ?? new Date(now.getTime() - 3600_000), now });42    result.targets = await runTargets(db, now);43    lastAlertRun = now;44  } catch (err) {45    log.error({ err: err instanceof Error ? err.message : String(err) }, 'alert jobs failed');46  }47  const dayKey = now.toISOString().slice(0, 10);48  const runDaily = opts.daily || lastDailyKey !== dayKey;49  if (runDaily) {50    const steps: Array<[keyof AccountJobsResult, () => Promise<number>]> = [51      ['snapshots', () => runPortfolioSnapshots(db, dayKey)],52      ['badges', () => runBadges(db)],53      ['savedSearches', () => runSavedSearches(db, now)],54      ['digests', () => runDigests(db, now)],55      ['purged', async () => (await runMaintenance(db, now)).purged],56    ];57    for (const [key, fn] of steps) {58      try {59        (result as unknown as Record<string, unknown>)[key] = await fn();60      } catch (err) {61        log.error({ err: err instanceof Error ? err.message : String(err), step: key }, 'daily account job failed');62      }63    }64    lastDailyKey = dayKey;65  }66  return result;67}68