frontend_nyla/src/hooks/useRbacExportImport.ts
2026-04-11 19:44:52 +02:00

270 lines
7.9 KiB
TypeScript

/**
* useRbacExportImport Hook
*
* Hook for exporting and importing RBAC configurations.
* Supports mandate-level and global (template) exports.
*/
import { useState, useCallback } from 'react';
import api from '../api';
// =============================================================================
// TYPES
// =============================================================================
export type ImportMode = 'merge' | 'replace' | 'add_only';
export interface RbacExportScope {
type: 'global' | 'mandate' | 'instance';
mandateId?: string;
mandateName?: string;
featureInstanceId?: string;
featureCode?: string;
instanceLabel?: string;
}
export interface RbacExportRole {
roleLabel: string;
description?: string;
featureCode?: string;
}
export interface RbacExportRule {
roleLabel: string;
context: 'DATA' | 'UI' | 'RESOURCE';
item: string | null;
view: boolean;
read?: string | null;
create?: string | null;
update?: string | null;
delete?: string | null;
}
export interface RbacExport {
version: string;
exportedAt: string;
exportedBy?: string;
scope: RbacExportScope;
roles: RbacExportRole[];
accessRules: RbacExportRule[];
}
export interface RbacImportResult {
status: 'success' | 'error';
mode: ImportMode;
rolesCreated: number;
rolesUpdated: number;
rulesCreated: number;
rulesUpdated: number;
errors?: string[];
}
// =============================================================================
// HOOK
// =============================================================================
export function useRbacExportImport() {
const [exporting, setExporting] = useState(false);
const [importing, setImporting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [lastExport, setLastExport] = useState<RbacExport | null>(null);
const [lastImportResult, setLastImportResult] = useState<RbacImportResult | null>(null);
/**
* Export RBAC configuration for a mandate
*/
const exportMandateRbac = useCallback(async (
mandateId: string,
featureCode?: string
): Promise<{ success: boolean; data?: RbacExport; error?: string }> => {
setExporting(true);
setError(null);
try {
const params = new URLSearchParams();
if (featureCode) params.append('featureCode', featureCode);
const url = `/api/mandates/${mandateId}/rbac/export${params.toString() ? '?' + params.toString() : ''}`;
const response = await api.get(url);
setLastExport(response.data);
return { success: true, data: response.data };
} catch (err: any) {
const errorMessage = err.response?.data?.detail || err.message || 'Failed to export RBAC';
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setExporting(false);
}
}, []);
/**
* Export global RBAC templates (SysAdmin only)
*/
const exportGlobalRbac = useCallback(async (
featureCode?: string
): Promise<{ success: boolean; data?: RbacExport; error?: string }> => {
setExporting(true);
setError(null);
try {
const params = new URLSearchParams();
if (featureCode) params.append('featureCode', featureCode);
const url = `/api/admin/rbac/global/export${params.toString() ? '?' + params.toString() : ''}`;
const response = await api.get(url);
setLastExport(response.data);
return { success: true, data: response.data };
} catch (err: any) {
const errorMessage = err.response?.data?.detail || err.message || 'Failed to export global RBAC';
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setExporting(false);
}
}, []);
/**
* Export feature instance RBAC
*/
const exportInstanceRbac = useCallback(async (
instanceId: string
): Promise<{ success: boolean; data?: RbacExport; error?: string }> => {
setExporting(true);
setError(null);
try {
const response = await api.get(`/api/features/instances/${instanceId}/rbac/export`);
setLastExport(response.data);
return { success: true, data: response.data };
} catch (err: any) {
const errorMessage = err.response?.data?.detail || err.message || 'Failed to export instance RBAC';
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setExporting(false);
}
}, []);
/**
* Import RBAC configuration into a mandate
*/
const importMandateRbac = useCallback(async (
mandateId: string,
data: RbacExport,
mode: ImportMode = 'merge'
): Promise<{ success: boolean; result?: RbacImportResult; error?: string }> => {
setImporting(true);
setError(null);
try {
const response = await api.post(
`/api/mandates/${mandateId}/rbac/import?mode=${mode}`,
data
);
setLastImportResult(response.data);
return { success: true, result: response.data };
} catch (err: any) {
const errorMessage = err.response?.data?.detail || err.message || 'Failed to import RBAC';
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setImporting(false);
}
}, []);
/**
* Import global RBAC templates (SysAdmin only)
*/
const importGlobalRbac = useCallback(async (
data: RbacExport,
mode: ImportMode = 'merge'
): Promise<{ success: boolean; result?: RbacImportResult; error?: string }> => {
setImporting(true);
setError(null);
try {
const response = await api.post(
`/api/admin/rbac/global/import?mode=${mode}`,
data
);
setLastImportResult(response.data);
return { success: true, result: response.data };
} catch (err: any) {
const errorMessage = err.response?.data?.detail || err.message || 'Failed to import global RBAC';
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setImporting(false);
}
}, []);
/**
* Download export as JSON file
*/
const downloadExport = useCallback((data: RbacExport, filename?: string) => {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename || `rbac-export-${data.scope.type}-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, []);
/**
* Parse uploaded JSON file
*/
const parseImportFile = useCallback(async (file: File): Promise<{ success: boolean; data?: RbacExport; error?: string }> => {
try {
const text = await file.text();
const data = JSON.parse(text) as RbacExport;
// Basic validation
if (!data.version) {
return { success: false, error: 'Ungültiges Format: Fehlende Version' };
}
if (!data.scope) {
return { success: false, error: 'Ungültiges Format: Fehlender Scope' };
}
if (!Array.isArray(data.roles)) {
return { success: false, error: 'Ungültiges Format: Roles muss ein Array sein' };
}
if (!Array.isArray(data.accessRules)) {
return { success: false, error: 'Ungültiges Format: AccessRules muss ein Array sein' };
}
return { success: true, data };
} catch (err: any) {
return { success: false, error: `Fehler beim Parsen: ${err.message}` };
}
}, []);
/**
* Clear state
*/
const reset = useCallback(() => {
setError(null);
setLastExport(null);
setLastImportResult(null);
}, []);
return {
exporting,
importing,
error,
lastExport,
lastImportResult,
exportMandateRbac,
exportGlobalRbac,
exportInstanceRbac,
importMandateRbac,
importGlobalRbac,
downloadExport,
parseImportFile,
reset,
};
}
export default useRbacExportImport;