133 lines
3.8 KiB
React
133 lines
3.8 KiB
React
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
import axios from "axios";
|
|
|
|
const MaintenanceContext = createContext(null);
|
|
|
|
const INITIAL_STATE = {
|
|
maintenance: null,
|
|
update: null,
|
|
script: null,
|
|
loading: true,
|
|
error: "",
|
|
lastUpdated: null
|
|
};
|
|
|
|
export default function MaintenanceProvider({ children }) {
|
|
const [state, setState] = useState(INITIAL_STATE);
|
|
const pollingRef = useRef(null);
|
|
const wasRunningRef = useRef(false);
|
|
|
|
const applyState = useCallback((partial) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
...partial
|
|
}));
|
|
}, []);
|
|
|
|
const fetchConfig = useCallback(async () => {
|
|
applyState({ loading: true, error: "" });
|
|
try {
|
|
const response = await axios.get("/api/maintenance/config");
|
|
const data = response.data ?? {};
|
|
applyState({
|
|
maintenance: data.maintenance ?? null,
|
|
update: data.update ?? null,
|
|
script: data.script ?? null,
|
|
loading: false,
|
|
error: "",
|
|
lastUpdated: new Date()
|
|
});
|
|
} catch (err) {
|
|
const message = err.response?.data?.error || err.message || "Fehler beim Laden der Wartungsdaten";
|
|
applyState({ loading: false, error: message });
|
|
}
|
|
}, [applyState]);
|
|
|
|
const refreshUpdateStatus = useCallback(async () => {
|
|
try {
|
|
const response = await axios.get("/api/maintenance/update-status");
|
|
const data = response.data ?? {};
|
|
setState((prev) => ({
|
|
...prev,
|
|
maintenance: data.maintenance ?? prev.maintenance,
|
|
update: data.update ?? prev.update,
|
|
error: ""
|
|
}));
|
|
} catch (err) {
|
|
const message = err.response?.data?.error || err.message || "Fehler beim Aktualisieren des Wartungsstatus";
|
|
setState((prev) => ({ ...prev, error: message }));
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
fetchConfig();
|
|
}, [fetchConfig]);
|
|
|
|
useEffect(() => {
|
|
if (state.update?.running) {
|
|
refreshUpdateStatus();
|
|
const intervalId = setInterval(() => {
|
|
refreshUpdateStatus();
|
|
}, 5000);
|
|
pollingRef.current = intervalId;
|
|
return () => clearInterval(intervalId);
|
|
}
|
|
if (pollingRef.current) {
|
|
clearInterval(pollingRef.current);
|
|
pollingRef.current = null;
|
|
}
|
|
return undefined;
|
|
}, [state.update?.running, refreshUpdateStatus]);
|
|
|
|
useEffect(() => {
|
|
const currentlyRunning = Boolean(state.update?.running);
|
|
if (!currentlyRunning && wasRunningRef.current) {
|
|
fetchConfig();
|
|
}
|
|
wasRunningRef.current = currentlyRunning;
|
|
}, [state.update?.running, fetchConfig]);
|
|
|
|
const triggerUpdate = useCallback(async (payload = {}) => {
|
|
await axios.post("/api/maintenance/portainer-update", payload);
|
|
await refreshUpdateStatus();
|
|
}, [refreshUpdateStatus]);
|
|
|
|
const saveScript = useCallback(async (script) => {
|
|
await axios.put("/api/maintenance/update-script", { script });
|
|
await fetchConfig();
|
|
}, [fetchConfig]);
|
|
|
|
const resetScript = useCallback(async () => {
|
|
await axios.delete("/api/maintenance/update-script");
|
|
await fetchConfig();
|
|
}, [fetchConfig]);
|
|
|
|
const value = useMemo(() => ({
|
|
maintenance: state.maintenance,
|
|
update: state.update,
|
|
script: state.script,
|
|
loading: state.loading,
|
|
error: state.error,
|
|
lastUpdated: state.lastUpdated,
|
|
refreshConfig: fetchConfig,
|
|
refreshUpdateStatus,
|
|
triggerUpdate,
|
|
saveScript,
|
|
resetScript
|
|
}), [state, fetchConfig, refreshUpdateStatus, triggerUpdate, saveScript, resetScript]);
|
|
|
|
return (
|
|
<MaintenanceContext.Provider value={value}>
|
|
{children}
|
|
</MaintenanceContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useMaintenance() {
|
|
const context = useContext(MaintenanceContext);
|
|
if (!context) {
|
|
throw new Error('useMaintenance must be used within a MaintenanceProvider');
|
|
}
|
|
return context;
|
|
}
|