feat: scaffold UI web (React/Vite/Tailwind) + Login (v0.22.0)
Package web/ : React 19 + Vite 8 + Tailwind 4 + react-router 7 + PWA. Tokens dark HUD Jarvis-red, client API, contexte auth JWT, shell + garde de route, écran Login (mot de passe + TOTP). Chat/Review en stubs. Build OK, 0 vuln. docs/ui-design.md. Palier de risque : reversible (front statique, aucun accès infra direct). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// Client de l'API CHLOVA. Same-origin en prod (le backend sert le SPA) ;
|
||||
// en dev, Vite proxifie /api vers le backend.
|
||||
|
||||
export interface Asset {
|
||||
id: string;
|
||||
type: string;
|
||||
version: string;
|
||||
riskTier: string;
|
||||
status: string;
|
||||
createdAt: number;
|
||||
expiresAt: number | null;
|
||||
execCount: number;
|
||||
commitLink: string | null;
|
||||
docLink: string | null;
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
public status: number,
|
||||
message: string,
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function req<T>(path: string, token: string | null, init?: RequestInit): Promise<T> {
|
||||
const headers: Record<string, string> = { "content-type": "application/json" };
|
||||
if (token) headers.authorization = `Bearer ${token}`;
|
||||
const res = await fetch(`/api${path}`, { ...init, headers: { ...headers, ...(init?.headers as Record<string, string>) } });
|
||||
if (!res.ok) {
|
||||
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
||||
throw new ApiError(res.status, body.error ?? `HTTP ${res.status}`);
|
||||
}
|
||||
return (await res.json()) as T;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
login: (user: string, password: string, totp: string) =>
|
||||
req<{ token: string }>("/auth/login", null, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ user, password, totp }),
|
||||
}),
|
||||
|
||||
chat: (token: string, message: string) =>
|
||||
req<{ reply: string }>("/chat", token, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ message }),
|
||||
}),
|
||||
|
||||
review: (token: string) => req<{ assets: Asset[] }>("/review", token),
|
||||
|
||||
approve: (token: string, id: string) =>
|
||||
req<{ asset: Asset }>(`/review/${encodeURIComponent(id)}/approve`, token, { method: "POST" }),
|
||||
|
||||
refuse: (token: string, id: string) =>
|
||||
req<{ asset: Asset }>(`/review/${encodeURIComponent(id)}/refuse`, token, { method: "POST" }),
|
||||
|
||||
state: (token: string) => req<{ phase: string; tools: number }>("/state", token),
|
||||
};
|
||||
Reference in New Issue
Block a user