-
-
-
+
+
+
);
}
diff --git a/app/lib/getTelefuncContext.ts b/app/lib/getTelefuncContext.ts
new file mode 100644
index 0000000..5071844
--- /dev/null
+++ b/app/lib/getTelefuncContext.ts
@@ -0,0 +1,18 @@
+import type { FastifyRequest, FastifyReply } from "fastify";
+
+import { getContext } from "telefunc";
+
+export function getTelefuncContext() {
+ const context = getContext<{
+ fastify: {
+ request: FastifyRequest;
+ reply: FastifyReply;
+ };
+ }>();
+
+ return {
+ ...context,
+ reply: context.fastify.reply,
+ request: context.fastify.request,
+ };
+}
diff --git a/app/lib/navigation.ts b/app/lib/navigation.ts
index c584b75..a74b5e2 100644
--- a/app/lib/navigation.ts
+++ b/app/lib/navigation.ts
@@ -2,12 +2,13 @@ const navigationsTypes = {
GLOBAL: "👋 Général",
CERTIFICATIONS: "🎓 Certifications",
DOCUMENTATIONS: "📚 Documentations",
+ OTHER: "🔗 Autres",
};
export type NavigationSection = {
title: string;
type: (typeof navigationsTypes)[keyof typeof navigationsTypes];
- position: "start" | "auto";
+ position: "start" | "end" | "auto";
links: NavigationLink[];
};
@@ -47,6 +48,15 @@ export const navigation: NavigationSection[] = [
{ title: "Partages et réutilisations", href: "/docs/communaute/partages", subitems: [] },
],
},
+ {
+ title: "Légal",
+ type: navigationsTypes.OTHER,
+ position: "end",
+ links: [
+ { title: "Mentions légales", href: "/mentions-legales", subitems: [] },
+ { title: "Politique de confidentialité", href: "/politique-de-confidentialite", subitems: [] },
+ ],
+ },
{
title: "Développeur Web et Web Mobile",
type: navigationsTypes.CERTIFICATIONS,
diff --git a/app/package.json b/app/package.json
index 01afe5a..a6e5eba 100644
--- a/app/package.json
+++ b/app/package.json
@@ -7,6 +7,7 @@
"prod": "npm-run-all build preview"
},
"dependencies": {
+ "@fastify/cookie": "^11.0.2",
"@fastify/middie": "^9.0.3",
"@fastify/static": "^8.1.1",
"@fontsource-variable/inter": "^5.2.5",
diff --git a/app/pages/+Head.tsx b/app/pages/+Head.tsx
index cb6c941..02506c6 100644
--- a/app/pages/+Head.tsx
+++ b/app/pages/+Head.tsx
@@ -1,10 +1,28 @@
+import { usePageContext } from "vike-react/usePageContext";
import logoUrl from "@/assets/logo.svg";
import React from "react";
export default function HeadDefault() {
+ const { cookies } = usePageContext();
+
return (
<>
+ {cookies.consent.analytics && (
+ <>
+
+
+
+
+ >
+ )}
>
);
}
diff --git a/app/pages/+config.ts b/app/pages/+config.ts
index 1cea509..13565e5 100644
--- a/app/pages/+config.ts
+++ b/app/pages/+config.ts
@@ -25,7 +25,7 @@ export default {
image: "/og.webp",
- // prerender: true,
+ passToClient: ["cookies"],
prefetchStaticAssets: "hover",
extends: vikeReact,
diff --git a/app/pages/mentions-legales/+Page.tsx b/app/pages/mentions-legales/+Page.tsx
new file mode 100644
index 0000000..8e0d668
--- /dev/null
+++ b/app/pages/mentions-legales/+Page.tsx
@@ -0,0 +1,52 @@
+import React from "react";
+
+export function Page() {
+ return (
+
+
Mentions légales
+
+
+ Éditeur du site
+
+ Nom : Gauthier Daniels
+
+
+ Adresse physique : {" "}
+ 689 Chemin Latéral, 45240 La Ferté Saint-Aubin
+
+
+ Adresse email : {" "}
+ gauthier@gauthierdaniels.fr
+
+
+ Téléphone : +33 6 52 84 92 41
+
+
+
+
+ Directeur de la publication
+
+ Nom : Gauthier Daniels
+
+
+ Adresse email : {" "}
+ gauthier@gauthierdaniels.fr
+
+
+
+
+ Hébergement du site
+
+ Nom : Infomaniak Network SA
+
+
+ Site internet : www.infomaniak.com
+
+
+ Adresse physique : {" "}
+ Rue Eugène Marziano 25, 1227 Les Acacias (GE), Suisse
+
+
+
+ );
+}
diff --git a/app/pages/politique-de-confidentialite/+Page.tsx b/app/pages/politique-de-confidentialite/+Page.tsx
new file mode 100644
index 0000000..e87265a
--- /dev/null
+++ b/app/pages/politique-de-confidentialite/+Page.tsx
@@ -0,0 +1,189 @@
+import { CookiesContext } from "@/components/common/Cookies";
+import { Button } from "@/components/syntax/Button";
+import { Link } from "@/components/common/Link";
+import React, { useContext } from "react";
+
+export function Page() {
+ const { setIsOpen } = useContext(CookiesContext);
+
+ return (
+
+
+ Politique de confidentialité
+
+
+
+ 1. Introduction
+
+
+ Sur Memento Dev , qui est un site à but de documentation, je prends très au sérieux la
+ protection de votre vie privée et de vos données personnelles.
+
+
+
+ Cette politique de confidentialité explique comment je collecte, utilise et protège les informations des
+ utilisateurs de ce site.
+
+
+
+
+ 2. Outils externes
+
+
+ Mon site ne collecte aucune donnée personnelle de manière directe. Cependant, j'utilise des outils externes
+ pour améliorer votre expérience utilisateur et analyser l'utilisation du site.
+
+
+
+ a. Google Analytics
+
+ J'utilise Google Analytics pour analyser le trafic et l'utilisation de mon site. Les
+ données collectées par Google Analytics sont anonymisées et ne sont pas partagées avec des tiers.
+
+
+
+
+ b. Umami
+
+ Umami est un autre outil d'analyse que j'utilise pour comprendre comment les visiteurs
+ interagissent avec mon site. Comme Google Analytics , les données collectées sont
+ anonymisées et ne sont pas tranmises à des tiers.
+
+
+
+
+ c. Cookie "theme"
+
+ J'utilise et dépose un cookie nommé "theme" pour mémoriser votre préférence de thème (clair ou sombre). Ce
+ cookie est utilisé uniquement pour personnaliser votre expérience utilisateur et n'est pas utilisé à des
+ fins de suivi ou de marketing.
+
+
+
+
+
+ 3. Cookies
+
+
+ Ce site utilise des cookies pour améliorer votre expérience utilisateur et analyser l'utilisation du site. Les
+ cookies sont de petits fichiers texte stockés sur votre appareil lorsque vous visitez un site web. Ils
+ permettent de mémoriser vos préférences et d'analyser le trafic du site.
+
+
+
+ Vous pouvez gérer vos préférences de cookies directement via les paramètres de votre navigateur. La plupart
+ des navigateurs vous permettent de refuser ou de supprimer les cookies. Cependant, cela peut affecter votre
+ expérience sur le site et certaines fonctionnalités peuvent ne pas fonctionner correctement (comme la
+ personnalisation du thème par exemple).
+
+
+
+ Pour reconfigurer les cookies, vous pouvez appuyer sur le bouton "Paramétrer les cookies" à la suite de ce
+ paragraphe.
+
+
+ setIsOpen(true)}>
+ Paramétrer les cookies
+
+
+
+ Pour plus d'informations sur la gestion des cookies, vous pouvez consulter la documentation de votre
+ navigateur. Voici quelques liens utiles :
+
+
+
+
+
+ Google Chrome
+
+
+
+
+ Mozilla Firefox
+
+
+
+
+ Internet Explorer
+
+
+
+
+ Safari
+
+
+
+
+ Microsoft Edge
+
+
+
+
+
+
+ 4. Utilisation des données
+
+
+ Les données collectées par les outils d'analyse sont utilisées uniquement pour améliorer le site Memento Dev
+ et comprendre comment les visiteurs l'utilisent. Je n'utilise pas ces données à des fins commerciales ou pour
+ cibler des publicités.
+
+
+
+
+ 5. Protection des données
+
+
+ Les données collectées par Google Analytics et Umami sont anonymisées et
+ stockées de manière sécurisée par ces services. Je ne stocke aucune donnée personnelle sur mes propres
+ serveurs. Pour plus d'informations sur la manière dont ces services protègent vos données, veuillez consulter
+ leurs politiques de confidentialité respectives.
+
+
+
+
+ 6. Vos droits
+
+
+ Étant donné que je ne stocke aucune donnée personnelle, je ne suis pas en mesure de répondre aux demandes
+ d'accès, de rectification ou de suppression de données personnelles. Cependant, vous pouvez gérer vos
+ préférences de cookies directement via les paramètres de votre navigateur. Pour toute question concernant vos
+ droits, veuillez me contacter à gauthier@gauthierdaniels.fr .
+
+
+
+
+
+ 7. Modifications de la Politique de Confidentialité
+
+
+
+ Je me réserve le droit de modifier cette politique de confidentialité à tout moment. Les modifications seront
+ publiées sur cette page et entreront en vigueur immédiatement. Je vous encourage à consulter régulièrement
+ cette page pour rester informé de mes pratiques en matière de confidentialité.
+
+
+
+
+ 8. Contact
+
+
+ Si vous avez des questions ou des préoccupations concernant ma politique de confidentialité, veuillez me
+ contacter à l'adresse suivante : gauthier@gauthierdaniels.fr .
+
+
+
+ );
+}
diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml
index ebbe8bb..0057572 100644
--- a/app/pnpm-lock.yaml
+++ b/app/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@fastify/cookie':
+ specifier: ^11.0.2
+ version: 11.0.2
'@fastify/middie':
specifier: ^9.0.3
version: 9.0.3
@@ -94,7 +97,7 @@ importers:
version: 1.2.1
telefunc:
specifier: ^0.1.87
- version: 0.1.87(@babel/core@7.26.10)(@babel/parser@7.27.0)(@babel/types@7.27.0)(react-streaming@0.3.50(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
+ version: 0.1.87(@babel/core@7.7.4)(@babel/parser@7.27.0)(@babel/types@7.27.0)(react-streaming@0.3.50(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
unplugin-fonts:
specifier: ^1.3.1
version: 1.3.1(vite@6.2.6(@types/node@18.19.86)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3))
@@ -1301,6 +1304,9 @@ packages:
'@fastify/ajv-compiler@4.0.2':
resolution: {integrity: sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==}
+ '@fastify/cookie@11.0.2':
+ resolution: {integrity: sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==}
+
'@fastify/error@4.1.0':
resolution: {integrity: sha512-KeFcciOr1eo/YvIXHP65S94jfEEqn1RxTRBT1aJaHxY5FK0/GDXYozsQMMWlZoHgi8i0s+YtrLsgj/JkUUjSkQ==}
@@ -9754,6 +9760,11 @@ snapshots:
ajv-formats: 3.0.1(ajv@8.17.1)
fast-uri: 3.0.6
+ '@fastify/cookie@11.0.2':
+ dependencies:
+ cookie: 1.0.2
+ fastify-plugin: 5.0.1
+
'@fastify/error@4.1.0': {}
'@fastify/fast-json-stringify-compiler@5.0.3':
@@ -10506,10 +10517,10 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
- '@typescript-eslint/eslint-plugin@2.34.0(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)':
+ '@typescript-eslint/eslint-plugin@2.34.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies:
'@typescript-eslint/experimental-utils': 2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
- '@typescript-eslint/parser': 8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
+ '@typescript-eslint/parser': 2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.24.0(jiti@2.4.2)
functional-red-black-tree: 1.0.1
regexpp: 3.2.0
@@ -12828,13 +12839,13 @@ snapshots:
eslint-config-react-app@5.2.1(@typescript-eslint/eslint-plugin@2.34.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(babel-eslint@10.0.3(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-flowtype@3.13.0(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-import@2.18.2(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-jsx-a11y@6.2.3(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-react-hooks@1.7.0(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-react@7.16.0(eslint@9.24.0(jiti@2.4.2)))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3):
dependencies:
- '@typescript-eslint/eslint-plugin': 2.34.0(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
+ '@typescript-eslint/eslint-plugin': 2.34.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
babel-eslint: 10.0.3(eslint@9.24.0(jiti@2.4.2))
confusing-browser-globals: 1.0.11
eslint: 9.24.0(jiti@2.4.2)
eslint-plugin-flowtype: 3.13.0(eslint@9.24.0(jiti@2.4.2))
- eslint-plugin-import: 2.18.2(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))
+ eslint-plugin-import: 2.18.2(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))
eslint-plugin-jsx-a11y: 6.2.3(eslint@9.24.0(jiti@2.4.2))
eslint-plugin-react: 7.16.0(eslint@9.24.0(jiti@2.4.2))
eslint-plugin-react-hooks: 1.7.0(eslint@9.24.0(jiti@2.4.2))
@@ -12859,11 +12870,11 @@ snapshots:
schema-utils: 2.7.1
webpack: 4.41.2
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.24.0(jiti@2.4.2)):
+ eslint-module-utils@2.12.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.24.0(jiti@2.4.2)):
dependencies:
debug: 3.2.7(supports-color@6.1.0)
optionalDependencies:
- '@typescript-eslint/parser': 8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
+ '@typescript-eslint/parser': 2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.24.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
@@ -12874,7 +12885,7 @@ snapshots:
eslint: 9.24.0(jiti@2.4.2)
lodash: 4.17.21
- eslint-plugin-import@2.18.2(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2)):
+ eslint-plugin-import@2.18.2(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2)):
dependencies:
array-includes: 3.1.8
contains-path: 0.1.0
@@ -12882,14 +12893,14 @@ snapshots:
doctrine: 1.5.0
eslint: 9.24.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.24.0(jiti@2.4.2))
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.24.0(jiti@2.4.2))
has: 1.0.4
minimatch: 3.1.2
object.values: 1.2.1
read-pkg-up: 2.0.0
resolve: 1.22.10
optionalDependencies:
- '@typescript-eslint/parser': 8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
+ '@typescript-eslint/parser': 2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -16363,7 +16374,7 @@ snapshots:
dependencies:
'@babel/core': 7.7.4
'@svgr/webpack': 4.3.3
- '@typescript-eslint/eslint-plugin': 2.34.0(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
+ '@typescript-eslint/eslint-plugin': 2.34.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
babel-eslint: 10.0.3(eslint@9.24.0(jiti@2.4.2))
babel-jest: 24.9.0(@babel/core@7.7.4)
@@ -16379,7 +16390,7 @@ snapshots:
eslint-config-react-app: 5.2.1(@typescript-eslint/eslint-plugin@2.34.0(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(babel-eslint@10.0.3(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-flowtype@3.13.0(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-import@2.18.2(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-jsx-a11y@6.2.3(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-react-hooks@1.7.0(eslint@9.24.0(jiti@2.4.2)))(eslint-plugin-react@7.16.0(eslint@9.24.0(jiti@2.4.2)))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)
eslint-loader: 3.0.2(eslint@9.24.0(jiti@2.4.2))(webpack@4.41.2)
eslint-plugin-flowtype: 3.13.0(eslint@9.24.0(jiti@2.4.2))
- eslint-plugin-import: 2.18.2(@typescript-eslint/parser@8.29.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))
+ eslint-plugin-import: 2.18.2(@typescript-eslint/parser@2.34.0(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))
eslint-plugin-jsx-a11y: 6.2.3(eslint@9.24.0(jiti@2.4.2))
eslint-plugin-react: 7.16.0(eslint@9.24.0(jiti@2.4.2))
eslint-plugin-react-hooks: 1.7.0(eslint@9.24.0(jiti@2.4.2))
@@ -17435,7 +17446,7 @@ snapshots:
tapable@2.2.1: {}
- telefunc@0.1.87(@babel/core@7.26.10)(@babel/parser@7.27.0)(@babel/types@7.27.0)(react-streaming@0.3.50(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0):
+ telefunc@0.1.87(@babel/core@7.7.4)(@babel/parser@7.27.0)(@babel/types@7.27.0)(react-streaming@0.3.50(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0):
dependencies:
'@brillout/import': 0.2.6
'@brillout/json-serializer': 0.5.15
@@ -17444,7 +17455,7 @@ snapshots:
es-module-lexer: 1.6.0
ts-morph: 19.0.0
optionalDependencies:
- '@babel/core': 7.26.10
+ '@babel/core': 7.7.4
'@babel/parser': 7.27.0
'@babel/types': 7.27.0
react: 19.1.0
diff --git a/app/providers/ThemeProvider.telefunc.ts b/app/providers/ThemeProvider.telefunc.ts
new file mode 100644
index 0000000..e1f1fc8
--- /dev/null
+++ b/app/providers/ThemeProvider.telefunc.ts
@@ -0,0 +1,22 @@
+import type { Theme } from "@/contexts/ThemeContext";
+
+import { getTelefuncContext } from "@/lib/getTelefuncContext";
+import { CookieParser } from "@/services/CookieParser";
+
+export async function onUpdateThemeCookie(value: Theme) {
+ const context = getTelefuncContext();
+ const { reply } = context;
+
+ CookieParser.set(reply, "theme", value, 365);
+
+ return { ok: true, message: "Updated theme cookie", value };
+}
+
+export async function onDeleteThemeCookie() {
+ const context = getTelefuncContext();
+ const { reply } = context;
+
+ CookieParser.delete(reply, "theme");
+
+ return { ok: true, message: "Deleted theme cookie" };
+}
diff --git a/app/providers/ThemeProvider.tsx b/app/providers/ThemeProvider.tsx
index 9d248f4..279ce56 100644
--- a/app/providers/ThemeProvider.tsx
+++ b/app/providers/ThemeProvider.tsx
@@ -1,5 +1,8 @@
+import { onUpdateThemeCookie, onDeleteThemeCookie } from "@/providers/ThemeProvider.telefunc";
import { ThemeContext, type Theme } from "@/contexts/ThemeContext";
+import { usePageContext } from "vike-react/usePageContext";
import React, { useEffect, useState } from "react";
+import { toast } from "react-toastify";
type ThemeProviderProps = {
children: React.ReactNode;
@@ -7,6 +10,7 @@ type ThemeProviderProps = {
};
export function ThemeProvider(props: ThemeProviderProps) {
+ const { cookies } = usePageContext();
const [theme, setTheme] = useState
(props.defaultTheme || "light");
useEffect(() => {
@@ -14,7 +18,17 @@ export function ThemeProvider(props: ThemeProviderProps) {
rootElement.classList.toggle("dark", theme === "dark");
rootElement.classList.toggle("light", theme === "light");
- }, [theme]);
+
+ if (cookies.consent.customization) {
+ onUpdateThemeCookie(theme).catch(() => {
+ toast.error("Erreur lors de la mise à jour du cookie de thème");
+ });
+ } else {
+ onDeleteThemeCookie().catch(() => {
+ toast.error("Erreur lors de la suppression du cookie de thème");
+ });
+ }
+ }, [theme, cookies.consent.customization]);
return {props.children} ;
}
diff --git a/app/server/vike-handler.ts b/app/server/vike-handler.ts
index 5d1ae96..3613d5f 100644
--- a/app/server/vike-handler.ts
+++ b/app/server/vike-handler.ts
@@ -1,10 +1,29 @@
///
-import { renderPage } from "vike/server";
+
// TODO: stop using universal-middleware and directly integrate server middlewares instead. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
import type { Get, UniversalHandler } from "@universal-middleware/core";
+import { CookieParser } from "@/services/CookieParser";
+import { renderPage } from "vike/server";
+
export const vikeHandler: Get<[], UniversalHandler> = () => async (request, context, runtime) => {
- const pageContextInit = { ...context, ...runtime, urlOriginal: request.url, headersOriginal: request.headers };
+ const cookies = new CookieParser(request.headers.get("cookie") || "");
+
+ const pageContextInit = {
+ ...context,
+ ...runtime,
+ urlOriginal: request.url,
+ headersOriginal: request.headers,
+ cookies: {
+ consent: {
+ analytics: cookies.get("analytics", (value) => value === "true") || false,
+ customization: cookies.get("customization", (value) => value === "true") || false,
+ },
+ settings: {
+ theme: cookies.get("theme") || "light",
+ },
+ },
+ };
const pageContext = await renderPage(pageContextInit);
const response = pageContext.httpResponse;
diff --git a/app/services/CookieParser.ts b/app/services/CookieParser.ts
new file mode 100644
index 0000000..dfab89e
--- /dev/null
+++ b/app/services/CookieParser.ts
@@ -0,0 +1,70 @@
+import type { PageContext } from "vike/types";
+
+import { FastifyReply } from "fastify";
+
+type ConsentCookies = keyof PageContext["cookies"]["consent"];
+type SettingsCookie = keyof PageContext["cookies"]["settings"];
+
+type CookieKeys = ConsentCookies | SettingsCookie;
+
+export class CookieParser {
+ private rawCookies: string;
+ private cookies: Record;
+
+ constructor(rawCookies: string) {
+ this.rawCookies = rawCookies;
+ this.cookies = {};
+ this.parse();
+ }
+
+ public static buildOptions(daysDuration: number | Date) {
+ let expires: Date;
+
+ if (daysDuration instanceof Date) {
+ expires = daysDuration;
+ } else {
+ expires = new Date(Date.now() + 60 * 60 * 24 * daysDuration * 1000);
+ }
+
+ return {
+ path: "/",
+ httpOnly: true,
+ secure: process.env.NODE_ENV === "production",
+ expires,
+ };
+ }
+
+ parse() {
+ this.cookies = this.rawCookies.split("; ").reduce(
+ (acc, cookie) => {
+ const [key, value] = cookie.split("=");
+ acc[key] = decodeURIComponent(value);
+ return acc;
+ },
+ {} as Record,
+ );
+ }
+
+ get(key: CookieKeys): string | undefined;
+ get(key: CookieKeys, formatter: (value: string) => T): T | undefined;
+ get(key: CookieKeys, formatter?: (value: string) => T): T | string | undefined {
+ const value = this.cookies[key];
+
+ if (formatter) return formatter(value);
+ return value;
+ }
+
+ static set(reply: FastifyReply, key: CookieKeys, value: string, daysDuration = 30): FastifyReply {
+ const options = CookieParser.buildOptions(daysDuration);
+ reply.setCookie(key, value, options);
+
+ return reply;
+ }
+
+ static delete(reply: FastifyReply, key: CookieKeys): FastifyReply {
+ const options = CookieParser.buildOptions(new Date("1900-01-01"));
+ reply.setCookie(key, "", options);
+
+ return reply;
+ }
+}