- Added a new function to list consecutive failure streaks for workflows, allowing tracking of workflows that have failed multiple times in a row. - Integrated the consecutive failure data into the dashboard, displaying streaks and counts for better visibility of workflow health. - Created a dedicated FailuresPage to present detailed information about consecutive failures, enhancing user experience and monitoring capabilities. - Updated API endpoints and hooks to support the new failure tracking features, ensuring seamless data retrieval and display. This enhancement improves the ability to monitor and respond to workflow failures, contributing to overall system reliability.
90 lines
2.7 KiB
JavaScript
90 lines
2.7 KiB
JavaScript
import yaml from "yaml";
|
|
import * as store from "../../store.js";
|
|
import * as fsStore from "../../fs-store.js";
|
|
|
|
/**
|
|
* @param {{ workflows: Map<string, any>, loadErrors: Map<string, string> }} registry
|
|
*/
|
|
export default function dashboardPluginFactory(registry) {
|
|
/**
|
|
* @param {import("fastify").FastifyInstance} fastify
|
|
*/
|
|
return async function dashboardPlugin(fastify) {
|
|
fastify.get("/dashboard", async () => {
|
|
const owners = fsStore.listOwners();
|
|
let workflowCount = 0;
|
|
let enabledCount = 0;
|
|
let brokenCount = registry.loadErrors.size;
|
|
const brokenWorkflows = [];
|
|
|
|
for (const [key, message] of registry.loadErrors) {
|
|
const [owner, ...rest] = key.split("/");
|
|
brokenWorkflows.push({
|
|
key,
|
|
owner,
|
|
file: rest.join("/"),
|
|
loadError: message,
|
|
});
|
|
}
|
|
|
|
for (const owner of owners) {
|
|
let registered = [];
|
|
try {
|
|
registered = fsStore.readRegisters(owner);
|
|
} catch {
|
|
registered = [];
|
|
}
|
|
const files = [
|
|
...new Set([...registered, ...fsStore.listOwnerYamlFiles(owner)]),
|
|
];
|
|
for (const file of files) {
|
|
workflowCount += 1;
|
|
const key = `${owner}/${file}`;
|
|
const loaded = registry.workflows.get(key);
|
|
if (loaded?.workflow && loaded.workflow.enabled !== false) {
|
|
enabledCount += 1;
|
|
} else if (!loaded && !registry.loadErrors.has(key)) {
|
|
const raw = fsStore.readWorkflowYaml(owner, file);
|
|
if (raw) {
|
|
try {
|
|
const parsed = yaml.parse(raw);
|
|
if (parsed?.enabled !== false) enabledCount += 1;
|
|
} catch (err) {
|
|
brokenCount += 1;
|
|
brokenWorkflows.push({
|
|
key,
|
|
owner,
|
|
file,
|
|
loadError: err instanceof Error ? err.message : String(err),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const [running, streaks, failedEvents, recent] = await Promise.all([
|
|
store.listRuns({ status: "running", limit: 10 }),
|
|
store.listConsecutiveFailureStreaks({ minCount: 4, limit: 10 }),
|
|
store.listRuns({ status: "failed", limit: 5 }),
|
|
store.listRuns({ limit: 10 }),
|
|
]);
|
|
|
|
return {
|
|
workflowCount,
|
|
scriptCount: fsStore.listScriptFiles().length,
|
|
enabledCount,
|
|
brokenCount,
|
|
running,
|
|
needsAttention: {
|
|
consecutiveFailures: streaks.items,
|
|
consecutiveFailureCount: streaks.total,
|
|
brokenWorkflows,
|
|
},
|
|
failedEvents,
|
|
recent,
|
|
};
|
|
});
|
|
};
|
|
}
|