Files
pingvin-share/frontend/src/middleware.ts
Aaron df1ffaa2bc feat: add legal page with configuration options (#724)
* Addconfig entries for legal notice

* Add legal route handling to middleware

* Make legal notice public

* Add legal category to config sidebar

* Add legal notice page

* Add German translations for legal notice and configuration options

* Replace legal page with separate imprint and privacy pages

* Update middleware

* Add footer component

* Update legal text descriptions to indicate Markdown support again

* Refactor footer layout

* Add zIndex to footer component

* improve mobile layout

* run formatter

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2025-01-02 17:29:01 +01:00

146 lines
3.8 KiB
TypeScript

import { jwtDecode } from "jwt-decode";
import { NextRequest, NextResponse } from "next/server";
import configService from "./services/config.service";
// This middleware redirects based on different conditions:
// - Authentication state
// - Setup status
// - Admin privileges
export const config = {
matcher: "/((?!api|static|.*\\..*|_next).*)",
};
export async function middleware(request: NextRequest) {
const routes = {
unauthenticated: new Routes(["/auth/*", "/"]),
public: new Routes([
"/share/*",
"/s/*",
"/upload/*",
"/error",
"/imprint",
"/privacy",
]),
admin: new Routes(["/admin/*"]),
account: new Routes(["/account*"]),
disabled: new Routes([]),
};
// Get config from backend
const apiUrl = process.env.API_URL || "http://localhost:8080";
const config = await (await fetch(`${apiUrl}/api/configs`)).json();
const getConfig = (key: string) => {
return configService.get(key, config);
};
const route = request.nextUrl.pathname;
let user: { isAdmin: boolean } | null = null;
const accessToken = request.cookies.get("access_token")?.value;
try {
const claims = jwtDecode<{ exp: number; isAdmin: boolean }>(
accessToken as string,
);
if (claims.exp * 1000 > Date.now()) {
user = claims;
}
} catch {
user = null;
}
if (!getConfig("share.allowRegistration")) {
routes.disabled.routes.push("/auth/signUp");
}
if (getConfig("share.allowUnauthenticatedShares")) {
routes.public.routes = ["*"];
}
if (!getConfig("smtp.enabled")) {
routes.disabled.routes.push("/auth/resetPassword*");
}
if (!getConfig("legal.enabled")) {
routes.disabled.routes.push("/imprint", "/privacy");
} else {
if (!getConfig("legal.imprintText") && !getConfig("legal.imprintUrl")) {
routes.disabled.routes.push("/imprint");
}
if (
!getConfig("legal.privacyPolicyText") &&
!getConfig("legal.privacyPolicyUrl")
) {
routes.disabled.routes.push("/privacy");
}
}
// prettier-ignore
const rules = [
// Disabled routes
{
condition: routes.disabled.contains(route),
path: "/",
},
// Authenticated state
{
condition: user && routes.unauthenticated.contains(route) && !getConfig("share.allowUnauthenticatedShares"),
path: "/upload",
},
// Unauthenticated state
{
condition: !user && !routes.public.contains(route) && !routes.unauthenticated.contains(route),
path: "/auth/signIn",
},
{
condition: !user && routes.account.contains(route),
path: "/upload",
},
// Admin privileges
{
condition: routes.admin.contains(route) && !user?.isAdmin,
path: "/upload",
},
// Home page
{
condition: (!getConfig("general.showHomePage") || user) && route == "/",
path: "/upload",
},
// Imprint redirect
{
condition: route == "/imprint" && !getConfig("legal.imprintText") && getConfig("legal.imprintUrl"),
path: getConfig("legal.imprintUrl"),
},
// Privacy redirect
{
condition: route == "/privacy" && !getConfig("legal.privacyPolicyText") && getConfig("legal.privacyPolicyUrl"),
path: getConfig("legal.privacyPolicyUrl"),
},
];
for (const rule of rules) {
if (rule.condition) {
let { path } = rule;
if (path == "/auth/signIn") {
path = path + "?redirect=" + encodeURIComponent(route);
}
return NextResponse.redirect(new URL(path, request.url));
}
}
}
// Helper class to check if a route matches a list of routes
class Routes {
// eslint-disable-next-line no-unused-vars
constructor(public routes: string[]) {}
contains(_route: string) {
for (const route of this.routes) {
if (new RegExp("^" + route.replace(/\*/g, ".*") + "$").test(_route))
return true;
}
return false;
}
}