Réécriture SvelteKit statique (Svelte 5 runes + TS, adapter-static)

- Données PeerTube (GADE), Castopod (KUTE) et Mastodon (BOKANTE) bakées au
  build via +page.server.ts (throttle + retry 429 + déduplication)
- Charte OKI complète : tokens, thème sombre par défaut (clair opt-in,
  anti-FOUC), Archivo/Inter self-hébergées, flag-bar, sprite SVG (zéro emoji
  en interface, zéro Font Awesome/CDN)
- i18n FR/EN par routage [[locale]], bundles JSON, hreflang, %lang% serveur
- Pages : accueil, /video/[uuid] (embed, téléchargements, partage,
  commentaires, JSON-LD VideoObject), /categories/[id], /recherche (index
  JSON baké, recherche client), /direct (live + annonce multi-fuseaux),
  /dons, /mentions-legales, /offline + 404 kréyòl
- Motion gwoka : KineticText (scrub view()), ScrollProgressBar, FlagChip,
  reveal syncopé, View Transitions, gate prefers-reduced-motion unique
- PWA : manifest + Workbox generateSW (fallback /offline/), registerSW
  statique
- Sécurité : CSP par page en <meta> (SHA-256 des inline via
  scripts/postbuild-csp.mjs) + headers globaux (_headers Cloudflare et
  .htaccess o2switch), redirections des anciennes URLs PHP
