veza/apps/web/src/services/cookieService.ts
senke 09bb663659 chore: enable noUncheckedIndexedAccess, isolate ghost MSW handlers, document go-clamd tech debt
- Enable TypeScript noUncheckedIndexedAccess and fix 133 resulting errors
  across 46 files with proper null guards, optional chaining, and fallbacks
- Extract education/gamification ghost feature MSW handlers into handlers-ghost.ts
- Add Storybook test plugin documentation in vitest.config.ts
- Document abandoned go-clamd dependency (2017) as tech debt in upload_validator.go

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 23:12:35 +01:00

112 lines
2.8 KiB
TypeScript

/**
* CookieService - Service de gestion des cookies côté client
* SECURITY: Pour les cookies httpOnly, le backend les gère directement
* Ce service gère uniquement les cookies non-httpOnly si nécessaire
*/
/**
* Définit un cookie
* Note: Pour les cookies httpOnly, le backend doit les setter via Set-Cookie header
* @param name - Nom du cookie
* @param value - Valeur du cookie
* @param days - Nombre de jours avant expiration
* @param options - Options supplémentaires (path, domain, secure, sameSite)
*/
export function setCookie(
name: string,
value: string,
days?: number,
options?: {
path?: string;
domain?: string;
secure?: boolean;
sameSite?: 'Strict' | 'Lax' | 'None';
},
): void {
let cookieString = `${name}=${encodeURIComponent(value)}`;
if (days !== undefined) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
cookieString += `; expires=${date.toUTCString()}`;
}
if (options?.path) {
cookieString += `; path=${options.path}`;
}
if (options?.domain) {
cookieString += `; domain=${options.domain}`;
}
if (options?.secure || import.meta.env.PROD) {
cookieString += '; secure';
}
if (options?.sameSite) {
cookieString += `; sameSite=${options.sameSite}`;
}
document.cookie = cookieString;
}
/**
* Récupère un cookie par son nom
* Note: Les cookies httpOnly ne sont pas accessibles via JavaScript
* @param name - Nom du cookie
* @returns Valeur du cookie ou null si non trouvé
*/
export function getCookie(name: string): string | null {
const nameEQ = `${name}=`;
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = cookies[i];
if (cookie == null) continue;
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1, cookie.length);
}
if (cookie.indexOf(nameEQ) === 0) {
return decodeURIComponent(cookie.substring(nameEQ.length, cookie.length));
}
}
return null;
}
/**
* Supprime un cookie
* @param name - Nom du cookie
* @param options - Options (path, domain)
*/
export function deleteCookie(
name: string,
options?: { path?: string; domain?: string },
): void {
let cookieString = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC`;
if (options?.path) {
cookieString += `; path=${options.path}`;
}
if (options?.domain) {
cookieString += `; domain=${options.domain}`;
}
document.cookie = cookieString;
}
/**
* Vérifie si les cookies sont supportés
* @returns true si les cookies sont supportés
*/
export function areCookiesSupported(): boolean {
try {
document.cookie = 'test=1';
const supported = document.cookie.indexOf('test=') !== -1;
document.cookie = 'test=; expires=Thu, 01 Jan 1970 00:00:00 UTC';
return supported;
} catch {
return false;
}
}