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:
2026-08-14 14:25:37 +07:00
parent 5574d6f723
commit 023cf83cfa
48 changed files with 6127 additions and 359 deletions
+82
View File
@@ -0,0 +1,82 @@
import { Link, useSearchParams } from "react-router-dom";
import { useRuns } from "../api/hooks.js";
import { formatTime, StatusBadge } from "../lib/format.jsx";
export function EventsPage() {
const [params, setParams] = useSearchParams();
const workflow = params.get("workflow") || "";
const status = params.get("status") || "";
const { data: runs = [], isLoading } = useRuns({
workflow: workflow || undefined,
status: status || undefined,
limit: 100,
});
function update(key, value) {
const next = new URLSearchParams(params);
if (value) next.set(key, value);
else next.delete(key);
setParams(next);
}
return (
<div className="space-y-4">
<h1 className="text-xl font-semibold">Events</h1>
<div className="flex flex-col sm:flex-row gap-2">
<input
className="input input-sm w-full sm:max-w-sm"
placeholder="workflow key"
value={workflow}
onChange={(e) => update("workflow", e.target.value)}
/>
<select
className="select select-sm w-full sm:max-w-xs"
value={status}
onChange={(e) => update("status", e.target.value)}
>
<option value="">all statuses</option>
<option value="running">running</option>
<option value="success">success</option>
<option value="failed">failed</option>
</select>
</div>
{isLoading ? (
<span className="loading loading-spinner" />
) : (
<div className="overflow-x-auto">
<table className="table table-sm">
<thead>
<tr>
<th>Status</th>
<th>Workflow</th>
<th>Trigger</th>
<th>Started</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{runs.map((r) => (
<tr key={r.id} className="hover">
<td>
<StatusBadge status={r.status} />
</td>
<td>
<Link className="link" to={`/events/${r.id}`}>
{r.workflow_name || r.workflow}
</Link>
</td>
<td className="text-xs">
{r.trigger_type}
{r.trigger_detail ? ` · ${r.trigger_detail}` : ""}
</td>
<td className="whitespace-nowrap">{formatTime(r.started_at)}</td>
<td>{r.duration_ms != null ? `${r.duration_ms}ms` : "—"}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
}