feat: enhance project structure with server and workflow management
- Introduced a new server package with Fastify for handling API requests and workflows. - Implemented user authentication and authorization with JWT and cookie management. - Added endpoints for managing workflows, runs, and user accounts. - Established a dashboard for monitoring workflow status and statistics. - Included a script sandbox for executing user-defined scripts securely. - Updated README with setup instructions and API documentation. - Configured database migrations for user management. - Enhanced logging capabilities for better traceability.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
export function formatTime(iso) {
|
||||
if (!iso) return "—";
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return "—";
|
||||
return d.toLocaleString();
|
||||
}
|
||||
|
||||
export function StatusBadge({ status }) {
|
||||
const cls =
|
||||
status === "success"
|
||||
? "badge-success"
|
||||
: status === "failed"
|
||||
? "badge-error"
|
||||
: status === "running"
|
||||
? "badge-warning"
|
||||
: "badge-ghost";
|
||||
return <span className={`badge badge-sm ${cls}`}>{status ?? "—"}</span>;
|
||||
}
|
||||
|
||||
/** Config load + last-run health for the workflows list. */
|
||||
export function WorkflowStatusBadge({ workflow }) {
|
||||
if (workflow.loadError) {
|
||||
return (
|
||||
<span className="badge badge-error badge-sm" title={workflow.loadError}>
|
||||
broken
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (!workflow.enabled) {
|
||||
return <span className="badge badge-ghost badge-sm">disabled</span>;
|
||||
}
|
||||
if (workflow.lastStatus === "failed") {
|
||||
return <span className="badge badge-error badge-sm">failed</span>;
|
||||
}
|
||||
if (workflow.lastStatus === "running") {
|
||||
return <span className="badge badge-warning badge-sm">running</span>;
|
||||
}
|
||||
if (workflow.lastStatus === "success") {
|
||||
return <span className="badge badge-success badge-sm">working</span>;
|
||||
}
|
||||
return <span className="badge badge-ghost badge-sm">never run</span>;
|
||||
}
|
||||
|
||||
export function levelName(level) {
|
||||
if (level <= 10) return "trace";
|
||||
if (level <= 20) return "debug";
|
||||
if (level <= 30) return "info";
|
||||
if (level <= 40) return "warn";
|
||||
if (level <= 50) return "error";
|
||||
return "fatal";
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
function sanitize(label) {
|
||||
const cleaned = String(label ?? "")
|
||||
.replace(/[^A-Za-z0-9]+/g, "_")
|
||||
.replace(/_+/g, "_")
|
||||
.replace(/^_|_$/g, "")
|
||||
.slice(0, 48);
|
||||
return cleaned || "node";
|
||||
}
|
||||
|
||||
function parseStep(step) {
|
||||
if (typeof step === "string") return step;
|
||||
if (step?.script) return step.script;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function workflowToArchitecture(parsed) {
|
||||
/** @type {Record<string, string>} */
|
||||
const scriptIds = {};
|
||||
if (!parsed || typeof parsed !== "object") {
|
||||
return { chart: "", scriptIds };
|
||||
}
|
||||
|
||||
const groupId = "wf";
|
||||
const name = sanitize(parsed.name || "workflow");
|
||||
const lines = ["architecture-beta", ` group ${groupId}[${name}]`];
|
||||
|
||||
const triggerIds = [];
|
||||
const triggers = Array.isArray(parsed.triggers) ? parsed.triggers : [];
|
||||
triggers.forEach((t, i) => {
|
||||
const id = `t${i}`;
|
||||
const icon = t?.type === "cron" ? "cloud" : "internet";
|
||||
const label =
|
||||
t?.type === "cron"
|
||||
? `cron ${t.schedule ?? ""}`
|
||||
: `${t?.method ?? "POST"} ${t?.path ?? ""}`;
|
||||
lines.push(` service ${id}(${icon})[${sanitize(label)}] in ${groupId}`);
|
||||
triggerIds.push(id);
|
||||
});
|
||||
|
||||
const scripts = Array.isArray(parsed.scripts) ? parsed.scripts : [];
|
||||
let prev = null;
|
||||
let firstScript = null;
|
||||
scripts.forEach((step, i) => {
|
||||
const script = parseStep(step);
|
||||
if (!script) return;
|
||||
const id = `s${i}`;
|
||||
scriptIds[id] = script;
|
||||
lines.push(` service ${id}(server)[${sanitize(script)}] in ${groupId}`);
|
||||
if (!firstScript) firstScript = id;
|
||||
if (prev) lines.push(` ${prev}:R -- L:${id}`);
|
||||
prev = id;
|
||||
});
|
||||
|
||||
// All triggers point at the first script (not chained to each other).
|
||||
if (firstScript) {
|
||||
for (const tid of triggerIds) {
|
||||
lines.push(` ${tid}:R -- L:${firstScript}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep multiple triggers in one column so they don't overlap.
|
||||
if (triggerIds.length >= 2) {
|
||||
lines.push(` align column ${triggerIds.join(" ")}`);
|
||||
}
|
||||
|
||||
return { chart: lines.join("\n"), scriptIds };
|
||||
}
|
||||
Reference in New Issue
Block a user