feat: initial TrustOS platform scaffold

- FastAPI backend: auth, findings, dashboard, attack paths, footprint, AI translator, risk calculator, PDF report generator
- Next.js frontend: Vault dashboard, login, findings table, finding detail with AI coach, digital footprint, reports
- PostgreSQL data model: tenants, users, assets, findings, risk scores, audit reports, attack paths
- Docker Compose + Dockerfiles for all services
- Demo seed data: Acme Corp with 6 findings and 90-day risk score history
- AI Risk Translator (OpenAI/Anthropic) with plain-English business impact
- Role-based access: executive / it_admin / trustos_admin
- Scope-lock engine: authorization required before any assessment

Stage 1-8 complete: Phase 1 Vault Audit product ready
This commit is contained in:
drjones
2026-07-05 09:46:08 +00:00
commit 463dff883b
64 changed files with 11679 additions and 0 deletions

127
frontend/src/lib/api.ts Normal file
View File

@@ -0,0 +1,127 @@
const BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
let authToken: string | null = null;
export function setAuthToken(token: string | null) {
authToken = token;
}
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
const headers: Record<string, string> = {
"Content-Type": "application/json",
...(options.headers as Record<string, string>),
};
if (authToken) {
headers["Authorization"] = `Bearer ${authToken}`;
}
const res = await fetch(`${BASE}${path}`, { ...options, headers });
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(err.detail || `HTTP ${res.status}`);
}
return res.json();
}
export const api = {
login: (email: string, password: string) =>
request<{ access_token: string; role: string; tenant_id: string; full_name: string }>(
"/api/v1/auth/login",
{ method: "POST", body: JSON.stringify({ email, password }) }
),
me: () => request<{ id: string; email: string; full_name: string; role: string; tenant_id: string }>(
"/api/v1/auth/me"
),
dashboard: (tenantId: string) =>
request<DashboardData>(`/api/v1/dashboard/${tenantId}`),
findings: (tenantId: string, params?: string) =>
request<Finding[]>(`/api/v1/findings?tenant_id=${tenantId}${params ? "&" + params : ""}`),
finding: (id: string) =>
request<Finding>(`/api/v1/findings/${id}`),
updateFindingStatus: (id: string, body: { status: string; resolution_note?: string }) =>
request<Finding>(`/api/v1/findings/${id}/status`, {
method: "PATCH",
body: JSON.stringify(body),
}),
attackPaths: (findingId: string) =>
request<AttackPath[]>(`/api/v1/attack-paths/${findingId}`),
footprint: (tenantId: string) =>
request<FootprintData>(`/api/v1/footprint/${tenantId}`),
aiExplain: (findingId: string, question: string) =>
request<{ question: string; answer: string }>(`/api/v1/ai/explain/${findingId}?question=${encodeURIComponent(question)}`),
};
// ─── Types ────────────────────────────────────────────────────────────────────
export interface DashboardData {
tenant_name: string;
current_score: number;
previous_score: number | null;
score_delta: number | null;
score_trend: { date: string; score: number }[];
top_risks: RiskCard[];
open_critical: number;
open_high: number;
open_medium: number;
total_open: number;
baseline_score: number | null;
baseline_date: string | null;
}
export interface RiskCard {
id: string;
title: string;
ai_summary: string | null;
ai_business_impact: string | null;
ai_impact_level: string | null;
ai_fix_priority: string | null;
severity: string;
category: string;
}
export interface Finding {
id: string;
tenant_id: string;
title: string;
severity: string;
status: string;
category: string;
technical_description: string | null;
cve_id: string | null;
cvss_score: number | null;
affected_component: string | null;
ai_summary: string | null;
ai_business_impact: string | null;
ai_impact_level: string | null;
ai_remediation_steps: string | null;
ai_fix_priority: string | null;
assignee_email: string | null;
due_date: string | null;
is_top_risk: boolean;
source: string | null;
created_at: string;
updated_at: string;
}
export interface AttackPath {
id: string;
finding_id: string;
title: string;
ai_narrative: string | null;
nodes_json: string | null;
edges_json: string | null;
}
export interface FootprintData {
tenant_id: string;
executives: { id: string; name: string; title: string; email: string }[];
footprint_findings: any[];
total_exposures: number;
}