feat: global Docker health indicator and graceful degradation

- GET /api/health endpoint returning Docker connectivity status
- Sidebar shows Docker connection dot (green=connected, red=disconnected)
- Stale scanner returns store-only results when Docker is unavailable
- Polls health every 30s
This commit is contained in:
2026-03-30 13:43:33 +03:00
parent b57b164be0
commit 37cfa090ac
15 changed files with 317 additions and 277 deletions
+8 -7
View File
@@ -34,12 +34,7 @@ class ApiError extends Error {
}
}
function getAuthToken(): string | null {
if (typeof localStorage !== 'undefined') {
return localStorage.getItem('auth_token');
}
return null;
}
import { getAuthToken, clearAuth } from './auth';
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const token = getAuthToken();
@@ -58,7 +53,7 @@ async function request<T>(path: string, init?: RequestInit): Promise<T> {
// Redirect to login on 401 (expired/missing token).
if (res.status === 401 && typeof window !== 'undefined' && !path.includes('/auth/')) {
localStorage.removeItem('auth_token');
clearAuth();
window.location.href = '/login';
throw new ApiError('Authentication required', 401);
}
@@ -270,6 +265,12 @@ export function listNpmCertificates(): Promise<NpmCertificate[]> {
return get<NpmCertificate[]>('/api/settings/npm-certificates');
}
// ── Health ──────────────────────────────────────────────────────────
export function getHealth(): Promise<{ docker: boolean }> {
return get<{ docker: boolean }>('/api/health');
}
// ── Auth ─────────────────────────────────────────────────────────────
export function login(username: string, password: string): Promise<{ token: string; expires_at: string }> {