/** * NIS2 Agile - API Client * * Client JavaScript per comunicare con il backend REST API. */ class NIS2API { constructor(baseUrl = '/api') { this.baseUrl = baseUrl; this.token = localStorage.getItem('nis2_access_token'); this.refreshToken = localStorage.getItem('nis2_refresh_token'); this.orgId = localStorage.getItem('nis2_org_id'); } // ═══════════════════════════════════════════════════════════════════ // HTTP Methods // ═══════════════════════════════════════════════════════════════════ async request(method, endpoint, data = null, options = {}) { const url = `${this.baseUrl}${endpoint}`; const headers = { 'Content-Type': 'application/json', }; if (this.token) { headers['Authorization'] = `Bearer ${this.token}`; } if (this.orgId) { headers['X-Organization-Id'] = this.orgId; } const config = { method, headers }; if (data && (method === 'POST' || method === 'PUT')) { config.body = JSON.stringify(data); } try { const response = await fetch(url, config); const json = await response.json(); // Token expired - try refresh if (response.status === 401 && this.refreshToken && !options._isRetry) { const refreshed = await this.doRefreshToken(); if (refreshed) { return this.request(method, endpoint, data, { ...options, _isRetry: true }); } } if (!json.success && !options.silent) { console.error(`[API] ${method} ${endpoint}:`, json.message); } return json; } catch (error) { console.error(`[API] Network error: ${method} ${endpoint}`, error); const msg = typeof I18n !== 'undefined' ? I18n.t('msg.error_connection') : 'Errore di connessione al server'; return { success: false, message: msg }; } } get(endpoint) { return this.request('GET', endpoint); } post(endpoint, data) { return this.request('POST', endpoint, data); } put(endpoint, data) { return this.request('PUT', endpoint, data); } del(endpoint) { return this.request('DELETE', endpoint); } // ═══════════════════════════════════════════════════════════════════ // Auth // ═══════════════════════════════════════════════════════════════════ async login(email, password) { const result = await this.post('/auth/login', { email, password }); if (result.success) { this.setTokens(result.data.access_token, result.data.refresh_token); this.setUserRole(result.data.user.role); if (result.data.organizations && result.data.organizations.length > 0) { const primary = result.data.organizations.find(o => o.is_primary) || result.data.organizations[0]; this.setOrganization(primary.organization_id); } } return result; } async register(email, password, fullName, roleOrType = 'azienda') { // Supporta sia i nuovi role NIS2 diretti (compliance_manager, org_admin, etc.) // che il vecchio user_type (azienda, consultant) per retrocompatibilità const result = await this.post('/auth/register', { email, password, full_name: fullName, role: roleOrType, user_type: roleOrType, // backward compat }); if (result.success) { this.setTokens(result.data.access_token, result.data.refresh_token); localStorage.setItem('nis2_user_role', result.data.user.role); } return result; } async doRefreshToken() { try { const result = await this.post('/auth/refresh', { refresh_token: this.refreshToken }); if (result.success) { this.setTokens(result.data.access_token, result.data.refresh_token); return true; } } catch (e) { /* ignore */ } this.logout(); return false; } logout() { this.post('/auth/logout', {}).catch(() => {}); this.clearTokens(); window.location.href = '/login.html'; } getMe() { return this.get('/auth/me'); } setTokens(access, refresh) { this.token = access; this.refreshToken = refresh; localStorage.setItem('nis2_access_token', access); localStorage.setItem('nis2_refresh_token', refresh); } clearTokens() { this.token = null; this.refreshToken = null; this.orgId = null; localStorage.removeItem('nis2_access_token'); localStorage.removeItem('nis2_refresh_token'); localStorage.removeItem('nis2_org_id'); localStorage.removeItem('nis2_user_role'); } // Salva ruolo utente al login setUserRole(role) { localStorage.setItem('nis2_user_role', role); } getUserRole() { return localStorage.getItem('nis2_user_role'); } isConsultant() { return this.getUserRole() === 'consultant'; } setOrganization(orgId) { this.orgId = orgId; localStorage.setItem('nis2_org_id', orgId); } isAuthenticated() { return !!this.token; } // ═══════════════════════════════════════════════════════════════════ // Organizations // ═══════════════════════════════════════════════════════════════════ createOrganization(data) { return this.post('/organizations/create', data); } getCurrentOrg() { return this.get('/organizations/current'); } listOrganizations() { return this.get('/organizations/list'); } updateOrganization(id, data) { return this.put(`/organizations/${id}`, data); } classifyEntity(data) { return this.post('/organizations/classify', data); } // ═══════════════════════════════════════════════════════════════════ // Assessments // ═══════════════════════════════════════════════════════════════════ listAssessments() { return this.get('/assessments/list'); } createAssessment(data) { return this.post('/assessments/create', data); } getAssessment(id) { return this.get(`/assessments/${id}`); } getAssessmentQuestions(id) { return this.get(`/assessments/${id}/questions`); } saveAssessmentResponse(id, data) { return this.post(`/assessments/${id}/respond`, data); } completeAssessment(id) { return this.post(`/assessments/${id}/complete`, {}); } getAssessmentReport(id) { return this.get(`/assessments/${id}/report`); } aiAnalyzeAssessment(id) { return this.post(`/assessments/${id}/ai-analyze`, {}); } // ═══════════════════════════════════════════════════════════════════ // Dashboard // ═══════════════════════════════════════════════════════════════════ getDashboardOverview() { return this.get('/dashboard/overview'); } getComplianceScore() { return this.get('/dashboard/compliance-score'); } getUpcomingDeadlines() { return this.get('/dashboard/upcoming-deadlines'); } getRecentActivity() { return this.get('/dashboard/recent-activity'); } getRiskHeatmap() { return this.get('/dashboard/risk-heatmap'); } // ═══════════════════════════════════════════════════════════════════ // Risks // ═══════════════════════════════════════════════════════════════════ listRisks(params = {}) { return this.get('/risks/list?' + new URLSearchParams(params)); } createRisk(data) { return this.post('/risks/create', data); } getRisk(id) { return this.get(`/risks/${id}`); } updateRisk(id, data) { return this.put(`/risks/${id}`, data); } deleteRisk(id) { return this.del(`/risks/${id}`); } getRiskMatrix() { return this.get('/risks/matrix'); } aiSuggestRisks() { return this.post('/risks/ai-suggest', {}); } // ═══════════════════════════════════════════════════════════════════ // Incidents // ═══════════════════════════════════════════════════════════════════ listIncidents(params = {}) { return this.get('/incidents/list?' + new URLSearchParams(params)); } createIncident(data) { return this.post('/incidents/create', data); } getIncident(id) { return this.get(`/incidents/${id}`); } updateIncident(id, data) { return this.put(`/incidents/${id}`, data); } sendEarlyWarning(id) { return this.post(`/incidents/${id}/early-warning`, {}); } sendNotification(id) { return this.post(`/incidents/${id}/notification`, {}); } sendFinalReport(id) { return this.post(`/incidents/${id}/final-report`, {}); } // ═══════════════════════════════════════════════════════════════════ // Policies // ═══════════════════════════════════════════════════════════════════ listPolicies(params = {}) { return this.get('/policies/list?' + new URLSearchParams(params)); } createPolicy(data) { return this.post('/policies/create', data); } getPolicy(id) { return this.get(`/policies/${id}`); } updatePolicy(id, data) { return this.put(`/policies/${id}`, data); } approvePolicy(id) { return this.post(`/policies/${id}/approve`, {}); } aiGeneratePolicy(category) { return this.post('/policies/ai-generate', { category }); } getPolicyTemplates() { return this.get('/policies/templates'); } // ═══════════════════════════════════════════════════════════════════ // Supply Chain // ═══════════════════════════════════════════════════════════════════ listSuppliers() { return this.get('/supply-chain/list'); } createSupplier(data) { return this.post('/supply-chain/create', data); } getSupplier(id) { return this.get(`/supply-chain/${id}`); } updateSupplier(id, data) { return this.put(`/supply-chain/${id}`, data); } assessSupplier(id, data) { return this.post(`/supply-chain/${id}/assess`, data); } // ═══════════════════════════════════════════════════════════════════ // Training // ═══════════════════════════════════════════════════════════════════ listCourses() { return this.get('/training/courses'); } getMyTraining() { return this.get('/training/assignments'); } getTrainingCompliance() { return this.get('/training/compliance-status'); } // ═══════════════════════════════════════════════════════════════════ // Assets // ═══════════════════════════════════════════════════════════════════ listAssets(params = {}) { return this.get('/assets/list?' + new URLSearchParams(params)); } createAsset(data) { return this.post('/assets/create', data); } getAsset(id) { return this.get(`/assets/${id}`); } updateAsset(id, data) { return this.put(`/assets/${id}`, data); } // ═══════════════════════════════════════════════════════════════════ // Audit // ═══════════════════════════════════════════════════════════════════ listControls() { return this.get('/audit/controls'); } updateControl(id, data) { return this.put(`/audit/controls/${id}`, data); } generateComplianceReport() { return this.get('/audit/report'); } getAuditLogs(params = {}) { return this.get('/audit/logs?' + new URLSearchParams(params)); } getIsoMapping() { return this.get('/audit/iso27001-mapping'); } getExecutiveReportUrl() { return this.baseUrl + '/audit/executive-report'; } getExportUrl(type) { return this.baseUrl + '/audit/export?type=' + type; } // ═══════════════════════════════════════════════════════════════════ // Onboarding // ═══════════════════════════════════════════════════════════════════ async uploadVisura(file) { const formData = new FormData(); formData.append('visura', file); const headers = { 'Authorization': 'Bearer ' + this.token }; if (this.orgId) headers['X-Organization-Id'] = this.orgId; try { const response = await fetch(this.baseUrl + '/onboarding/upload-visura', { method: 'POST', headers, body: formData, }); return response.json(); } catch (error) { const msg = typeof I18n !== 'undefined' ? I18n.t('msg.error_connection') : 'Errore di connessione al server'; return { success: false, message: msg }; } } fetchCompany(vatNumber) { return this.post('/onboarding/fetch-company', { vat_number: vatNumber }); } completeOnboarding(data) { return this.post('/onboarding/complete', data); } // ═══════════════════════════════════════════════════════════════════ // Non-Conformity & CAPA // ═══════════════════════════════════════════════════════════════════ listNCRs(params = {}) { return this.get('/ncr/list?' + new URLSearchParams(params)); } createNCR(data) { return this.post('/ncr/create', data); } getNCR(id) { return this.get(`/ncr/${id}`); } updateNCR(id, data) { return this.put(`/ncr/${id}`, data); } addCapa(ncrId, data) { return this.post(`/ncr/${ncrId}/capa`, data); } updateCapa(capaId, data) { return this.put(`/ncr/capa/${capaId}`, data); } createNCRsFromAssessment(assessmentId) { return this.post('/ncr/from-assessment', { assessment_id: assessmentId }); } getNCRStats() { return this.get('/ncr/stats'); } // ═══════════════════════════════════════════════════════════════════ // Feedback & Segnalazioni // ═══════════════════════════════════════════════════════════════════ submitFeedback(data) { return this.post('/feedback/submit', data); } getMyFeedback() { return this.get('/feedback/mine'); } listFeedback(params = {}) { return this.get('/feedback/list?' + new URLSearchParams(params)); } getFeedback(id) { return this.get(`/feedback/${id}`); } updateFeedback(id, data) { return this.put(`/feedback/${id}`, data); } resolveFeedback(id, password) { return this.post(`/feedback/${id}/resolve`, { password }); } } // Singleton globale const api = new NIS2API();