feat(app) : socle SvelteKit phase 0 — SSO, thème OKI, adapter-node

- Scaffold SvelteKit 2 + Svelte 5 (runes), TypeScript strict, adapter-node
- Auth SSO via header Ynh-User (hooks.server) ; accueil public minimal sans header,
  401 sur toute autre route ; DEV_USER en dev local uniquement
- Design system OKI : tokens, thème sombre par défaut, clair opt-in anti-FOUC,
  flag-bar, fonts Archivo/Inter self-hébergées, sprite icons.svg (zéro emoji)
- CSP via kit.csp (mode nonce) + en-têtes de sécurité ; paths.base configurable
  au build (BASE_PATH) pour le sous-chemin YunoHost, paths.relative = false
- i18n fr (structure prête gcf/en), pages : accueil public/privé, erreurs designées
- Tests vitest : résolution SSO + routes publiques (10 cas)
- Vérifié : check/lint/test/build verts, test fumée HTTP sur build (SSO, CSP,
  sous-chemin /veille), zéro emoji et zéro domaine tiers dans le build
This commit is contained in:
cyber-mawonaj
2026-08-01 09:18:27 -04:00
parent dc6bf1bbde
commit ff26136dfb
37 changed files with 5178 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
+50
View File
@@ -0,0 +1,50 @@
/* Fonts self-hébergées (woff2, subset latin) — zéro appel externe. */
/* Fichiers : Archivo 600/700/800 (display) + Inter 400/600/700 (body). */
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('archivo-latin-600.woff2') format('woff2');
}
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('archivo-latin-700.woff2') format('woff2');
}
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('archivo-latin-800.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('inter-latin-400.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('inter-latin-600.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('inter-latin-700.woff2') format('woff2');
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
<!-- Set SVG veille-ia (conventions OKI : viewBox 24×24, stroke 2 px, currentColor, angles nets).
Usage : <use href="icons.svg#id" /> via le composant Icon.svelte. -->
<symbol id="ka" viewBox="0 0 24 24">
<!-- Tambour ka : icône maîtresse -->
<path d="M7 3h10l2 18H5L7 3z" />
<path d="M5 8h14M6 15h12" />
<path d="M9 3v5M15 3v5" />
</symbol>
<symbol id="accueil" viewBox="0 0 24 24">
<path d="M3 12l9-9 9 9" />
<path d="M5 10v11h14V10" />
</symbol>
<symbol id="registre" viewBox="0 0 24 24">
<path d="M4 4h16v16H4z" />
<path d="M8 8h8M8 12h8M8 16h5" />
</symbol>
<symbol id="profils" viewBox="0 0 24 24">
<circle cx="12" cy="8" r="4" />
<path d="M4 21l2-5h12l2 5" />
</symbol>
<symbol id="cible" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="9" />
<circle cx="12" cy="12" r="4" />
<path d="M12 1v4M12 19v4M1 12h4M19 12h4" />
</symbol>
<symbol id="alerte" viewBox="0 0 24 24">
<path d="M6 16v-5a6 6 0 0 1 12 0v5l2 3H4l2-3z" />
<path d="M10 21a2 2 0 0 0 4 0" />
</symbol>
<symbol id="cle" viewBox="0 0 24 24">
<circle cx="8" cy="12" r="5" />
<path d="M13 12h9M18 12v4M21 12v3" />
</symbol>
<symbol id="avertissement" viewBox="0 0 24 24">
<path d="M12 3L22 20H2L12 3z" />
<path d="M12 10v4M12 17v0.5" />
</symbol>
<symbol id="verifie" viewBox="0 0 24 24">
<path d="M3 13l6 6L21 5" />
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+3
View File
@@ -0,0 +1,3 @@
# App privée derrière SSO : aucune indexation.
User-agent: *
Disallow: /
+11
View File
@@ -0,0 +1,11 @@
// Bascule de thème anti-FOUC — script externe (CSP : aucun script inline).
// Thème sombre par défaut (identité OKI) ; clair opt-in persisté en localStorage.
(function () {
try {
if (localStorage.getItem('veille-ia-theme') === 'light') {
document.documentElement.classList.add('light-theme');
}
} catch {
// localStorage indisponible : thème sombre par défaut, rien à faire.
}
})();