fix(cicd): 自动恢复 GitHub webhook 失败投递
This commit is contained in:
@@ -420,6 +420,7 @@ export function createDurableSyncController(options) {
|
||||
journal: storageReady ? store.journalMetadata() : null,
|
||||
counts,
|
||||
deliveries: statusRecords(records).map(publicInboxRecord),
|
||||
failedDeliveryRecovery: await readFailedDeliveryRecoveryStatus(options.failedDeliveryRecoveryStatePath),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -946,6 +947,7 @@ export function loadRuntimeConfig() {
|
||||
committedRetentionMs: positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_COMMITTED_RETENTION_SECONDS") * 1000,
|
||||
cleanupIntervalMs: positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_CLEANUP_INTERVAL_MS"),
|
||||
},
|
||||
failedDeliveryRecoveryStatePath: process.env.UNIDESK_GITEA_HOOK_RECOVERY_STATE_PATH || null,
|
||||
workerRetry: {
|
||||
maxAttempts: positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_RETRY_MAX_ATTEMPTS"),
|
||||
initialDelayMs: positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_RETRY_INITIAL_DELAY_MS"),
|
||||
@@ -964,6 +966,51 @@ export function loadRuntimeConfig() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function readFailedDeliveryRecoveryStatus(statePath) {
|
||||
if (!statePath) return { enabled: false, ready: true, state: "disabled", counts: {}, entries: [] };
|
||||
let ledger;
|
||||
try {
|
||||
ledger = JSON.parse(await readFile(statePath, "utf8"));
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") return { enabled: true, ready: false, state: "not-initialized", counts: {}, entries: [] };
|
||||
return { enabled: true, ready: false, state: "invalid-or-unreadable", errorType: "hook-recovery-ledger-unreadable", counts: {}, entries: [] };
|
||||
}
|
||||
if (ledger?.version !== 1 || ledger?.kind !== "GiteaGithubHookRecoveryLedger" || !ledger.records || typeof ledger.records !== "object" || Array.isArray(ledger.records)) {
|
||||
return { enabled: true, ready: false, state: "invalid-or-unreadable", errorType: "hook-recovery-ledger-invalid", counts: {}, entries: [] };
|
||||
}
|
||||
const allowedStates = new Set(["pending", "attempted", "delivery-succeeded-awaiting-source", "source-committed", "terminal-exhausted"]);
|
||||
const records = Object.values(ledger.records);
|
||||
const validRecord = (item) => item && typeof item === "object"
|
||||
&& allowedStates.has(item.state)
|
||||
&& typeof item.repository === "string" && item.repository.length > 0
|
||||
&& (typeof item.hookId === "number" || typeof item.hookId === "string")
|
||||
&& typeof item.deliveryId === "string" && item.deliveryId.length > 0
|
||||
&& Number.isInteger(item.attempts) && item.attempts >= 0
|
||||
&& Number.isInteger(item.cooldownMs) && item.cooldownMs >= 0
|
||||
&& typeof item.updatedAt === "string" && item.updatedAt.length > 0;
|
||||
if (records.some((item) => !validRecord(item))) {
|
||||
return { enabled: true, ready: false, state: "invalid-or-unreadable", errorType: "hook-recovery-ledger-record-invalid", counts: {}, entries: [] };
|
||||
}
|
||||
const counts = { pending: 0, attempted: 0, "delivery-succeeded-awaiting-source": 0, "source-committed": 0, "terminal-exhausted": 0 };
|
||||
for (const item of records) counts[item.state] += 1;
|
||||
const entries = records
|
||||
.sort((left, right) => String(right.updatedAt || "").localeCompare(String(left.updatedAt || "")))
|
||||
.slice(0, 50)
|
||||
.map((item) => ({
|
||||
repository: item.repository,
|
||||
hookId: item.hookId,
|
||||
deliveryId: item.deliveryId,
|
||||
state: item.state,
|
||||
attempts: item.attempts,
|
||||
cooldownMs: item.cooldownMs,
|
||||
nextAt: item.nextAt ?? null,
|
||||
updatedAt: item.updatedAt,
|
||||
lastStatusCode: item.lastStatusCode ?? null,
|
||||
completionReason: item.completionReason ?? null,
|
||||
}));
|
||||
return { enabled: true, ready: true, state: "ready", counts, entries };
|
||||
}
|
||||
|
||||
export function startServer(config = loadRuntimeConfig()) {
|
||||
const controller = config.controller ?? createDurableSyncController(config);
|
||||
const server = createServer(createGithubSyncHandler({ ...config, controller }));
|
||||
|
||||
Reference in New Issue
Block a user