feat: router with RJSF workflows and access management UI
This commit is contained in:
parent
46ecab00db
commit
62d01b5097
15 changed files with 948 additions and 144 deletions
|
|
@ -69,6 +69,33 @@ export interface SyncReport {
|
|||
errors: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface Me {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
role: string;
|
||||
tenant_id: string;
|
||||
}
|
||||
|
||||
export interface AccessRequest {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
reason: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
decided_at: string | null;
|
||||
decided_by: string | null;
|
||||
}
|
||||
|
||||
export interface InputSchema {
|
||||
flow_id: string;
|
||||
version_no: number;
|
||||
json_schema: Record<string, unknown>;
|
||||
ui_schema: Record<string, unknown>;
|
||||
fallback: string | null;
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
constructor(status: number, message: string) {
|
||||
|
|
@ -79,6 +106,8 @@ export class ApiError extends Error {
|
|||
|
||||
async function req<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(path, {
|
||||
// Session cookie (HttpOnly) must travel with every request.
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
...init,
|
||||
});
|
||||
|
|
@ -114,11 +143,14 @@ export const api = {
|
|||
req<FlowVersion>(`/api/flows/${flowId}/versions/${versionNo}/deploy`, {
|
||||
method: "POST",
|
||||
}),
|
||||
runFlow: (flowId: string, input: unknown, tenantId: string) =>
|
||||
runFlow: (flowId: string, input: unknown) =>
|
||||
req<Run>(`/api/flows/${flowId}/run`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ input, tenant_id: tenantId }),
|
||||
// Tenant comes from the session server-side; no tenant box anymore.
|
||||
body: JSON.stringify({ input }),
|
||||
}),
|
||||
getInputSchema: (flowId: string) =>
|
||||
req<InputSchema>(`/api/flows/${flowId}/input-schema`),
|
||||
listRuns: (flowId: string) => req<Run[]>(`/api/flows/${flowId}/runs`),
|
||||
listApprovals: () => req<ApprovalTask[]>("/api/approvals"),
|
||||
decide: (
|
||||
|
|
@ -148,4 +180,33 @@ export const api = {
|
|||
req<SyncReport>("/api/flows/sync", {
|
||||
method: "POST",
|
||||
}),
|
||||
me: async (): Promise<Me | null> => {
|
||||
try {
|
||||
return await req<Me>("/api/auth/me");
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError && e.status === 401) return null;
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
consumeMagic: (token: string) =>
|
||||
req<Me>("/api/auth/magic/consume", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ token }),
|
||||
}),
|
||||
logout: () =>
|
||||
req<{ message: string }>("/api/auth/logout", { method: "POST" }),
|
||||
requestAccess: (name: string, email: string, reason: string) =>
|
||||
req<AccessRequest>("/api/access/requests", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name, email, reason }),
|
||||
}),
|
||||
listAccessRequests: (status?: string) =>
|
||||
req<AccessRequest[]>(
|
||||
`/api/access/requests${status ? `?status=${status}` : ""}`,
|
||||
),
|
||||
decideAccess: (id: string, approve: boolean) =>
|
||||
req<{ request: AccessRequest; email_sent: boolean }>(
|
||||
`/api/access/requests/${id}/${approve ? "approve" : "reject"}`,
|
||||
{ method: "POST" },
|
||||
),
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue