feat: add sync alert webhooks
This commit is contained in:
+8
-1
@@ -6,6 +6,7 @@ import { decrypt } from "../lib/crypto";
|
||||
import { createMemo, createRemoteFile, getMemosIdentity, listMemos, memoUrl, setMemoAttachments } from "../lib/memos";
|
||||
import { recordError } from "../lib/observability";
|
||||
import { fetchRss } from "../lib/rss";
|
||||
import { sendAlert } from "../lib/alerts";
|
||||
|
||||
type Source = { id: number; user_id: number; base_url: string; token_encrypted: string; remote_user: string | null; integration_type: "memos" | "rss"; rss_feed_url: string | null; is_enabled: number; sync_tags_json: string; sync_from: string | null; sync_to: string | null; sync_attachment_mode: "all" | "images" | "none"; attachment_storage_mode: "remote" | "images" | "all"; attachment_cache_limit_bytes: number; sync_batch_size: number; sync_max_posts: number | null; sync_cursor: string | null; sync_imported_count: number; sync_run_id: string | null };
|
||||
type Job = { id: number; source_id: number; kind: "pull" | "push"; payload_json: string | null; attempts: number };
|
||||
@@ -96,6 +97,7 @@ async function run() {
|
||||
recordError("sync", error, { sourceId: source.id, jobId: job.id, kind: job.kind, attempts: job.attempts });
|
||||
db.prepare("UPDATE sync_jobs SET status=?,last_error=?,finished_at=CASE WHEN ? THEN CURRENT_TIMESTAMP ELSE NULL END,run_after=CASE WHEN ? THEN run_after ELSE datetime('now','+5 minutes') END WHERE id=?").run(exhausted ? "failed" : "queued", message, exhausted ? 1 : 0, exhausted ? 1 : 0, job.id);
|
||||
db.prepare("UPDATE sources SET sync_status='error',last_error=?,last_connection_error=? WHERE id=?").run(message, message, source.id);
|
||||
if (exhausted) void sendAlert(`sync:${source.id}`, "Mebbling 同步失敗", `來源 #${source.id} 已重試 5 次仍失敗:${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +113,9 @@ function retention() {
|
||||
cleanup("notifications", "created_at", days("NOTIFICATION_RETENTION_DAYS")); cleanup("reading_history", "last_read_at", days("READING_HISTORY_RETENTION_DAYS")); cleanup("audit_events", "created_at", days("AUDIT_RETENTION_DAYS"));
|
||||
}
|
||||
|
||||
let lastRetention = 0; setInterval(() => { schedule(); if (Date.now() - lastRetention > 86_400_000) { retention(); lastRetention = Date.now(); } void run(); }, 5000); schedule(); retention(); void run();
|
||||
function webhookHealth() {
|
||||
const stale = db.prepare("SELECT id,name,last_webhook_at FROM sources WHERE is_enabled=1 AND webhook_mode='signed' AND (last_webhook_at IS NULL OR last_webhook_at<datetime('now','-7 days'))").all() as { id: number; name: string; last_webhook_at: string | null }[];
|
||||
for (const source of stale) void sendAlert(`webhook:${source.id}`, "Mebbling Webhook 未收到事件", `來源「${source.name}」超過 7 天未收到 Webhook;目前仍會以定期 API 同步校正。`);
|
||||
}
|
||||
|
||||
let lastRetention = 0, lastWebhookHealth = 0; setInterval(() => { schedule(); if (Date.now() - lastRetention > 86_400_000) { retention(); lastRetention = Date.now(); } if (Date.now() - lastWebhookHealth > 3_600_000) { webhookHealth(); lastWebhookHealth = Date.now(); } void run(); }, 5000); schedule(); retention(); webhookHealth(); void run();
|
||||
|
||||
Reference in New Issue
Block a user