mirror of
https://github.com/davila7/claude-code-templates.git
synced 2026-09-19 01:30:23 +08:00
e327437f2c
Adds a proactive daily heartbeat, since the existing Telegram flows (docs-monitor, pulse) only notify on change/error/weekly schedule and never confirm "everything's fine." Runs at 14:00 UTC (10:00 AM EDT) and reports: - Dashboard site health (reuses the existing /api/health-check endpoint) - Unresolved Sentry issues from the last 24h across all 3 Sentry projects (aitmpl-workers, aitmpl-dashboard, aitmpl-cli) Does NOT poll the other 3 workers' /status endpoints directly — Cloudflare blocks Worker-to-Worker fetches over the public *.workers.dev hostname within the same account (error 1042, confirmed via wrangler dev --remote). Their health is covered by their own Sentry Cron Monitor check-ins instead. Also adds a /status endpoint to aitmpl-crons (cloudflare-workers/crons/index.js) for consistency with the other workers, even though it isn't polled here. Verified end-to-end: manual /trigger call returns a clean report and successfully delivers to Telegram (message_id confirmed). Sentry summary section requires SENTRY_AUTH_TOKEN + SENTRY_ORG_SLUG secrets (pending — degrades gracefully to "not configured" until set). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
92 lines
2.8 KiB
JavaScript
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 });
|
|
},
|
|
};
|