feat(gci): add Gesamt-Compliance-Index scoring engine and dashboard
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 34s
CI / test-python-backend-compliance (push) Successful in 28s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 17s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 34s
CI / test-python-backend-compliance (push) Successful in 28s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 17s
Implements the 4-level GCI scoring model (Module -> Risk-Weighted -> Regulation Area -> Final GCI) with DSGVO, NIS2, ISO 27001, and EU AI Act integration. Backend: - 9 Go files: engine, models, weights, validity, NIS2 roles/scoring, ISO mapping/gap-analysis, mock data - GCI handlers with 13 API endpoints under /sdk/v1/gci/ - Routes registered in main.go Frontend: - TypeScript types, API client, Next.js API proxy - Dashboard page with 6 tabs (Overview, Breakdown, NIS2, ISO 27001, Matrix, Audit Trail) - Sidebar navigation entry Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
99
admin-compliance/lib/sdk/gci/api.ts
Normal file
99
admin-compliance/lib/sdk/gci/api.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* GCI API Client
|
||||
* Communicates with the Go backend via Next.js API proxy at /api/sdk/v1/gci/*
|
||||
*/
|
||||
|
||||
import type {
|
||||
GCIResult,
|
||||
GCIBreakdown,
|
||||
GCIHistoryResponse,
|
||||
GCIMatrixResponse,
|
||||
NIS2Score,
|
||||
NIS2Role,
|
||||
ISOGapAnalysis,
|
||||
WeightProfile,
|
||||
} from './types'
|
||||
|
||||
const BASE_URL = '/api/sdk/v1/gci'
|
||||
|
||||
async function apiFetch<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
const res = await fetch(`${BASE_URL}${path}`, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': typeof window !== 'undefined'
|
||||
? (localStorage.getItem('bp-tenant-id') || 'default')
|
||||
: 'default',
|
||||
...options?.headers,
|
||||
},
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => ({ error: res.statusText }))
|
||||
throw new Error(error.error || `API Error: ${res.status}`)
|
||||
}
|
||||
|
||||
return res.json()
|
||||
}
|
||||
|
||||
/** GCI Score abrufen */
|
||||
export async function getGCIScore(profile?: string): Promise<GCIResult> {
|
||||
const params = profile ? `?profile=${profile}` : ''
|
||||
return apiFetch<GCIResult>(`/score${params}`)
|
||||
}
|
||||
|
||||
/** Detailliertes 4-Level Breakdown abrufen */
|
||||
export async function getGCIBreakdown(profile?: string): Promise<GCIBreakdown> {
|
||||
const params = profile ? `?profile=${profile}` : ''
|
||||
return apiFetch<GCIBreakdown>(`/score/breakdown${params}`)
|
||||
}
|
||||
|
||||
/** GCI History abrufen */
|
||||
export async function getGCIHistory(): Promise<GCIHistoryResponse> {
|
||||
return apiFetch<GCIHistoryResponse>('/score/history')
|
||||
}
|
||||
|
||||
/** Compliance Matrix abrufen */
|
||||
export async function getGCIMatrix(): Promise<GCIMatrixResponse> {
|
||||
return apiFetch<GCIMatrixResponse>('/matrix')
|
||||
}
|
||||
|
||||
/** Audit Trail abrufen */
|
||||
export async function getGCIAuditTrail(profile?: string): Promise<{ tenant_id: string; gci_score: number; audit_trail: any[] }> {
|
||||
const params = profile ? `?profile=${profile}` : ''
|
||||
return apiFetch(`/audit-trail${params}`)
|
||||
}
|
||||
|
||||
/** Gewichtungsprofile abrufen */
|
||||
export async function getWeightProfiles(): Promise<{ profiles: WeightProfile[] }> {
|
||||
return apiFetch<{ profiles: WeightProfile[] }>('/profiles')
|
||||
}
|
||||
|
||||
/** NIS2 Score abrufen */
|
||||
export async function getNIS2Score(): Promise<NIS2Score> {
|
||||
return apiFetch<NIS2Score>('/nis2/score')
|
||||
}
|
||||
|
||||
/** NIS2 Rollen auflisten */
|
||||
export async function getNIS2Roles(): Promise<{ roles: NIS2Role[]; total: number }> {
|
||||
return apiFetch<{ roles: NIS2Role[]; total: number }>('/nis2/roles')
|
||||
}
|
||||
|
||||
/** NIS2 Rolle zuweisen */
|
||||
export async function assignNIS2Role(roleId: string, userId: string): Promise<any> {
|
||||
return apiFetch('/nis2/roles/assign', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ role_id: roleId, user_id: userId }),
|
||||
})
|
||||
}
|
||||
|
||||
/** ISO Gap-Analyse abrufen */
|
||||
export async function getISOGapAnalysis(): Promise<ISOGapAnalysis> {
|
||||
return apiFetch<ISOGapAnalysis>('/iso/gap-analysis')
|
||||
}
|
||||
|
||||
/** ISO Mappings abrufen */
|
||||
export async function getISOMappings(category?: string): Promise<any> {
|
||||
const params = category ? `?category=${category}` : ''
|
||||
return apiFetch(`/iso/mappings${params}`)
|
||||
}
|
||||
246
admin-compliance/lib/sdk/gci/types.ts
Normal file
246
admin-compliance/lib/sdk/gci/types.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* GCI (Gesamt-Compliance-Index) Types
|
||||
* TypeScript definitions for the 4-level compliance scoring model
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// MATURITY LEVELS
|
||||
// =============================================================================
|
||||
|
||||
export type MaturityLevel = 'OPTIMIZED' | 'MANAGED' | 'DEFINED' | 'REACTIVE' | 'HIGH_RISK'
|
||||
|
||||
export const MATURITY_INFO: Record<MaturityLevel, { label: string; color: string; bgColor: string; borderColor: string; description: string }> = {
|
||||
OPTIMIZED: { label: 'Optimiert', color: 'text-green-700', bgColor: 'bg-green-100', borderColor: 'border-green-300', description: 'Kontinuierliche Verbesserung, proaktive Compliance' },
|
||||
MANAGED: { label: 'Gesteuert', color: 'text-blue-700', bgColor: 'bg-blue-100', borderColor: 'border-blue-300', description: 'Messbare Prozesse, regelmaessige Reviews' },
|
||||
DEFINED: { label: 'Definiert', color: 'text-yellow-700', bgColor: 'bg-yellow-100', borderColor: 'border-yellow-300', description: 'Dokumentierte Prozesse, erste Strukturen' },
|
||||
REACTIVE: { label: 'Reaktiv', color: 'text-orange-700', bgColor: 'bg-orange-100', borderColor: 'border-orange-300', description: 'Ad-hoc Massnahmen, wenig Struktur' },
|
||||
HIGH_RISK: { label: 'Hohes Risiko', color: 'text-red-700', bgColor: 'bg-red-100', borderColor: 'border-red-300', description: 'Erheblicher Handlungsbedarf, Compliance-Luecken' },
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LEVEL 1: MODULE SCORE
|
||||
// =============================================================================
|
||||
|
||||
export interface ModuleScore {
|
||||
module_id: string
|
||||
module_name: string
|
||||
assigned: number
|
||||
completed: number
|
||||
raw_score: number
|
||||
validity_factor: number
|
||||
final_score: number
|
||||
risk_weight: number
|
||||
category: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LEVEL 2: RISK-WEIGHTED AREA SCORE
|
||||
// =============================================================================
|
||||
|
||||
export interface RiskWeightedScore {
|
||||
area_id: string
|
||||
area_name: string
|
||||
modules: ModuleScore[]
|
||||
weighted_sum: number
|
||||
total_weight: number
|
||||
area_score: number
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LEVEL 3: REGULATION AREA SCORE
|
||||
// =============================================================================
|
||||
|
||||
export interface RegulationAreaScore {
|
||||
regulation_id: string
|
||||
regulation_name: string
|
||||
score: number
|
||||
weight: number
|
||||
weighted_score: number
|
||||
module_count: number
|
||||
completed_count: number
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LEVEL 4: GCI RESULT
|
||||
// =============================================================================
|
||||
|
||||
export interface AuditEntry {
|
||||
timestamp: string
|
||||
factor: string
|
||||
description: string
|
||||
value: number
|
||||
impact: 'positive' | 'negative' | 'neutral'
|
||||
}
|
||||
|
||||
export interface GCIResult {
|
||||
tenant_id: string
|
||||
gci_score: number
|
||||
maturity_level: MaturityLevel
|
||||
maturity_label: string
|
||||
calculated_at: string
|
||||
profile: string
|
||||
area_scores: RegulationAreaScore[]
|
||||
criticality_multiplier: number
|
||||
incident_adjustment: number
|
||||
audit_trail: AuditEntry[]
|
||||
}
|
||||
|
||||
export interface GCIBreakdown extends GCIResult {
|
||||
level1_modules: ModuleScore[]
|
||||
level2_areas: RiskWeightedScore[]
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GCI HISTORY
|
||||
// =============================================================================
|
||||
|
||||
export interface GCISnapshot {
|
||||
tenant_id: string
|
||||
score: number
|
||||
maturity_level: MaturityLevel
|
||||
area_scores: Record<string, number>
|
||||
calculated_at: string
|
||||
}
|
||||
|
||||
export interface GCIHistoryResponse {
|
||||
tenant_id: string
|
||||
snapshots: GCISnapshot[]
|
||||
total: number
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMPLIANCE MATRIX
|
||||
// =============================================================================
|
||||
|
||||
export interface ComplianceMatrixEntry {
|
||||
role: string
|
||||
role_name: string
|
||||
regulations: Record<string, number>
|
||||
overall_score: number
|
||||
required_modules: number
|
||||
completed_modules: number
|
||||
}
|
||||
|
||||
export interface GCIMatrixResponse {
|
||||
tenant_id: string
|
||||
matrix: ComplianceMatrixEntry[]
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// NIS2
|
||||
// =============================================================================
|
||||
|
||||
export interface NIS2Role {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
mandatory_modules: string[]
|
||||
priority: number
|
||||
}
|
||||
|
||||
export interface NIS2AreaScore {
|
||||
area_id: string
|
||||
area_name: string
|
||||
weight: number
|
||||
score: number
|
||||
weighted_score: number
|
||||
}
|
||||
|
||||
export interface NIS2RoleScore {
|
||||
role_id: string
|
||||
role_name: string
|
||||
assigned_users: number
|
||||
completion_rate: number
|
||||
modules_completed: number
|
||||
modules_required: number
|
||||
}
|
||||
|
||||
export interface NIS2Score {
|
||||
tenant_id: string
|
||||
overall_score: number
|
||||
maturity_level: string
|
||||
areas: NIS2AreaScore[]
|
||||
role_scores: NIS2RoleScore[]
|
||||
calculated_at: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ISO 27001
|
||||
// =============================================================================
|
||||
|
||||
export interface ISOControl {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category_id: string
|
||||
category_name: string
|
||||
control_type: string
|
||||
is_critical: boolean
|
||||
sdk_modules: string[]
|
||||
}
|
||||
|
||||
export interface ISOGap {
|
||||
control_id: string
|
||||
control_name: string
|
||||
category: string
|
||||
status: string
|
||||
priority: string
|
||||
recommendation: string
|
||||
}
|
||||
|
||||
export interface ISOCategorySummary {
|
||||
category_id: string
|
||||
category_name: string
|
||||
total_controls: number
|
||||
covered_full: number
|
||||
covered_partial: number
|
||||
not_covered: number
|
||||
}
|
||||
|
||||
export interface ISOGapAnalysis {
|
||||
tenant_id: string
|
||||
total_controls: number
|
||||
covered_full: number
|
||||
covered_partial: number
|
||||
not_covered: number
|
||||
coverage_percent: number
|
||||
category_summaries: ISOCategorySummary[]
|
||||
gaps: ISOGap[]
|
||||
calculated_at: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// WEIGHT PROFILES
|
||||
// =============================================================================
|
||||
|
||||
export interface WeightProfile {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
weights: Record<string, number>
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
export function getScoreColor(score: number): string {
|
||||
if (score >= 80) return 'text-green-600'
|
||||
if (score >= 60) return 'text-yellow-600'
|
||||
if (score >= 40) return 'text-orange-600'
|
||||
return 'text-red-600'
|
||||
}
|
||||
|
||||
export function getScoreBgColor(score: number): string {
|
||||
if (score >= 80) return 'bg-green-500'
|
||||
if (score >= 60) return 'bg-yellow-500'
|
||||
if (score >= 40) return 'bg-orange-500'
|
||||
return 'bg-red-500'
|
||||
}
|
||||
|
||||
export function getScoreRingColor(score: number): string {
|
||||
if (score >= 80) return '#22c55e'
|
||||
if (score >= 60) return '#eab308'
|
||||
if (score >= 40) return '#f97316'
|
||||
return '#ef4444'
|
||||
}
|
||||
Reference in New Issue
Block a user