refactor: useSchedulesStore.ts
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
// Logs slice: per-node-run log fetch + display state.
|
||||
// toggleLog owns the open/closed toggle and lazily loads content from the
|
||||
// signed URL on first open.
|
||||
|
||||
import type { StateCreator } from "zustand";
|
||||
|
||||
import { requireApi } from "./helpers";
|
||||
import type { LogState } from "./types";
|
||||
import type { SchedulesStore } from "./useSchedulesStore";
|
||||
|
||||
// ---- State ----
|
||||
|
||||
export type LogsSliceState = {
|
||||
openLogNodeRunIds: Set<string>;
|
||||
logStates: Record<string, LogState>;
|
||||
};
|
||||
|
||||
// ---- Actions ----
|
||||
|
||||
export type LogsSliceActions = {
|
||||
// pure setters
|
||||
setOpenLogNodeRunIds: (
|
||||
value: Set<string> | ((current: Set<string>) => Set<string>),
|
||||
) => void;
|
||||
setLogStates: (
|
||||
updater: (current: Record<string, LogState>) => Record<string, LogState>,
|
||||
) => void;
|
||||
|
||||
// mutations
|
||||
toggleLog: (runId: string, nodeRunId: string) => Promise<void>;
|
||||
};
|
||||
|
||||
// `SchedulesStore` is the combined state+actions of all slices, so `get()`
|
||||
// returns a fully typed snapshot — no `any` propagation.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const createLogsSlice: StateCreator<SchedulesStore, [], [], LogsSliceState & LogsSliceActions> = (set, get) => ({
|
||||
// ---- initial state ----
|
||||
openLogNodeRunIds: new Set(),
|
||||
logStates: {},
|
||||
|
||||
// ---- pure setters ----
|
||||
|
||||
setOpenLogNodeRunIds: (value) =>
|
||||
set((s: any) => ({
|
||||
openLogNodeRunIds:
|
||||
typeof value === "function"
|
||||
? (value as (current: Set<string>) => Set<string>)(
|
||||
s.openLogNodeRunIds,
|
||||
)
|
||||
: value,
|
||||
})),
|
||||
setLogStates: (updater) =>
|
||||
set((s: any) => ({ logStates: updater(s.logStates) })),
|
||||
|
||||
// ---- mutations ----
|
||||
|
||||
toggleLog: async (runId, nodeRunId) => {
|
||||
const api = requireApi();
|
||||
const state = get();
|
||||
const open = state.openLogNodeRunIds;
|
||||
if (open.has(nodeRunId)) {
|
||||
const next = new Set(open);
|
||||
next.delete(nodeRunId);
|
||||
set({ openLogNodeRunIds: next });
|
||||
return;
|
||||
}
|
||||
const next = new Set(open);
|
||||
next.add(nodeRunId);
|
||||
set({ openLogNodeRunIds: next });
|
||||
if (state.logStates[nodeRunId]?.content) return;
|
||||
set((s: any) => ({
|
||||
logStates: {
|
||||
...s.logStates,
|
||||
[nodeRunId]: {
|
||||
loading: true,
|
||||
content: null,
|
||||
fileName: null,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
}));
|
||||
try {
|
||||
const artifacts = await api.getScheduleNodeRunArtifacts(runId, nodeRunId);
|
||||
if (!artifacts.log) throw new Error("本次节点执行没有可用日志");
|
||||
const response = await fetch(artifacts.log.url, {
|
||||
credentials: "same-origin",
|
||||
cache: "no-store",
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`日志读取失败(HTTP ${response.status})`);
|
||||
}
|
||||
const content = await response.text();
|
||||
set((s: any) => ({
|
||||
logStates: {
|
||||
...s.logStates,
|
||||
[nodeRunId]: {
|
||||
loading: false,
|
||||
content,
|
||||
fileName: artifacts.log?.file_name ?? null,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
}));
|
||||
} catch (error) {
|
||||
set((s: any) => ({
|
||||
logStates: {
|
||||
...s.logStates,
|
||||
[nodeRunId]: {
|
||||
loading: false,
|
||||
content: null,
|
||||
fileName: null,
|
||||
error: error instanceof Error ? error.message : "日志读取失败",
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user