feat(legal): registre des sous-traitants et inventaire reel des cookies
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
This commit is contained in:
parent
94a681601f
commit
5504f9c1ce
113
apps/frontend/src/lib/legal/processing.ts
Normal file
113
apps/frontend/src/lib/legal/processing.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user