feat(jobs): register devintelli-daily-sync cron job (0 6 * * *)
- Add devintelli-daily-sync handler: POST to DevIntelli backend /api/sync/daily - Uses x-internal-key header for service-to-service auth - Add DEVINTELLI_BACKEND_URL + DEVINTELLI_INTERNAL_API_KEY env vars - Cron: 0 6 * * * (6am UTC daily), timeout: 5 min - Returns triggered/skipped/totalConnections metrics from DevIntelli response
This commit is contained in:
parent
89e200fa9f
commit
05594a334f
@ -68,6 +68,9 @@ const envSchema = z.object({
|
||||
EVENT_BUS_FILE: z.string().default('.data/platform-events.json'),
|
||||
EVENT_BUS_POLL_MS: z.coerce.number().default(100),
|
||||
EVENT_BUS_LEASE_MS: z.coerce.number().default(30_000),
|
||||
// ── Cross-Service: DevIntelli ──
|
||||
DEVINTELLI_BACKEND_URL: z.string().default('http://localhost:4021'),
|
||||
DEVINTELLI_INTERNAL_API_KEY: z.string().default('dev-internal-key-do-not-use-in-prod'),
|
||||
});
|
||||
|
||||
export const config = envSchema.parse(process.env);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { getCollection } from '../../lib/datastore.js';
|
||||
import { config } from '../../lib/config.js';
|
||||
import { registerJob } from './registry.js';
|
||||
import type { JobContext, JobResult } from './types.js';
|
||||
import type { SessionDoc } from '../sessions/types.js';
|
||||
@ -20,6 +21,7 @@ export function registerBuiltInJobs(): void {
|
||||
registerJob('telemetry-ttl-sweep', telemetryTtlSweep);
|
||||
registerJob('waitlist-reminder', waitlistReminder);
|
||||
registerJob('license-expiry-check', licenseExpiryCheck);
|
||||
registerJob('devintelli-daily-sync', devintelliDailySync);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,6 +64,12 @@ export const BUILT_IN_JOB_DEFAULTS = [
|
||||
description: 'Warn users with expiring licenses (8am UTC)',
|
||||
timeoutMs: 120_000,
|
||||
},
|
||||
{
|
||||
name: 'devintelli-daily-sync',
|
||||
cron: '0 6 * * *',
|
||||
description: 'DevIntelli daily GitHub sync (6am UTC)',
|
||||
timeoutMs: 300_000,
|
||||
},
|
||||
] as const;
|
||||
|
||||
// ── Job Implementations ──────────────────────────────────────
|
||||
@ -248,3 +256,46 @@ async function licenseExpiryCheck(ctx: JobContext): Promise<JobResult> {
|
||||
|
||||
return { success: true, message: `Found ${warned} licenses expiring soon`, metrics: { warned } };
|
||||
}
|
||||
|
||||
async function devintelliDailySync(ctx: JobContext): Promise<JobResult> {
|
||||
ctx.log.info({ jobName: ctx.jobName }, '[jobs] Running DevIntelli daily GitHub sync');
|
||||
|
||||
const baseUrl = config.DEVINTELLI_BACKEND_URL;
|
||||
const apiKey = config.DEVINTELLI_INTERNAL_API_KEY;
|
||||
const url = `${baseUrl}/api/sync/daily`;
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-internal-key': apiKey,
|
||||
},
|
||||
signal: AbortSignal.timeout(240_000),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const body = await res.text().catch(() => '');
|
||||
return {
|
||||
success: false,
|
||||
message: `DevIntelli backend returned ${res.status}: ${body.slice(0, 200)}`,
|
||||
metrics: { statusCode: res.status },
|
||||
};
|
||||
}
|
||||
|
||||
const data = (await res.json()) as Record<string, unknown>;
|
||||
return {
|
||||
success: true,
|
||||
message: `Triggered ${data.triggered ?? 0} syncs, skipped ${data.skipped ?? 0}`,
|
||||
metrics: {
|
||||
totalConnections: data.totalConnections,
|
||||
triggered: data.triggered,
|
||||
skipped: data.skipped,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
ctx.log.error({ err, url }, '[jobs] DevIntelli daily sync failed');
|
||||
return { success: false, message: msg };
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user