Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/root-core.server.ts
T

124 lines
5.4 KiB
TypeScript

import type { LoaderFunctionArgs } from "react-router";
import { redirect } from "react-router";
import { allCookieCategories } from "./application/cookies/ApplicationCookies";
import { CookieCategory } from "./application/cookies/CookieCategory";
import EventsService from "./modules/events/services/.server/EventsService";
import type { UserPreferencesUpdatedDto } from "./modules/events/dtos/UserPreferencesUpdatedDto";
import AuthService from "./modules/users/services/AuthService";
import { enabledLocales, getTranslations, i18nCookie } from "./locale/i18next.server";
import { isSupportedLocale } from "./locale/i18n";
import { getCanonicalHostRedirectUrl } from "./utils/app/HostRouting.server";
import { safeRedirectPathFromRequest } from "./utils/app/RedirectSafety.server";
import { loadRootData } from "./utils/data/.server/rootData";
import { getUser, updateUserProfile } from "./utils/db/users.db.server";
import { createUserSession, getUserInfo, validateCSRFToken } from "./utils/session.server";
import { nextLightOrDarkMode, normalizeLightOrDarkMode, persistUserLightOrDarkMode } from "./utils/theme/userThemePreference.server";
import { runWithRlsContext } from "@hivegrid/db/rls-context.server";
export async function runCoreRootMiddleware(request: Request, next: () => Promise<Response>) {
const userInfo = await getUserInfo(request);
if (!userInfo.userId) return next();
return runWithRlsContext({ userId: userInfo.userId }, next);
}
export async function loadCoreRootData({ request, params }: Pick<LoaderFunctionArgs, "request" | "params">) {
const canonicalRedirectUrl = getCanonicalHostRedirectUrl(request);
if (canonicalRedirectUrl) {
throw redirect(canonicalRedirectUrl, { status: 308 });
}
return loadRootData({ request, params });
}
export async function handleCoreRootAction(request: Request) {
const canonicalRedirectUrl = getCanonicalHostRedirectUrl(request);
if (canonicalRedirectUrl) {
throw redirect(canonicalRedirectUrl, { status: 308 });
}
const userInfo = await getUserInfo(request);
const form = await request.clone().formData();
const action = form.get("action");
if (userInfo.userId && (action === "toggleLightOrDarkMode" || action === "setLocale" || action === "setTheme")) {
await validateCSRFToken(request, { formData: form });
}
const redirectTo = safeRedirectPathFromRequest(request, form.get("redirect")?.toString());
if (action === "toggleLightOrDarkMode") {
const lightOrDarkMode = nextLightOrDarkMode(userInfo.lightOrDarkMode);
if (userInfo.userId) {
const user = await getUser(userInfo.userId);
if (user) {
await EventsService.createUserEventWithMutation(
{
request,
event: "user.preferences.updated",
userId: user.id,
data: {
old: { lightOrDarkMode: normalizeLightOrDarkMode(user.lightOrDarkMode) },
new: { lightOrDarkMode },
user: { id: user.id, email: user.email },
} satisfies UserPreferencesUpdatedDto,
},
() => persistUserLightOrDarkMode(user.id, lightOrDarkMode)
);
}
}
return createUserSession({ ...userInfo, lightOrDarkMode }, redirectTo, { request });
}
if (action === "setLocale") {
const lng = form.get("lng")?.toString() ?? "";
if (!isSupportedLocale(lng) || !enabledLocales.includes(lng)) {
throw new Response(null, { status: 404 });
}
if (userInfo.userId) {
const user = await getUser(userInfo.userId);
if (user) {
await EventsService.createUserEventWithMutation(
{
request,
event: "user.preferences.updated",
userId: user.id,
data: {
old: { locale: user.locale ?? "" },
new: { locale: lng },
user: { id: user.id, email: user.email },
} satisfies UserPreferencesUpdatedDto,
},
() => updateUserProfile({ locale: lng }, user.id)
);
}
}
const localeSession = await createUserSession({ ...userInfo, lng }, redirectTo, { request });
localeSession.headers.append("Set-Cookie", await i18nCookie.serialize(lng));
return localeSession;
}
if (action === "setCookieConsent") {
const preserveParams = ["ref", "source", "utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"];
const allowed = form.getAll("cookies[]").map((field) => field.toString());
const cookies: { category: string; allowed: boolean }[] = [];
allCookieCategories.forEach((item) => {
const categoryName = CookieCategory[item];
const isAllowed = allowed.some((field) => field === categoryName.toString());
cookies.push({ category: categoryName, allowed: isAllowed ?? item === CookieCategory.REQUIRED });
});
const searchParams = new URLSearchParams();
preserveParams.forEach((param) => {
const value = form.get(param)?.toString();
if (value) searchParams.set(param, value);
});
return createUserSession({ ...userInfo, cookies }, redirectTo, { request });
}
if (action === "login") {
return AuthService.loginFromRequest(request, form);
}
if (action === "setTheme") {
const { t } = await getTranslations(request);
const theme = form.get("theme")?.toString();
if (!theme) {
return Response.json({ error: t("settings.theme.errors.invalidTheme") });
}
return createUserSession({ ...userInfo, theme }, redirectTo, { request });
}
}