Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

92 lines
2.8 KiB
JavaScript

/**
* aitmpl-crons — Cloudflare Worker
*
* Replaces Vercel cron jobs by calling the dashboard API endpoints
* on a schedule. Cron: every 30 minutes (claude-code-check) and hourly (health-check).
*
* Secrets (wrangler secret put):
* DASHBOARD_URL — e.g. https://www.aitmpl.com
* TRIGGER_SECRET — shared secret sent as Authorization header
* SENTRY_DSN — DSN from the "aitmpl-workers" Sentry project (error tracking)
*/
import { reportError, checkIn } from './sentry.js';
const MONITORS = {
'*/30 * * * *': 'claude-code-check',
'0 * * * *': 'health-check',
};
export default {
async scheduled(event, env, ctx) {
const base = env.DASHBOARD_URL || 'https://www.aitmpl.com';
const headers = {
'Authorization': `Bearer ${env.TRIGGER_SECRET || ''}`,
'User-Agent': 'aitmpl-crons/1.0',
};
const cron = event.cron;
const endpoint = cron === '*/30 * * * *' ? '/api/claude-code-check'
: cron === '0 * * * *' ? '/api/health-check'
: null;
if (!endpoint) {
console.log(`Unknown cron schedule: ${cron}`);
return;
}
const monitorSlug = MONITORS[cron];
const checkInId = await checkIn(env, monitorSlug, 'in_progress');
try {
const res = await fetch(`${base}${endpoint}`, { headers });
console.log(`${endpoint}: ${res.status}`);
if (!res.ok) {
await reportError(env, `${endpoint} returned ${res.status}`, {
worker: 'aitmpl-crons',
cron,
endpoint,
status: res.status,
});
await checkIn(env, monitorSlug, 'error', checkInId);
} else {
await checkIn(env, monitorSlug, 'ok', checkInId);
}
} catch (error) {
console.error(`${endpoint} failed:`, error.message);
await reportError(env, error, { worker: 'aitmpl-crons', cron, endpoint });
await checkIn(env, monitorSlug, 'error', checkInId);
}
},
// Manual trigger for testing: GET /trigger?cron=*/30+*+*+*+*
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === '/status') {
return new Response(JSON.stringify({
status: 'running',
worker: 'aitmpl-crons',
schedules: {
'*/30 * * * *': '/api/claude-code-check',
'0 * * * *': '/api/health-check',
},
}), { headers: { 'Content-Type': 'application/json' } });
}
if (url.pathname !== '/trigger') {
return new Response('aitmpl-crons worker', { status: 200 });
}
const auth = request.headers.get('Authorization');
if (!env.TRIGGER_SECRET || auth !== `Bearer ${env.TRIGGER_SECRET}`) {
return new Response('Unauthorized', { status: 401 });
}
const cron = url.searchParams.get('cron') || '*/30 * * * *';
await this.scheduled({ cron }, env, {});
return new Response(`Triggered: ${cron}`, { status: 200 });
},
};