xpeditis2.0/apps/frontend/src/lib/legal/processing.ts
2026-09-07 21:40:58 +02:00

114 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Registre des traitements — source unique.
*
* ⚠️ Contenu juridique. Ce fichier liste les destinataires des données et ce
* que le site dépose sur l'appareil du visiteur. La politique de
* confidentialité publique, la politique de cookies et la page de conformité
* de l'administration le lisent toutes les trois : un sous-traitant ajouté ici
* apparaît partout, et il devient impossible de mettre à jour l'un en oubliant
* les autres. Il tient lieu de registre de l'article 30 côté produit.
*
* Seuls les faits structurels vivent ici — noms, nature, encadrement du
* transfert. Les descriptions sont dans les fichiers de messages, sinon la
* moitié de la page resterait en français pour un lecteur anglophone.
*
* Les durées de conservation, elles, vivent côté serveur
* (`domain/services/data-retention`) : c'est le serveur qui les applique.
*/
/** Fondement d'un transfert hors Union européenne (chap. V du RGPD). */
export type TransferBasis =
| 'eu' // Aucune sortie de l'Union.
| 'adequacy' // Décision d'adéquation de la Commission.
| 'scc'; // Clauses contractuelles types.
export interface Subprocessor {
/** Clé de traduction sous `marketing.privacy.processors`. */
key: string;
/** Nom commercial, identique dans toutes les langues. */
name: string;
transferBasis: TransferBasis;
/** Vrai si le prestataire n'intervient que sur une fonctionnalité précise. */
conditional: boolean;
}
/**
* Chaque entrée correspond à un service réellement branché, vérifié dans le
* code — pas une liste d'intentions.
*/
export const SUBPROCESSORS: readonly Subprocessor[] = [
{ key: 'stripe', name: 'Stripe', transferBasis: 'scc', conditional: false },
{ key: 'openai', name: 'OpenAI', transferBasis: 'scc', conditional: true },
{ key: 'sentry', name: 'Sentry', transferBasis: 'scc', conditional: false },
{ key: 'pappers', name: 'Pappers', transferBasis: 'eu', conditional: true },
{ key: 'oauth', name: 'Google, Microsoft', transferBasis: 'scc', conditional: true },
];
export interface StoredItem {
/** Nom technique, tel qu'il apparaît dans le navigateur. */
name: string;
/** Cookie déposé par le serveur, ou valeur écrite par la page. */
kind: 'cookie' | 'localStorage';
}
/**
* Ce que le site dépose réellement.
*
* L'inventaire précédent annonçait Google Analytics (`_ga`, `_gid`, `_gat`),
* Facebook Pixel (`_fbp`), LinkedIn (`li_fat_id`) et HubSpot (`hubspotutk`).
* Aucun de ces scripts n'est chargé par l'application : décrire des traceurs
* qui n'existent pas est aussi inexact que d'en dissimuler, et rendait sans
* objet le consentement demandé pour eux.
*/
export const ESSENTIAL_STORAGE: readonly StoredItem[] = [
{ name: 'accessToken', kind: 'cookie' },
{ name: 'refreshToken', kind: 'cookie' },
{ name: 'xpeditis_session', kind: 'cookie' },
{ name: 'cookieConsent', kind: 'localStorage' },
];
export const FUNCTIONAL_STORAGE: readonly StoredItem[] = [{ name: 'NEXT_LOCALE', kind: 'cookie' }];
/**
* Responsable du traitement (art. 13.1.a).
*
* Les champs à `null` sont obligatoires et manquants. La page publique ne
* publie que ce qui est renseigné, et la console d'administration les signale :
* mieux vaut une mention absente, visible et corrigeable, qu'une mention
* inventée.
*/
export interface ControllerIdentity {
tradingName: string;
legalName: string | null;
registrationNumber: string | null;
address: string | null;
privacyContact: string;
/** Délégué à la protection des données, si un DPO a été désigné. */
dpo: string | null;
}
export const CONTROLLER: ControllerIdentity = {
tradingName: 'Xpeditis',
legalName: null,
registrationNumber: null,
address: null,
privacyContact: 'privacy@xpeditis.com',
dpo: null,
};
/** Autorité de contrôle compétente, à mentionner au titre de l'art. 13.2.d. */
export const SUPERVISORY_AUTHORITY = {
name: 'Commission nationale de l’informatique et des libertés (CNIL)',
url: 'https://www.cnil.fr/fr/plaintes',
} as const;
/** Mentions obligatoires encore absentes de l'identité du responsable. */
export function missingControllerFields(controller: ControllerIdentity = CONTROLLER): string[] {
const required: [keyof ControllerIdentity, string][] = [
['legalName', 'legalName'],
['registrationNumber', 'registrationNumber'],
['address', 'address'],
];
return required.filter(([field]) => !controller[field]).map(([, key]) => key);
}