Initial commit: StackPilot Phase 1 (Core)

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>
This commit is contained in:
menzeljandClaude Opus 4.8 committed 2026-06-07 16:04:58 +00:00
commit f732cb080b
59 files changed
+3677

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
import axios, { AxiosError } from "axios";
import { useAuthStore } from "@/store/auth";
const api = axios.create({ baseURL: "/" });
api.interceptors.request.use((config) => {
const token = useAuthStore.getState().accessToken;
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
let refreshing: Promise<string | null> | null = null;
api.interceptors.response.use(
(res) => res,
async (error: AxiosError) => {
const original = error.config as any;
if (error.response?.status === 401 && original && !original._retry) {
original._retry = true;
if (!refreshing) {
refreshing = useAuthStore
.getState()
.refresh()
.finally(() => {
refreshing = null;
});
}
const newToken = await refreshing;
if (newToken) {
original.headers.Authorization = `Bearer ${newToken}`;
return api(original);
}
useAuthStore.getState().logout();
}
return Promise.reject(error);
}
);
export function apiErrorMessage(err: unknown): string {
const e = err as AxiosError<any>;
const detail = e?.response?.data?.detail;
if (typeof detail === "string") return detail;
if (detail?.detail) return `${detail.error}: ${detail.detail}`;
if (detail?.error) return detail.error;
return e?.message || "Unexpected error";
}
export default api;