Self-hosted Docker Compose manager. - Backend: FastAPI + docker-py + SQLite (JWT auth, file-first stacks, lifecycle, live status, WebSocket logs, docker-run converter, audit log) - Frontend: React + Vite + Tailwind (login/setup, dashboard, stacks, stack detail, Monaco editor, dark/light theme) - Deployment: docker-compose.yml, Dockerfiles, nginx reverse proxy Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.2 KiB
TypeScript
40 lines
1.2 KiB
TypeScript
import { useState } from "react";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import { toast } from "sonner";
|
|
import { stacksApi } from "@/api/stacks";
|
|
import { apiErrorMessage } from "@/api/client";
|
|
|
|
export function useStackActions() {
|
|
const qc = useQueryClient();
|
|
const [busyId, setBusyId] = useState<string | null>(null);
|
|
|
|
const run = async (
|
|
id: string,
|
|
label: string,
|
|
fn: (id: string) => Promise<unknown>
|
|
) => {
|
|
setBusyId(id);
|
|
const t = toast.loading(`${label} ${id}…`);
|
|
try {
|
|
await fn(id);
|
|
toast.success(`${label} ${id} ✓`, { id: t });
|
|
qc.invalidateQueries({ queryKey: ["stacks"] });
|
|
qc.invalidateQueries({ queryKey: ["stack", id] });
|
|
} catch (err) {
|
|
toast.error(apiErrorMessage(err), { id: t });
|
|
} finally {
|
|
setBusyId(null);
|
|
}
|
|
};
|
|
|
|
return {
|
|
busyId,
|
|
start: (id: string) => run(id, "Starting", stacksApi.start),
|
|
stop: (id: string) => run(id, "Stopping", stacksApi.stop),
|
|
restart: (id: string) => run(id, "Restarting", stacksApi.restart),
|
|
pull: (id: string) => run(id, "Pulling", stacksApi.pull),
|
|
updateImages: (id: string) => run(id, "Updating", stacksApi.update_images),
|
|
down: (id: string) => run(id, "Tearing down", stacksApi.down),
|
|
};
|
|
}
|