Introduce plugin/<id> installs (zip, HTTPS git, example, fork), jerapah-plugin.json manifests with jerapah semver ranges, isolated plugin dirs under data/plugins, and app version 0.1.0. Core scripts are non-editable; get-current-time moves to examples/plugins. Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
575 lines
16 KiB
JavaScript
575 lines
16 KiB
JavaScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import { api, opsApi } from "./client.js";
|
|
|
|
export function useBootstrap() {
|
|
return useQuery({
|
|
queryKey: ["bootstrap"],
|
|
queryFn: async () => (await api.get("/auth/bootstrap")).data,
|
|
});
|
|
}
|
|
|
|
export function useMe() {
|
|
return useQuery({
|
|
queryKey: ["me"],
|
|
queryFn: async () => {
|
|
try {
|
|
return (await api.get("/auth/me")).data;
|
|
} catch (err) {
|
|
if (err.response?.status === 401) return { user: null };
|
|
throw err;
|
|
}
|
|
},
|
|
retry: false,
|
|
});
|
|
}
|
|
|
|
export function useLogin() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.post("/auth/login", body)).data,
|
|
onSuccess: (data) => {
|
|
qc.setQueryData(["me"], data);
|
|
qc.invalidateQueries({ queryKey: ["bootstrap"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRegister() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.post("/auth/register", body)).data,
|
|
onSuccess: (data) => {
|
|
qc.setQueryData(["me"], data);
|
|
qc.invalidateQueries({ queryKey: ["bootstrap"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useLogout() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await api.post("/auth/logout")).data,
|
|
onSuccess: () => {
|
|
qc.setQueryData(["me"], null);
|
|
qc.clear();
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDashboard() {
|
|
return useQuery({
|
|
queryKey: ["dashboard"],
|
|
queryFn: async () => (await api.get("/dashboard")).data,
|
|
refetchInterval: (query) =>
|
|
query.state.data?.running?.length ? 4000 : 15000,
|
|
});
|
|
}
|
|
|
|
export function useScripts() {
|
|
return useQuery({
|
|
queryKey: ["scripts"],
|
|
queryFn: async () => (await api.get("/scripts")).data.scripts,
|
|
});
|
|
}
|
|
|
|
export function useScript(name, enabled = true) {
|
|
return useQuery({
|
|
queryKey: ["scripts", name],
|
|
queryFn: async () => (await api.get(`/scripts/${encodeURIComponent(name)}`)).data,
|
|
enabled: Boolean(name) && enabled,
|
|
});
|
|
}
|
|
|
|
export function useSaveScript() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ name, content }) =>
|
|
(await api.put(`/scripts/${encodeURIComponent(name)}`, { content })).data,
|
|
onSuccess: (_data, vars) => {
|
|
qc.invalidateQueries({ queryKey: ["scripts"] });
|
|
qc.invalidateQueries({ queryKey: ["scripts", vars.name] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
qc.invalidateQueries({ queryKey: ["ops-status"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useCreatePlugin() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ id, content, description }) =>
|
|
(await api.post("/plugins/create", { id, content, description })).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["scripts"] });
|
|
qc.invalidateQueries({ queryKey: ["ops-status"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useForkScript() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ name, id, description }) =>
|
|
(
|
|
await api.post(`/scripts/${encodeURIComponent(name)}/fork`, {
|
|
id,
|
|
description,
|
|
})
|
|
).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["scripts"] });
|
|
qc.invalidateQueries({ queryKey: ["ops-status"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useInstallPlugin() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.post("/plugins/install", body)).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["scripts"] });
|
|
qc.invalidateQueries({ queryKey: ["ops-status"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDeleteScript() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (name) =>
|
|
(await api.delete(`/scripts/${encodeURIComponent(name)}`)).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["scripts"] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDryRunScript() {
|
|
return useMutation({
|
|
mutationFn: async ({ name, content, data, context, config, owner }) =>
|
|
(
|
|
await api.post(`/scripts/${encodeURIComponent(name)}/dry-run`, {
|
|
content,
|
|
data,
|
|
context,
|
|
config,
|
|
owner,
|
|
})
|
|
).data,
|
|
});
|
|
}
|
|
|
|
export function useWorkflows(owner) {
|
|
return useQuery({
|
|
queryKey: ["workflows", owner ?? "all"],
|
|
queryFn: async () => {
|
|
const params = owner ? { owner } : {};
|
|
return (await api.get("/workflows", { params })).data.workflows;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useWorkflow(owner, file, enabled = true) {
|
|
return useQuery({
|
|
queryKey: ["workflows", owner, file],
|
|
queryFn: async () =>
|
|
(await api.get(`/workflows/${encodeURIComponent(owner)}/${encodeURIComponent(file)}`))
|
|
.data,
|
|
enabled: Boolean(owner && file) && enabled,
|
|
});
|
|
}
|
|
|
|
export function useOwners() {
|
|
return useQuery({
|
|
queryKey: ["owners"],
|
|
queryFn: async () => (await api.get("/owners")).data.owners,
|
|
});
|
|
}
|
|
|
|
export function useSaveWorkflow() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ owner, file, content }) =>
|
|
(
|
|
await api.put(
|
|
`/workflows/${encodeURIComponent(owner)}/${encodeURIComponent(file)}`,
|
|
{ content },
|
|
)
|
|
).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
qc.invalidateQueries({ queryKey: ["owners"] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useSetWorkflowEnabled() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ owner, file, enabled }) =>
|
|
(
|
|
await api.patch(
|
|
`/workflows/${encodeURIComponent(owner)}/${encodeURIComponent(file)}`,
|
|
{ enabled },
|
|
)
|
|
).data,
|
|
onSuccess: (_data, vars) => {
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
qc.invalidateQueries({ queryKey: ["workflows", vars.owner, vars.file] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDeleteWorkflow() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ owner, file }) =>
|
|
(
|
|
await api.delete(
|
|
`/workflows/${encodeURIComponent(owner)}/${encodeURIComponent(file)}`,
|
|
)
|
|
).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDuplicateWorkflow() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ owner, file, destOwner, destFile }) =>
|
|
(
|
|
await api.post(
|
|
`/workflows/${encodeURIComponent(owner)}/${encodeURIComponent(file)}/duplicate`,
|
|
{
|
|
...(destOwner ? { owner: destOwner } : {}),
|
|
...(destFile ? { file: destFile } : {}),
|
|
},
|
|
)
|
|
).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
qc.invalidateQueries({ queryKey: ["owners"] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRunWorkflow() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ owner, file, data }) =>
|
|
(
|
|
await api.post(
|
|
`/workflows/${encodeURIComponent(owner)}/${encodeURIComponent(file)}/run`,
|
|
data !== undefined ? { data } : {},
|
|
)
|
|
).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
qc.invalidateQueries({ queryKey: ["runs"] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useReregisterWorkflows() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await api.post("/workflows/reregister")).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
qc.invalidateQueries({ queryKey: ["scripts"] });
|
|
qc.invalidateQueries({ queryKey: ["owners"] });
|
|
qc.invalidateQueries({ queryKey: ["dashboard"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRuns(filters = {}) {
|
|
const { owner, workflow, status, limit } = filters;
|
|
return useQuery({
|
|
queryKey: ["runs", { owner, workflow, status, limit }],
|
|
queryFn: async () => {
|
|
const params = {};
|
|
if (owner) params.owner = owner;
|
|
if (workflow) params.workflow = workflow;
|
|
if (status) params.status = status;
|
|
if (limit) params.limit = limit;
|
|
return (await api.get("/runs", { params })).data.runs;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useConsecutiveFailures(limit) {
|
|
return useQuery({
|
|
queryKey: ["consecutive-failures", limit ?? "all"],
|
|
queryFn: async () => {
|
|
const params = {};
|
|
if (limit) params.limit = limit;
|
|
return (await api.get("/consecutive-failures", { params })).data;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRun(id) {
|
|
return useQuery({
|
|
queryKey: ["runs", id],
|
|
queryFn: async () => (await api.get(`/runs/${encodeURIComponent(id)}`)).data,
|
|
enabled: Boolean(id),
|
|
refetchInterval: (query) => {
|
|
const status = query.state.data?.status;
|
|
return status === "running" || status === "queued" ? 1500 : false;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useUsers() {
|
|
return useQuery({
|
|
queryKey: ["users"],
|
|
queryFn: async () => (await api.get("/users")).data.users,
|
|
});
|
|
}
|
|
|
|
export function useCreateUser() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.post("/users", body)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["users"] }),
|
|
});
|
|
}
|
|
|
|
export function useUpdateUser() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ id, ...body }) =>
|
|
(await api.patch(`/users/${encodeURIComponent(id)}`, body)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["users"] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteUser() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (id) =>
|
|
(await api.delete(`/users/${encodeURIComponent(id)}`)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["users"] }),
|
|
});
|
|
}
|
|
|
|
export function useSecrets(owner) {
|
|
return useQuery({
|
|
queryKey: ["secrets", owner ?? "all"],
|
|
queryFn: async () => {
|
|
const params = owner ? { owner } : {};
|
|
return (await api.get("/secrets", { params })).data.secrets;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useUpsertSecret() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.put("/secrets", body)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["secrets"] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteSecret() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (id) =>
|
|
(await api.delete(`/secrets/${encodeURIComponent(id)}`)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["secrets"] }),
|
|
});
|
|
}
|
|
|
|
export function useVariables(owner) {
|
|
return useQuery({
|
|
queryKey: ["variables", owner ?? "all"],
|
|
queryFn: async () => {
|
|
const params = owner ? { owner } : {};
|
|
return (await api.get("/variables", { params })).data.variables;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useUpsertVariable() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.put("/variables", body)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["variables"] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteVariable() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (id) =>
|
|
(await api.delete(`/variables/${encodeURIComponent(id)}`)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["variables"] }),
|
|
});
|
|
}
|
|
|
|
export function useKvNamespaces() {
|
|
return useQuery({
|
|
queryKey: ["kv", "namespaces"],
|
|
queryFn: async () => (await api.get("/kv/namespaces")).data.namespaces,
|
|
});
|
|
}
|
|
|
|
export function useKv(filters = {}) {
|
|
const { namespace, q, limit, offset } = filters;
|
|
return useQuery({
|
|
queryKey: ["kv", { namespace, q, limit, offset }],
|
|
queryFn: async () => {
|
|
const params = {};
|
|
if (namespace) params.namespace = namespace;
|
|
if (q) params.q = q;
|
|
if (limit != null) params.limit = limit;
|
|
if (offset != null) params.offset = offset;
|
|
return (await api.get("/kv", { params })).data;
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useHttpPages() {
|
|
return useQuery({
|
|
queryKey: ["http-pages"],
|
|
queryFn: async () => (await api.get("/http-pages")).data.pages,
|
|
});
|
|
}
|
|
|
|
export function useUpsertHttpPage() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.put("/http-pages", body)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["http-pages"] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteHttpPage() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (id) =>
|
|
(await api.delete(`/http-pages/${encodeURIComponent(id)}`)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["http-pages"] }),
|
|
});
|
|
}
|
|
|
|
export function useHttpAuths() {
|
|
return useQuery({
|
|
queryKey: ["http-auths"],
|
|
queryFn: async () => (await api.get("/http-auths")).data.auths,
|
|
});
|
|
}
|
|
|
|
export function useUpsertHttpAuth() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (body) => (await api.put("/http-auths", body)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["http-auths"] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteHttpAuth() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (id) =>
|
|
(await api.delete(`/http-auths/${encodeURIComponent(id)}`)).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["http-auths"] }),
|
|
});
|
|
}
|
|
|
|
/** Fetch plaintext literals only (not encrypted secrets). */
|
|
export async function fetchHttpAuthLiterals(name) {
|
|
return (await api.get(`/http-auths/${encodeURIComponent(name)}/reveal`)).data;
|
|
}
|
|
|
|
export function useOpsStatus(enabled = true) {
|
|
return useQuery({
|
|
queryKey: ["ops-status"],
|
|
queryFn: async () => (await opsApi.get("/status")).data,
|
|
enabled,
|
|
retry: false,
|
|
refetchInterval: enabled ? 3000 : false,
|
|
});
|
|
}
|
|
|
|
export function useOpsPause() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await opsApi.post("/pause")).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|
|
export function useOpsResume() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await opsApi.post("/resume")).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|
|
export function useOpsReload() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await opsApi.post("/reload")).data,
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["ops-status"] });
|
|
qc.invalidateQueries({ queryKey: ["workflows"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useOpsRestart() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ force = false } = {}) =>
|
|
(await opsApi.post("/restart", { force })).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|
|
export function useOpsScale() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({ workers, force = false }) =>
|
|
(await opsApi.post("/scale", { workers, force })).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|
|
export function useOpsHttpStart() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await opsApi.post("/http/start")).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|
|
export function useOpsHttpStop() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async () => (await opsApi.post("/http/stop")).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|
|
export function useOpsBumpGeneration() {
|
|
const qc = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async (reason) =>
|
|
(await opsApi.post("/generation/bump", { reason })).data,
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["ops-status"] }),
|
|
});
|
|
}
|
|
|