Cookies are now preserve long term

This commit is contained in:
DrMint 2024-06-01 13:06:33 +02:00
parent 43d5d1568b
commit 8fc5d2c53d
3 changed files with 104 additions and 51 deletions

View File

@ -28,7 +28,6 @@
## Mid term ## Mid term
- [Feat] Change icons to filled one - [Feat] Change icons to filled one
- [Feat] Save cookies for longer than just the session
- [Bugs] [Timeline] Error if collectible not published? - [Bugs] [Timeline] Error if collectible not published?
- [Feat] [Timeline] display source language - [Feat] [Timeline] display source language
- [Feat] [Timeline] Add details button in footer with credits + last updated / created - [Feat] [Timeline] Add details button in footer with credits + last updated / created

View File

@ -769,6 +769,6 @@ const { currentTheme } = Astro.locals;
); );
document.body.style.setProperty("--scroll-absolute", window.scrollY); document.body.style.setProperty("--scroll-absolute", window.scrollY);
}, },
false { passive: true }
); );
</script> </script>

View File

@ -6,6 +6,8 @@ import { z } from "astro:content";
import { trackRequest, trackEvent } from "src/shared/analytics/analytics"; import { trackRequest, trackEvent } from "src/shared/analytics/analytics";
import { defaultLocale } from "src/i18n/i18n"; import { defaultLocale } from "src/i18n/i18n";
const ninetyDaysInSeconds = 60 * 60 * 24 * 90;
const getAbsoluteLocaleUrl = (locale: string, url: string) => `/${locale}${url}`; const getAbsoluteLocaleUrl = (locale: string, url: string) => `/${locale}${url}`;
const redirect = (redirectURL: string, headers: Record<string, string> = {}): Response => { const redirect = (redirectURL: string, headers: Record<string, string> = {}): Response => {
@ -44,43 +46,99 @@ const localeNegotiator = defineMiddleware(({ cookies, url, request }, next) => {
return next(); return next();
}); });
const handleActionsSearchParams = defineMiddleware(async ({ url }, next) => { const handleActionsSearchParams = defineMiddleware(
const actionLang = url.searchParams.get("action-lang"); async ({ url: { pathname, searchParams }, cookies }, next) => {
if (isValidLocale(actionLang)) { const language = searchParams.get("action-lang");
const currentLocale = getCurrentLocale(url.pathname); if (isValidLocale(language)) {
const currentLocale = getCurrentLocale(pathname);
const pathnameWithoutLocale = currentLocale const pathnameWithoutLocale = currentLocale
? url.pathname.substring(currentLocale.length + 1) ? pathname.substring(currentLocale.length + 1)
: url.pathname; : pathname;
const redirectURL = getAbsoluteLocaleUrl(actionLang, pathnameWithoutLocale); const redirectURL = getAbsoluteLocaleUrl(language, pathnameWithoutLocale);
trackEvent("action-lang"); trackEvent("action-lang");
return redirect(redirectURL, { cookies.set(CookieKeys.Language, language, {
"Set-Cookie": `${CookieKeys.Languages}=${JSON.stringify([actionLang])}; Path=/`, maxAge: ninetyDaysInSeconds,
path: "/",
sameSite: "strict",
}); });
return redirect(redirectURL);
} }
const actionCurrency = url.searchParams.get("action-currency"); const currency = searchParams.get("action-currency");
if (isValidCurrency(actionCurrency)) { if (isValidCurrency(currency)) {
trackEvent("action-currency"); trackEvent("action-currency");
return redirect(url.pathname, { cookies.set(CookieKeys.Currency, currency, {
"Set-Cookie": `${CookieKeys.Currency}=${JSON.stringify(actionCurrency)}; Path=/`, maxAge: ninetyDaysInSeconds,
path: "/",
sameSite: "strict",
}); });
return redirect(pathname);
} }
const actionTheme = url.searchParams.get("action-theme"); const theme = searchParams.get("action-theme");
const verifiedActionTheme = themeSchema.safeParse(actionTheme); if (isValidTheme(theme)) {
if (verifiedActionTheme.success) {
trackEvent("action-theme"); trackEvent("action-theme");
if (verifiedActionTheme.data === "auto") { cookies.set(CookieKeys.Theme, theme, {
return redirect(url.pathname, { maxAge: theme === "auto" ? 0 : ninetyDaysInSeconds,
"Set-Cookie": `${CookieKeys.Theme}=; Path=/; Expires=${new Date(0).toUTCString()}`, path: "/",
}); sameSite: "strict",
}
return redirect(url.pathname, {
"Set-Cookie": `${CookieKeys.Theme}=${verifiedActionTheme.data}; Path=/`,
}); });
return redirect(pathname);
} }
return next(); return next();
}
);
const refreshCookiesMaxAge = defineMiddleware(async ({ cookies }, next) => {
const response = await next();
const theme = cookies.get(CookieKeys.Theme)?.value;
if (isValidTheme(theme) && theme !== "auto") {
cookies.set(CookieKeys.Theme, theme, {
maxAge: ninetyDaysInSeconds,
path: "/",
sameSite: "strict",
});
} else if (theme) {
cookies.set(CookieKeys.Theme, theme, {
maxAge: 0,
path: "/",
sameSite: "strict",
});
}
const currency = cookies.get(CookieKeys.Currency)?.value;
if (isValidCurrency(currency)) {
cookies.set(CookieKeys.Currency, currency, {
maxAge: ninetyDaysInSeconds,
path: "/",
sameSite: "strict",
});
} else if (currency) {
cookies.set(CookieKeys.Currency, currency, {
maxAge: 0,
path: "/",
sameSite: "strict",
});
}
const language = cookies.get(CookieKeys.Language)?.value;
if (isValidLocale(language)) {
cookies.set(CookieKeys.Language, language, {
maxAge: ninetyDaysInSeconds,
path: "/",
sameSite: "strict",
});
} else if (language) {
cookies.set(CookieKeys.Language, language, {
maxAge: 0,
path: "/",
sameSite: "strict",
});
}
return response;
}); });
const addContentLanguageResponseHeader = defineMiddleware(async ({ url }, next) => { const addContentLanguageResponseHeader = defineMiddleware(async ({ url }, next) => {
@ -110,6 +168,7 @@ const analytics = defineMiddleware(async (context, next) => {
export const onRequest = sequence( export const onRequest = sequence(
addContentLanguageResponseHeader, addContentLanguageResponseHeader,
handleActionsSearchParams, handleActionsSearchParams,
refreshCookiesMaxAge,
localeNegotiator, localeNegotiator,
provideLocalsToRequest, provideLocalsToRequest,
analytics analytics
@ -140,25 +199,14 @@ const getBestAcceptedLanguage = (request: Request): string | undefined => {
export enum CookieKeys { export enum CookieKeys {
Currency = "al_pref_currency", Currency = "al_pref_currency",
Theme = "al_pref_theme", Theme = "al_pref_theme",
Languages = "al_pref_languages", Language = "al_pref_language",
} }
export const themeSchema = z.enum(["dark", "light", "auto"]); const themeSchema = z.enum(["dark", "light", "auto"]);
export const getCookieLocale = (cookies: AstroCookies): string | undefined => { export const getCookieLocale = (cookies: AstroCookies): string | undefined => {
const cookie = cookies.get(CookieKeys.Languages); const cookieValue = cookies.get(CookieKeys.Language)?.value;
return isValidLocale(cookieValue) ? cookieValue : undefined;
try {
const json = cookie?.json();
const result = z.array(z.string()).nonempty().safeParse(json);
if (result.success && isValidLocale(result.data[0])) {
return result.data[0];
}
} catch (e) {
console.error(e);
}
return undefined;
}; };
export const getCookieCurrency = (cookies: AstroCookies): string | undefined => { export const getCookieCurrency = (cookies: AstroCookies): string | undefined => {
@ -168,8 +216,7 @@ export const getCookieCurrency = (cookies: AstroCookies): string | undefined =>
export const getCookieTheme = (cookies: AstroCookies): z.infer<typeof themeSchema> | undefined => { export const getCookieTheme = (cookies: AstroCookies): z.infer<typeof themeSchema> | undefined => {
const cookieValue = cookies.get(CookieKeys.Theme)?.value; const cookieValue = cookies.get(CookieKeys.Theme)?.value;
const result = themeSchema.safeParse(cookieValue); return isValidTheme(cookieValue) ? cookieValue : undefined;
return result.success ? result.data : undefined;
}; };
export const isValidCurrency = (currency: string | null | undefined): currency is string => export const isValidCurrency = (currency: string | null | undefined): currency is string =>
@ -177,3 +224,10 @@ export const isValidCurrency = (currency: string | null | undefined): currency i
export const isValidLocale = (locale: string | null | undefined): locale is string => export const isValidLocale = (locale: string | null | undefined): locale is string =>
locale !== null && locale != undefined && cache.locales.map(({ id }) => id).includes(locale); locale !== null && locale != undefined && cache.locales.map(({ id }) => id).includes(locale);
export const isValidTheme = (
theme: string | null | undefined
): theme is z.infer<typeof themeSchema> => {
const result = themeSchema.safeParse(theme);
return result.success;
};