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:
commit
f732cb080b
59 files changed
+3677
No files matched your search
@@ -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;
|
||||
Reference in new issue
Block a user