- Doc : docs/DEPLOIEMENT-SVELTEKIT.md
This commit is contained in:
sucupira
2026-07-23 00:49:02 -04:00
parent 5b1a78782a
commit d6efb6736f
147 changed files with 14567 additions and 15145 deletions
+44
View File
@@ -0,0 +1,44 @@
# ANNU KUTE CED — configuration Apache (o2switch)
# Forcer HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Redirections des anciennes URLs PHP
RedirectMatch 301 ^/index\.php.*$ /
RedirectMatch 301 ^/direct\.php.*$ /direct/
RedirectMatch 301 ^/dons\.php.*$ /dons/
RedirectMatch 301 ^/mentions-legales\.php.*$ /mentions-legales/
RewriteRule ^video\.php\?uuid=([A-Za-z0-9]+).*$ /video/$1/ [R=301,L,QSD]
RewriteRule ^categories\.php\?id=([0-9]+).*$ /categories/$1/ [R=301,L,QSD]
RewriteRule ^recherche\.php\?q=(.*)$ /recherche/?q=$1 [R=301,L]
# Page 404
ErrorDocument 404 /404.html
# Headers de sécurité
<IfModule mod_headers.c>
Header always set Content-Security-Policy "style-src 'self' 'unsafe-inline'; img-src 'self' data: https://gade.o-k-i.net https://kute.o-k-i.net https://bokante.o-k-i.net; font-src 'self'; connect-src 'self'; media-src 'self' https://gade.o-k-i.net https://kute.o-k-i.net https://bokante.o-k-i.net; frame-src https://gade.o-k-i.net; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'"
Header always set Strict-Transport-Security "max-age=31536000"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
</IfModule>
# Cache immutable des assets fingerprintés et des fonts
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType image/png "access plus 1 week"
ExpiresByType image/x-icon "access plus 1 week"
</IfModule>
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_URI} ^/(_app|fonts)/ [NC]
RewriteRule .* - [E=CACHE_IMMUTABLE:1]
</IfModule>
<IfModule mod_headers.c>
Header set Cache-Control "public, max-age=31536000, immutable" env=CACHE_IMMUTABLE
</IfModule>
+113
View File
@@ -0,0 +1,113 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex" />
<title>404 — Paj la pa la</title>
<style>
:root {
--noir-oki: #0d0d0d;
--blanc-creme: #fff8e7;
--or-oki: #fdb813;
--vert-oki: #00d66c;
--rouge-oki: #ff1654;
--line: rgba(255, 255, 255, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Archivo', 'Arial Black', Arial, sans-serif;
background: var(--noir-oki);
color: var(--blanc-creme);
min-height: 100dvh;
display: flex;
flex-direction: column;
}
.flag-bar {
height: 6px;
background: linear-gradient(
to right,
var(--noir-oki) 0 25%,
var(--or-oki) 25% 50%,
var(--vert-oki) 50% 75%,
var(--rouge-oki) 75% 100%
);
}
main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1.5rem;
text-align: center;
padding: 4rem 1.5rem;
}
svg {
width: 96px;
height: 96px;
color: var(--or-oki);
}
.status {
font-weight: 900;
font-size: clamp(3rem, 10vw, 6rem);
line-height: 1;
color: var(--or-oki);
}
h1 {
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.01em;
font-size: clamp(1.6rem, 5vw, 2.6rem);
}
p {
opacity: 0.7;
font-family: 'Inter', Arial, sans-serif;
}
a {
display: inline-block;
padding: 0.75rem 1.5rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--or-oki);
border: 2px solid var(--or-oki);
border-radius: 3px;
text-decoration: none;
}
a:hover,
a:focus-visible {
background: var(--or-oki);
color: var(--noir-oki);
}
</style>
</head>
<body>
<div class="flag-bar" aria-hidden="true"></div>
<main>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" aria-hidden="true">
<path d="M4 6h16v12H4z" />
<path d="M4 6l3-3h10l3 3M4 18l3 3h10l3-3M4 10h16M4 14h16" />
</svg>
<p class="status">404</p>
<h1 lang="gcf">Paj la pa la</h1>
<p>Cette page n'existe pas ou a été déplacée.</p>
<a href="/">Retour à l'accueil</a>
</main>
<div class="flag-bar" aria-hidden="true"></div>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
/*
Content-Security-Policy: style-src 'self' 'unsafe-inline'; img-src 'self' data: https://gade.o-k-i.net https://kute.o-k-i.net https://bokante.o-k-i.net; font-src 'self'; connect-src 'self'; media-src 'self' https://gade.o-k-i.net https://kute.o-k-i.net https://bokante.o-k-i.net; frame-src https://gade.o-k-i.net; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
Strict-Transport-Security: max-age=31536000
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()
/_app/*
Cache-Control: public, max-age=31536000, immutable
/fonts/*
Cache-Control: public, max-age=31536000, immutable
/images/*
Cache-Control: public, max-age=604800
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+71
View File
@@ -0,0 +1,71 @@
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/archivo-latin-600-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/archivo-latin-700-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('/fonts/archivo-latin-800-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('/fonts/archivo-latin-900-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/inter-latin-400-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/fonts/inter-latin-500-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/inter-latin-600-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/inter-latin-700-normal.woff2') format('woff2');
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url('/fonts/inter-latin-800-normal.woff2') format('woff2');
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+147
View File
@@ -0,0 +1,147 @@
<svg xmlns="http://www.w3.org/2000/svg">
<!-- Set OKI : viewBox 24x24, stroke 2, angles nets, currentColor -->
<symbol id="ka" viewBox="0 0 24 24">
<path d="M4 6h16v12H4z" />
<path d="M4 6l3-3h10l3 3M4 18l3 3h10l3-3M4 10h16M4 14h16" />
</symbol>
<symbol id="lambi" viewBox="0 0 24 24">
<path d="M4 14l6-8h6l4 4v6l-4 4H8l-4-6z" />
<path d="M10 10h6v6h-6z" />
</symbol>
<symbol id="zetwal" viewBox="0 0 24 24">
<path d="M12 2v20M2 12h20M12 2l4 6h-8zM12 22l-4-6h8zM2 12l6-4v8zM22 12l-6 4v-8z" />
</symbol>
<symbol id="mawon" viewBox="0 0 24 24">
<path d="M9 5L5 9v6l4 4M15 5l4 4v6l-4 4M12 3v4M12 17v4" />
<path d="M10 12h4" />
</symbol>
<symbol id="lakanmou" viewBox="0 0 24 24">
<path d="M12 2l5 6-3 2 4 4-2 8H8l-2-8 4-4-3-2z" />
</symbol>
<symbol id="jaden" viewBox="0 0 24 24">
<path d="M12 22V10M12 10L6 4M12 10l6-6M12 14l-5-3M12 14l5-3" />
</symbol>
<symbol id="kannen" viewBox="0 0 24 24">
<path d="M8 22L16 2M6 18l6-2M9 12l6-2M12 6l5-2" />
</symbol>
<symbol id="lanme" viewBox="0 0 24 24">
<path d="M2 8l5-3 5 3 5-3 5 3M2 14l5-3 5 3 5-3 5 3M2 20l5-3 5 3 5-3 5 3" />
</symbol>
<symbol id="glo" viewBox="0 0 24 24">
<path d="M7 11V6h2v5M11 11V4h2v7M15 11V6h2v5M7 11H5v6l3 5h8l3-5v-6h-2" />
</symbol>
<symbol id="kle" viewBox="0 0 24 24">
<path d="M6 11h12v10H6zM8 11V7l4-4 4 4" />
<path d="M12 15v3" />
</symbol>
<symbol id="pawol" viewBox="0 0 24 24">
<path d="M3 4h18v12H9l-6 5V4z" />
</symbol>
<symbol id="mizik-note" viewBox="0 0 24 24">
<path d="M9 18V4l11-2v14" />
<path d="M5 22l4-4M5 18h4v4zM16 20l4-4M16 16h4v4z" />
</symbol>
<!-- Icônes UI -->
<symbol id="search" viewBox="0 0 24 24">
<path d="M4 4h10v10H4zM14 14l7 7" />
</symbol>
<symbol id="sun" viewBox="0 0 24 24">
<path d="M8 8h8v8H8zM12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9L17 7M7 17l-2.1 2.1" />
</symbol>
<symbol id="moon" viewBox="0 0 24 24">
<path d="M20 14l-8 8-9-9L12 3l-2 5 4 3z" />
</symbol>
<symbol id="menu" viewBox="0 0 24 24">
<path d="M3 6h18M3 12h18M3 18h18" />
</symbol>
<symbol id="close" viewBox="0 0 24 24">
<path d="M4 4l16 16M20 4L4 20" />
</symbol>
<symbol id="heart" viewBox="0 0 24 24">
<path d="M12 21L3 12l4-7h5l5 7z" transform="rotate(180 12 12)" />
</symbol>
<symbol id="broadcast" viewBox="0 0 24 24">
<path d="M12 12m-2 0a2 2 0 104 0 2 2 0 10-4 0" />
<path d="M7 7l-3 5 3 5M17 7l3 5-3 5M4 4L1 12l3 8M20 4l3 8-3 8" />
</symbol>
<symbol id="home" viewBox="0 0 24 24">
<path d="M3 12l9-9 9 9M6 10v10h12V10" />
</symbol>
<symbol id="hashtag" viewBox="0 0 24 24">
<path d="M9 3L7 21M17 3l-2 18M4 8h17M3 16h17" />
</symbol>
<symbol id="play" viewBox="0 0 24 24">
<path d="M6 4l14 8-14 8z" />
</symbol>
<symbol id="pause" viewBox="0 0 24 24">
<path d="M6 4h4v16H6zM14 4h4v16h-4z" />
</symbol>
<symbol id="download" viewBox="0 0 24 24">
<path d="M12 3v12M6 9l6 6 6-6M4 21h16" />
</symbol>
<symbol id="share" viewBox="0 0 24 24">
<path d="M18 8l-4-4v4C8 8 4 12 4 20l4-6c2-1 4-2 6-2v4z" />
</symbol>
<symbol id="external" viewBox="0 0 24 24">
<path d="M14 4h6v6M20 4L10 14M8 6H4v14h14v-4" />
</symbol>
<symbol id="email" viewBox="0 0 24 24">
<path d="M3 5h18v14H3zM3 5l9 7 9-7" />
</symbol>
<symbol id="git" viewBox="0 0 24 24">
<path d="M6 3v18M18 3v18M6 8l12 8M6 16l12-8" />
</symbol>
<symbol id="rss" viewBox="0 0 24 24">
<path d="M4 4v4c7 0 12 5 12 12h4C20 11 13 4 4 4zM4 12v2c3 0 5 2 5 5h2c0-4-3-7-7-7z" />
<path d="M5 19h2v2H5z" />
</symbol>
<symbol id="mastodon" viewBox="0 0 24 24">
<path d="M21 8l-1-4-4-1H8L4 4 3 8v8l2 4h14l2-4z" />
<path d="M8 16V9l4 3 4-3v7" />
</symbol>
<symbol id="peertube" viewBox="0 0 24 24">
<path d="M4 4h7v7H4zM13 4h7v7h-7zM8.5 11h7v7h-7z" />
</symbol>
<symbol id="facebook" viewBox="0 0 24 24">
<path d="M15 3h4v4h-3v3h3l-1 4h-2v7h-4v-7H9v-4h3V6z" />
</symbol>
<symbol id="youtube" viewBox="0 0 24 24">
<path d="M2 7h20v10H2zM10 10l5 2-5 2z" />
</symbol>
<symbol id="instagram" viewBox="0 0 24 24">
<path d="M4 4h16v16H4zM8 8h8v8H8zM17 7h.01" />
</symbol>
<symbol id="x-logo" viewBox="0 0 24 24">
<path d="M4 4l7 9-7 7h3l5.5-6L18 20h3l-7-9 6.5-7h-3L12 9.5 8.5 4z" />
</symbol>
<symbol id="tiktok" viewBox="0 0 24 24">
<path d="M14 3v12l-4 3-3-2 1-4 4-1M14 3l2 4h4v3h-4" />
</symbol>
<symbol id="volume" viewBox="0 0 24 24">
<path d="M4 9v6h4l5 4V5L8 9zM16 9l3 3-3 3M18 6l5 6-5 6" />
</symbol>
<symbol id="mute" viewBox="0 0 24 24">
<path d="M4 9v6h4l5 4V5L8 9zM16 9l5 6M21 9l-5 6" />
</symbol>
<symbol id="next" viewBox="0 0 24 24">
<path d="M5 4l10 8-10 8zM17 4h3v16h-3z" />
</symbol>
<symbol id="copy" viewBox="0 0 24 24">
<path d="M8 8h12v12H8zM4 4h12v4M4 4v12h4" />
</symbol>
<symbol id="check" viewBox="0 0 24 24">
<path d="M4 13l5 5L20 6" />
</symbol>
<symbol id="clock" viewBox="0 0 24 24">
<path d="M4 4h16v16H4zM12 8v5l4 2" />
</symbol>
<symbol id="eye" viewBox="0 0 24 24">
<path d="M2 12l6-6h8l6 6-6 6H8zM9 12h6" transform="translate(0 0)" />
</symbol>
<symbol id="calendar" viewBox="0 0 24 24">
<path d="M4 5h16v16H4zM4 10h16M9 3v4M15 3v4" />
</symbol>
<symbol id="live-dot" viewBox="0 0 24 24">
<path d="M12 12m-5 0a5 5 0 1010 0 5 5 0 10-10 0" fill="currentColor" stroke="none" />
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

+31
View File
@@ -0,0 +1,31 @@
{
"name": "ANNU KUTE CED — Hub multimédia du podcast",
"short_name": "ANNU KUTE CED",
"description": "Vidéos, épisodes audio et actualités du podcast ANNU KUTE CED, en direct du fédivers.",
"start_url": "/",
"scope": "/",
"display": "standalone",
"background_color": "#0D0D0D",
"theme_color": "#0D0D0D",
"lang": "fr",
"icons": [
{
"src": "/images/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/images/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/images/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
+5
View File
@@ -0,0 +1,5 @@
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/sw.js', { scope: '/' });
});
}
+4
View File
@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://annukuteced.buzz/sitemap.xml
+10
View File
@@ -0,0 +1,10 @@
(function () {
var root = document.documentElement;
root.classList.add('js');
try {
var theme = localStorage.getItem('kute-theme');
if (theme === 'light') {
root.classList.add('light-theme');
}
} catch (e) {}
})();