ncantu 13898d1012 UserWallet: useRelayNotifications, CNIL validation, ServiceSync polling, SyncScreen link
**Motivations:**
- Intégrer les notifications relais (polling, auto-process hashes) dans ServiceSyncScreen
- Valider conformité CNIL des contrats/champs à l’ajout au graphe
- Accéder à la sync par service depuis SyncScreen

**Root causes:**
- N/A (évolutions + petits correctifs)

**Correctifs:**
- relayNotificationService: suppression import inutilisé RelayConfig

**Evolutions:**
- useRelayNotifications: hook (RelayNotificationService, start/stop polling, auto-process hash)
- cnilValidation: validateContractCNIL, validateChampCNIL (valid, errors, warnings)
- graphResolver: addContrat/addChamp appellent validation CNIL (async, logs warnings/errors, non bloquant)
- ServiceSyncScreen: useRelayNotifications, GraphResolver ref, polling selon configs (fréquence min), sync result (hasMessages, « Aucun nouveau message »), useBloom mention
- SyncScreen: bouton « Sync par service » vers /service-sync

**Pages affectées:**
- userwallet: useRelayNotifications, cnilValidation, ServiceSyncScreen, SyncScreen, graphResolver, relayNotificationService
2026-01-28 01:42:26 +01:00

164 lines
4.7 KiB
TypeScript

import type { Contrat, Champ } from '../types/contract';
import type { DataJson } from '../types/message';
/**
* CNIL validation result.
*/
export interface CNILValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
}
/**
* Check if a contract requires CNIL fields based on its type.
*/
function contractRequiresCNIL(_contrat: Contrat): boolean {
// Tous les contrats nécessitent les champs CNIL selon la politique métier
// Peut être ajusté selon les besoins spécifiques
return true;
}
/**
* Validate CNIL fields in datajson.
*/
function validateCNILFields(datajson?: DataJson): {
valid: boolean;
errors: string[];
warnings: string[];
} {
const errors: string[] = [];
const warnings: string[] = [];
if (datajson === undefined || datajson === null) {
errors.push('datajson manquant');
return { valid: false, errors, warnings };
}
// Vérifier raisons_usage_tiers
if (datajson.raisons_usage_tiers !== undefined) {
if (!Array.isArray(datajson.raisons_usage_tiers)) {
errors.push('raisons_usage_tiers doit être un tableau');
} else {
for (const item of datajson.raisons_usage_tiers) {
if (
typeof item !== 'object' ||
item === null ||
!Array.isArray(item.raisons) ||
typeof item.tiers !== 'string'
) {
errors.push(
'raisons_usage_tiers: chaque élément doit avoir raisons (string[]) et tiers (string)',
);
}
}
}
}
// Vérifier raisons_partage_tiers
if (datajson.raisons_partage_tiers !== undefined) {
if (!Array.isArray(datajson.raisons_partage_tiers)) {
errors.push('raisons_partage_tiers doit être un tableau');
} else {
for (const item of datajson.raisons_partage_tiers) {
if (
typeof item !== 'object' ||
item === null ||
!Array.isArray(item.raisons) ||
typeof item.tiers !== 'string'
) {
errors.push(
'raisons_partage_tiers: chaque élément doit avoir raisons (string[]) et tiers (string)',
);
}
}
}
}
// Vérifier conditions_conservation
if (datajson.conditions_conservation !== undefined) {
if (typeof datajson.conditions_conservation !== 'object' || datajson.conditions_conservation === null) {
errors.push('conditions_conservation doit être un objet');
} else {
const cc = datajson.conditions_conservation as Record<string, unknown>;
if (typeof cc.delai_expiration !== 'string' && typeof cc.delai_expiration !== 'number') {
errors.push('conditions_conservation.delai_expiration requis (string ou number)');
}
}
} else {
warnings.push('conditions_conservation non défini (recommandé pour conformité CNIL)');
}
return {
valid: errors.length === 0,
errors,
warnings,
};
}
/**
* Validate CNIL compliance for a contract.
* Returns validation result with errors and warnings.
*/
export function validateContractCNIL(contrat: Contrat): CNILValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
if (!contractRequiresCNIL(contrat)) {
return { valid: true, errors: [], warnings: [] };
}
const datajson = contrat.datajson;
const cnilValidation = validateCNILFields(datajson);
errors.push(...cnilValidation.errors);
warnings.push(...cnilValidation.warnings);
// Vérifier que les champs obligatoires sont présents si requis par la politique
// Pour l'instant, on génère seulement des warnings, pas d'erreurs strictes
// La politique métier peut être ajustée ici
if (datajson?.raisons_usage_tiers === undefined) {
warnings.push('raisons_usage_tiers non défini (recommandé pour conformité CNIL)');
}
if (datajson?.raisons_partage_tiers === undefined) {
warnings.push('raisons_partage_tiers non défini (recommandé pour conformité CNIL)');
}
return {
valid: errors.length === 0,
errors,
warnings,
};
}
/**
* Validate CNIL compliance for a field (Champ).
*/
export function validateChampCNIL(champ: Champ): CNILValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
const datajson = champ.datajson;
const cnilValidation = validateCNILFields(datajson);
errors.push(...cnilValidation.errors);
warnings.push(...cnilValidation.warnings);
return {
valid: errors.length === 0,
errors,
warnings,
};
}
/**
* Check if CNIL fields are required for a contract type.
* Can be customized based on business policy.
*/
export function isCNILRequiredForContract(_contrat: Contrat): boolean {
// Politique métier : tous les contrats nécessitent les champs CNIL
// Peut être ajusté selon le type de contrat
return true;
}