diff --git a/app/[locale]/(site)/root/page.tsx b/app/[locale]/(site)/root/page.tsx
index a402aa5..705a5a4 100644
--- a/app/[locale]/(site)/root/page.tsx
+++ b/app/[locale]/(site)/root/page.tsx
@@ -1,324 +1,5 @@
-import {
- BarChart3,
- Boxes,
- FolderKanban,
- LockKeyhole,
- LogOut,
- Power,
- ShieldAlert,
- Users2,
-} from "lucide-react";
-import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
-import { MotionFade } from "@/components/motion-fade";
-import {
- clearAdminSessionCookie,
- getAdminLockState,
- isAdminAuthConfigured,
- isAdminAuthenticated,
- isPasswordValid,
- registerFailedAdminAttempt,
- resetAdminFailedAttempts,
- setAdminSessionCookie,
-} from "@/lib/admin-auth";
-import { getMaintenanceMode, setMaintenanceMode } from "@/lib/app-config";
-import { resolveLocale } from "@/lib/site-data";
-
-type RootPageProps = {
- params: {
- locale: string;
- };
- searchParams?: {
- error?: string;
- };
-};
-
-export const dynamic = "force-dynamic";
-
-export default async function RootPage({
- params: { locale },
- searchParams,
-}: RootPageProps) {
- const localeKey = resolveLocale(locale);
- const authConfigured = isAdminAuthConfigured();
- const basicConfigured = Boolean(
- process.env.ROOT_BASIC_AUTH_USER && process.env.ROOT_BASIC_AUTH_PASS,
- );
- const authenticated = isAdminAuthenticated();
- const lockState = getAdminLockState();
- const maintenanceEnabled = authenticated ? await getMaintenanceMode() : false;
-
- async function loginAction(formData: FormData) {
- "use server";
-
- const password = String(formData.get("password") ?? "");
- const currentLockState = getAdminLockState();
-
- if (currentLockState.locked) {
- redirect(`/${localeKey}/root?error=locked`);
- }
-
- if (!isAdminAuthConfigured() || !isPasswordValid(password)) {
- const failState = registerFailedAdminAttempt();
- if (failState.locked) {
- redirect(`/${localeKey}/root?error=locked`);
- }
-
- redirect(`/${localeKey}/root?error=invalid`);
- }
-
- resetAdminFailedAttempts();
- setAdminSessionCookie();
- redirect(`/${localeKey}/root`);
- }
-
- async function logoutAction() {
- "use server";
-
- clearAdminSessionCookie();
- redirect(`/${localeKey}/root`);
- }
-
- async function updateMaintenanceMode(formData: FormData) {
- "use server";
-
- if (!isAdminAuthenticated()) {
- redirect(`/${localeKey}/root`);
- }
-
- const nextValue = formData.get("enabled") === "true";
-
- await setMaintenanceMode(nextValue);
- revalidatePath(`/${localeKey}/root`);
- revalidatePath(`/${localeKey}/coming-soon`);
- redirect(`/${localeKey}/root`);
- }
-
- const copy =
- localeKey === "de"
- ? {
- title: "Root",
- subtitle: "Interner Bereich fuer Kennzahlen und Wartungssteuerung.",
- activeUsers: "Aktive Nutzer",
- projects: "Laufende Projekte",
- products: "Aktive Produkte",
- conversion: "Conversion",
- updates: "Letzte Updates",
- updateOne: "Kontaktformular wurde ueberarbeitet.",
- updateTwo: "Neue Produktseite fuer Growth Kit vorbereitet.",
- updateThree: "Portfolio-Daten fuer Q2 aktualisiert.",
- maintenanceTitle: "Wartungsmodus",
- maintenanceText:
- "Wenn aktiv, werden alle Seiten auf die Coming Soon Seite weitergeleitet.",
- maintenanceOn: "Aktiv",
- maintenanceOff: "Inaktiv",
- enableMaintenance: "Wartungsmodus aktivieren",
- disableMaintenance: "Wartungsmodus deaktivieren",
- loginTitle: "Root Login",
- loginText: "Nur autorisierte Nutzer duerfen diesen Bereich verwenden.",
- passwordLabel: "Passwort",
- loginButton: "Einloggen",
- invalidLogin: "Falsches Passwort.",
- lockedLogin: "Zu viele Fehlversuche. Bitte spaeter erneut versuchen.",
- configMissing: "ADMIN_PASSWORD und ADMIN_AUTH_SECRET fehlen in env.",
- basicAuthMissing:
- "ROOT_BASIC_AUTH_USER und ROOT_BASIC_AUTH_PASS fehlen in env.",
- logout: "Ausloggen",
- }
- : {
- title: "Root",
- subtitle: "Internal area for metrics and maintenance controls.",
- activeUsers: "Active users",
- projects: "Running projects",
- products: "Active products",
- conversion: "Conversion",
- updates: "Latest updates",
- updateOne: "Contact form layout updated.",
- updateTwo: "New Growth Kit product page prepared.",
- updateThree: "Portfolio data updated for Q2.",
- maintenanceTitle: "Maintenance mode",
- maintenanceText:
- "When enabled, all site routes are redirected to the coming soon page.",
- maintenanceOn: "Enabled",
- maintenanceOff: "Disabled",
- enableMaintenance: "Enable maintenance mode",
- disableMaintenance: "Disable maintenance mode",
- loginTitle: "Root login",
- loginText: "Only authorized users can access this area.",
- passwordLabel: "Password",
- loginButton: "Sign in",
- invalidLogin: "Invalid password.",
- lockedLogin: "Too many failed attempts. Please try again later.",
- configMissing: "ADMIN_PASSWORD and ADMIN_AUTH_SECRET are missing in env.",
- basicAuthMissing:
- "ROOT_BASIC_AUTH_USER and ROOT_BASIC_AUTH_PASS are missing in env.",
- logout: "Sign out",
- };
-
- if (!authenticated) {
- return (
-
-
-
-
-
- {copy.loginTitle}
-
- {copy.loginText}
-
- {!authConfigured ? (
-
- {copy.configMissing}
-
- ) : null}
-
- {!basicConfigured ? (
-
- {copy.basicAuthMissing}
-
- ) : null}
-
- {searchParams?.error === "invalid" ? (
-
- {copy.invalidLogin}
-
- ) : null}
-
- {searchParams?.error === "locked" || lockState.locked ? (
-
- {copy.lockedLogin}
-
- ) : null}
-
-
-
-
-
- );
- }
-
- const stats = [
- { label: copy.activeUsers, value: "1,280", icon: Users2 },
- { label: copy.projects, value: "24", icon: FolderKanban },
- { label: copy.products, value: "8", icon: Boxes },
- { label: copy.conversion, value: "4.8%", icon: BarChart3 },
- ];
-
- return (
-
-
-
-
-
- {copy.title}
-
-
-
-
- {copy.subtitle}
-
-
-
-
-
-
-
-
-
-
- {copy.maintenanceTitle}
-
-
- {copy.maintenanceText}
-
-
-
- {maintenanceEnabled ? copy.maintenanceOn : copy.maintenanceOff}
-
-
-
-
-
-
-
-
- {stats.map((item, index) => {
- const Icon = item.icon;
- return (
-
-
-
- {item.label}
-
- {item.value}
-
-
-
- );
- })}
-
-
-
-
- {copy.updates}
-
- {[copy.updateOne, copy.updateTwo, copy.updateThree].map((item) => (
- -
- {item}
-
- ))}
-
-
-
-
- );
+export default function LocalizedRootRedirectPage() {
+ redirect("/root");
}
diff --git a/app/[locale]/coming-soon/page.tsx b/app/[locale]/coming-soon/page.tsx
index f34d062..23ef466 100644
--- a/app/[locale]/coming-soon/page.tsx
+++ b/app/[locale]/coming-soon/page.tsx
@@ -64,7 +64,7 @@ export default function ComingSoonPage({ params: { locale } }: ComingSoonPagePro
diff --git a/app/root/page.tsx b/app/root/page.tsx
new file mode 100644
index 0000000..146bca3
--- /dev/null
+++ b/app/root/page.tsx
@@ -0,0 +1,320 @@
+import {
+ BarChart3,
+ Boxes,
+ FolderKanban,
+ LockKeyhole,
+ LogOut,
+ Power,
+ ShieldAlert,
+ Users2,
+} from "lucide-react";
+import { revalidatePath } from "next/cache";
+import { cookies } from "next/headers";
+import { redirect } from "next/navigation";
+
+import { MotionFade } from "@/components/motion-fade";
+import {
+ clearAdminSessionCookie,
+ getAdminLockState,
+ isAdminAuthConfigured,
+ isAdminAuthenticated,
+ isPasswordValid,
+ registerFailedAdminAttempt,
+ resetAdminFailedAttempts,
+ setAdminSessionCookie,
+} from "@/lib/admin-auth";
+import { getMaintenanceMode, setMaintenanceMode } from "@/lib/app-config";
+import { resolveLocale } from "@/lib/site-data";
+
+type RootPageProps = {
+ searchParams?: {
+ error?: string;
+ };
+};
+
+export const dynamic = "force-dynamic";
+
+export default async function RootPage({ searchParams }: RootPageProps) {
+ const requestLocale = cookies().get("NEXT_LOCALE")?.value;
+ const localeKey = resolveLocale(requestLocale ?? "de");
+ const authConfigured = isAdminAuthConfigured();
+ const basicConfigured = Boolean(
+ process.env.ROOT_BASIC_AUTH_USER && process.env.ROOT_BASIC_AUTH_PASS,
+ );
+ const authenticated = isAdminAuthenticated();
+ const lockState = getAdminLockState();
+ const maintenanceEnabled = authenticated ? await getMaintenanceMode() : false;
+
+ async function loginAction(formData: FormData) {
+ "use server";
+
+ const password = String(formData.get("password") ?? "");
+ const currentLockState = getAdminLockState();
+
+ if (currentLockState.locked) {
+ redirect("/root?error=locked");
+ }
+
+ if (!isAdminAuthConfigured() || !isPasswordValid(password)) {
+ const failState = registerFailedAdminAttempt();
+ if (failState.locked) {
+ redirect("/root?error=locked");
+ }
+
+ redirect("/root?error=invalid");
+ }
+
+ resetAdminFailedAttempts();
+ setAdminSessionCookie();
+ redirect("/root");
+ }
+
+ async function logoutAction() {
+ "use server";
+
+ clearAdminSessionCookie();
+ redirect("/root");
+ }
+
+ async function updateMaintenanceMode(formData: FormData) {
+ "use server";
+
+ if (!isAdminAuthenticated()) {
+ redirect("/root");
+ }
+
+ const nextValue = formData.get("enabled") === "true";
+
+ await setMaintenanceMode(nextValue);
+ revalidatePath("/root");
+ revalidatePath(`/${localeKey}/coming-soon`);
+ redirect("/root");
+ }
+
+ const copy =
+ resolveLocale(requestLocale ?? localeKey) === "de"
+ ? {
+ title: "Root",
+ subtitle: "Interner Bereich fuer Kennzahlen und Wartungssteuerung.",
+ activeUsers: "Aktive Nutzer",
+ projects: "Laufende Projekte",
+ products: "Aktive Produkte",
+ conversion: "Conversion",
+ updates: "Letzte Updates",
+ updateOne: "Kontaktformular wurde ueberarbeitet.",
+ updateTwo: "Neue Produktseite fuer Growth Kit vorbereitet.",
+ updateThree: "Portfolio-Daten fuer Q2 aktualisiert.",
+ maintenanceTitle: "Wartungsmodus",
+ maintenanceText:
+ "Wenn aktiv, werden alle Seiten auf die Coming Soon Seite weitergeleitet.",
+ maintenanceOn: "Aktiv",
+ maintenanceOff: "Inaktiv",
+ enableMaintenance: "Wartungsmodus aktivieren",
+ disableMaintenance: "Wartungsmodus deaktivieren",
+ loginTitle: "Root Login",
+ loginText: "Nur autorisierte Nutzer duerfen diesen Bereich verwenden.",
+ passwordLabel: "Passwort",
+ loginButton: "Einloggen",
+ invalidLogin: "Falsches Passwort.",
+ lockedLogin: "Zu viele Fehlversuche. Bitte spaeter erneut versuchen.",
+ configMissing: "ADMIN_PASSWORD und ADMIN_AUTH_SECRET fehlen in env.",
+ basicAuthMissing:
+ "ROOT_BASIC_AUTH_USER und ROOT_BASIC_AUTH_PASS fehlen in env.",
+ logout: "Ausloggen",
+ }
+ : {
+ title: "Root",
+ subtitle: "Internal area for metrics and maintenance controls.",
+ activeUsers: "Active users",
+ projects: "Running projects",
+ products: "Active products",
+ conversion: "Conversion",
+ updates: "Latest updates",
+ updateOne: "Contact form layout updated.",
+ updateTwo: "New Growth Kit product page prepared.",
+ updateThree: "Portfolio data updated for Q2.",
+ maintenanceTitle: "Maintenance mode",
+ maintenanceText:
+ "When enabled, all site routes are redirected to the coming soon page.",
+ maintenanceOn: "Enabled",
+ maintenanceOff: "Disabled",
+ enableMaintenance: "Enable maintenance mode",
+ disableMaintenance: "Disable maintenance mode",
+ loginTitle: "Root login",
+ loginText: "Only authorized users can access this area.",
+ passwordLabel: "Password",
+ loginButton: "Sign in",
+ invalidLogin: "Invalid password.",
+ lockedLogin: "Too many failed attempts. Please try again later.",
+ configMissing: "ADMIN_PASSWORD and ADMIN_AUTH_SECRET are missing in env.",
+ basicAuthMissing:
+ "ROOT_BASIC_AUTH_USER and ROOT_BASIC_AUTH_PASS are missing in env.",
+ logout: "Sign out",
+ };
+
+ if (!authenticated) {
+ return (
+
+
+
+
+
+ {copy.loginTitle}
+
+ {copy.loginText}
+
+ {!authConfigured ? (
+
+ {copy.configMissing}
+
+ ) : null}
+
+ {!basicConfigured ? (
+
+ {copy.basicAuthMissing}
+
+ ) : null}
+
+ {searchParams?.error === "invalid" ? (
+
+ {copy.invalidLogin}
+
+ ) : null}
+
+ {searchParams?.error === "locked" || lockState.locked ? (
+
+ {copy.lockedLogin}
+
+ ) : null}
+
+
+
+
+
+ );
+ }
+
+ const stats = [
+ { label: copy.activeUsers, value: "1,280", icon: Users2 },
+ { label: copy.projects, value: "24", icon: FolderKanban },
+ { label: copy.products, value: "8", icon: Boxes },
+ { label: copy.conversion, value: "4.8%", icon: BarChart3 },
+ ];
+
+ return (
+
+
+
+
+
+ {copy.title}
+
+
+
+
+ {copy.subtitle}
+
+
+
+
+
+
+
+
+
+
+ {copy.maintenanceTitle}
+
+
+ {copy.maintenanceText}
+
+
+
+ {maintenanceEnabled ? copy.maintenanceOn : copy.maintenanceOff}
+
+
+
+
+
+
+
+
+ {stats.map((item, index) => {
+ const Icon = item.icon;
+ return (
+
+
+
+ {item.label}
+
+ {item.value}
+
+
+
+ );
+ })}
+
+
+
+
+ {copy.updates}
+
+ {[copy.updateOne, copy.updateTwo, copy.updateThree].map((item) => (
+ -
+ {item}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/components/footer.tsx b/components/footer.tsx
index 7f9419e..c476379 100644
--- a/components/footer.tsx
+++ b/components/footer.tsx
@@ -23,7 +23,7 @@ export function Footer() {
item.key === "root" ? (
{tNav(item.key)}
diff --git a/components/navbar.tsx b/components/navbar.tsx
index 7b240a4..d9f74a5 100644
--- a/components/navbar.tsx
+++ b/components/navbar.tsx
@@ -52,7 +52,7 @@ export function Navbar() {
item.key === "root" ? (
{t(item.key)}
@@ -97,7 +97,7 @@ export function Navbar() {
item.key === "root" ? (
setIsOpen(false)}
>
diff --git a/middleware.ts b/middleware.ts
index 962ca17..15fbc9d 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -115,10 +115,10 @@ export default async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const locale = getLocaleFromPath(pathname);
const isSuperAdmin = await isSuperAdminSessionValid(request);
+ const isRootBaseRoute = pathname === "/root" || pathname.startsWith("/root/");
const isRootRoute =
- pathname === "/root" ||
- pathname.startsWith("/root/") ||
+ isRootBaseRoute ||
pathname === `/${locale}/root` ||
pathname.startsWith(`/${locale}/root/`);
const isComingSoonRoute =
@@ -136,6 +136,10 @@ export default async function middleware(request: NextRequest) {
});
}
+ if (isRootBaseRoute) {
+ return NextResponse.next();
+ }
+
if (!isRootRoute && !isComingSoonRoute && !isSuperAdmin) {
const maintenanceModeEnabled = await isMaintenanceModeEnabled(request);