This commit is contained in:
+2
-2
@@ -5,5 +5,5 @@ NEXT_PUBLIC_ADMIN_URL="https://root.mohfarawati.de"
|
||||
NEXT_TELEMETRY_DISABLED="1"
|
||||
ADMIN_PASSWORD="change-me"
|
||||
ADMIN_AUTH_SECRET="replace-with-a-long-random-secret"
|
||||
ROOT_BASIC_AUTH_USER="change-me"
|
||||
ROOT_BASIC_AUTH_PASS="change-me"
|
||||
ADMIN_BASIC_AUTH_USER="change-me"
|
||||
ADMIN_BASIC_AUTH_PASS="change-me"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getMaintenanceMode } from "@/lib/app-config";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
@@ -27,7 +27,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootMaintenancePage() {
|
||||
export default async function AdminMaintenancePage() {
|
||||
const authenticated = await isAdminAuthenticated();
|
||||
|
||||
if (!authenticated) {
|
||||
@@ -43,7 +43,7 @@ export default async function RootMaintenancePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="maintenance"
|
||||
logoutAction={logoutAction}
|
||||
@@ -63,6 +63,6 @@ export default async function RootMaintenancePage() {
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</MotionFade>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MarqueeSettingsForm } from "@/components/root/marquee-settings-form";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { MarqueeSettingsForm } from "@/components/admin/marquee-settings-form";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getMarqueeSettings } from "@/lib/app-config";
|
||||
@@ -25,7 +25,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootMarqueePage() {
|
||||
export default async function AdminMarqueePage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export default async function RootMarqueePage() {
|
||||
const marqueeSettings = await getMarqueeSettings();
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="marquee"
|
||||
logoutAction={logoutAction}
|
||||
@@ -55,6 +55,6 @@ export default async function RootMarqueePage() {
|
||||
/>
|
||||
</MotionFade>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MediaLibraryManager } from "@/components/root/media-library-manager";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { MediaLibraryManager } from "@/components/admin/media-library-manager";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getAdminMediaAssets } from "@/lib/media";
|
||||
|
||||
@@ -20,7 +20,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootMediaPage() {
|
||||
export default async function AdminMediaPage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export default async function RootMediaPage() {
|
||||
const mediaAssets = await getAdminMediaAssets();
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="media"
|
||||
logoutAction={logoutAction}
|
||||
@@ -45,6 +45,6 @@ export default async function RootMediaPage() {
|
||||
<div className="space-y-6">
|
||||
<MediaLibraryManager mediaAssets={mediaAssets} />
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
|
||||
import { Container } from "@/components/layout/container";
|
||||
import { StatsCard } from "@/components/dashboard/stats-card";
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card";
|
||||
@@ -24,7 +24,7 @@ import { getMaintenanceMode } from "@/lib/app-config";
|
||||
import { getAdminMediaAssets } from "@/lib/media";
|
||||
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
||||
|
||||
type RootPageProps = {
|
||||
type AdminPageProps = {
|
||||
searchParams?: Promise<{
|
||||
error?: string;
|
||||
}>;
|
||||
@@ -43,14 +43,14 @@ const copy = {
|
||||
uiKit: "UI Kit",
|
||||
logout: "Ausloggen",
|
||||
backToSite: "Zur Website",
|
||||
loginTitle: "Root Login",
|
||||
loginTitle: "Admin 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.",
|
||||
basicAuthMissing: "ADMIN_BASIC_AUTH_USER und ADMIN_BASIC_AUTH_PASS fehlen in env.",
|
||||
maintenanceOn: "Aktiv",
|
||||
maintenanceOff: "Inaktiv",
|
||||
portfolioOnline: "Portfolio online",
|
||||
@@ -60,11 +60,11 @@ const copy = {
|
||||
activeCategories: "Active Categories",
|
||||
};
|
||||
|
||||
export default async function RootPage({ searchParams }: RootPageProps) {
|
||||
export default async function AdminPage({ searchParams }: AdminPageProps) {
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const authConfigured = isAdminAuthConfigured();
|
||||
const basicConfigured = Boolean(
|
||||
process.env.ROOT_BASIC_AUTH_USER && process.env.ROOT_BASIC_AUTH_PASS,
|
||||
process.env.ADMIN_BASIC_AUTH_USER && process.env.ADMIN_BASIC_AUTH_PASS,
|
||||
);
|
||||
const authenticated = await isAdminAuthenticated();
|
||||
const lockState = await getAdminLockState();
|
||||
@@ -182,7 +182,7 @@ export default async function RootPage({ searchParams }: RootPageProps) {
|
||||
const imageCount = mediaAssets.filter((asset) => asset.kind === "IMAGE").length;
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="overview"
|
||||
logoutAction={logoutAction}
|
||||
@@ -238,6 +238,6 @@ export default async function RootPage({ searchParams }: RootPageProps) {
|
||||
</MotionFade>
|
||||
</section>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { PortfolioCategoriesManager } from "@/components/root/portfolio-categories-manager";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { PortfolioCategoriesManager } from "@/components/admin/portfolio-categories-manager";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getAdminPortfolioCategories } from "@/lib/portfolio";
|
||||
|
||||
@@ -22,7 +22,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootPortfolioCategoriesPage() {
|
||||
export default async function AdminPortfolioCategoriesPage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export default async function RootPortfolioCategoriesPage() {
|
||||
const assignedProjects = categories.reduce((sum, category) => sum + category.projectCount, 0);
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="portfolio"
|
||||
portfolioChild="categories"
|
||||
@@ -56,6 +56,6 @@ export default async function RootPortfolioCategoriesPage() {
|
||||
removeCategoryAction={deleteCategoryAction}
|
||||
/>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function RootPortfolioMediaRedirectPage() {
|
||||
export default function AdminPortfolioMediaRedirectPage() {
|
||||
redirect("/media");
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { PortfolioProjectsOverview } from "@/components/root/portfolio-projects-overview";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { PortfolioProjectsOverview } from "@/components/admin/portfolio-projects-overview";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
||||
|
||||
@@ -20,7 +20,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
type RootPortfolioPageProps = {
|
||||
type AdminPortfolioPageProps = {
|
||||
searchParams?: Promise<{
|
||||
category?: string;
|
||||
status?: "all" | "draft" | "published";
|
||||
@@ -29,7 +29,7 @@ type RootPortfolioPageProps = {
|
||||
}>;
|
||||
};
|
||||
|
||||
export default async function RootPortfolioPage({ searchParams }: RootPortfolioPageProps) {
|
||||
export default async function AdminPortfolioPage({ searchParams }: AdminPortfolioPageProps) {
|
||||
const resolvedSearchParams = await searchParams;
|
||||
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
@@ -58,7 +58,7 @@ export default async function RootPortfolioPage({ searchParams }: RootPortfolioP
|
||||
]);
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="portfolio"
|
||||
portfolioChild="overview"
|
||||
@@ -74,6 +74,6 @@ export default async function RootPortfolioPage({ searchParams }: RootPortfolioP
|
||||
selectedStatus={selectedStatus}
|
||||
/>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
+7
-7
@@ -1,8 +1,8 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { PortfolioProjectForm } from "@/components/root/portfolio-project-form";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { PortfolioProjectForm } from "@/components/admin/portfolio-project-form";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardContent } from "@/components/ui/card";
|
||||
@@ -43,15 +43,15 @@ const copy = {
|
||||
deleteProject: "Projekt loeschen",
|
||||
};
|
||||
|
||||
type RootPortfolioProjectPageProps = {
|
||||
type AdminPortfolioProjectPageProps = {
|
||||
params: Promise<{
|
||||
id: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export default async function RootPortfolioProjectPage({
|
||||
export default async function AdminPortfolioProjectPage({
|
||||
params,
|
||||
}: RootPortfolioProjectPageProps) {
|
||||
}: AdminPortfolioProjectPageProps) {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export default async function RootPortfolioProjectPage({
|
||||
}
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="portfolio"
|
||||
portfolioChild="projects"
|
||||
@@ -132,6 +132,6 @@ export default async function RootPortfolioProjectPage({
|
||||
</AppCard>
|
||||
</MotionFade>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { PortfolioProjectForm } from "@/components/root/portfolio-project-form";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { PortfolioProjectForm } from "@/components/admin/portfolio-project-form";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getMediaOptions } from "@/lib/media";
|
||||
import { getActivePortfolioCategories } from "@/lib/portfolio";
|
||||
@@ -24,7 +24,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootNewPortfolioProjectPage() {
|
||||
export default async function AdminNewPortfolioProjectPage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export default async function RootNewPortfolioProjectPage() {
|
||||
]);
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="portfolio"
|
||||
portfolioChild="new-project"
|
||||
@@ -61,6 +61,6 @@ export default async function RootNewPortfolioProjectPage() {
|
||||
/>
|
||||
</MotionFade>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { PortfolioProjectsOverview } from "@/components/root/portfolio-projects-overview";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { PortfolioProjectsOverview } from "@/components/admin/portfolio-projects-overview";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
||||
|
||||
@@ -20,16 +20,16 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
type RootPortfolioProjectsPageProps = {
|
||||
type AdminPortfolioProjectsPageProps = {
|
||||
searchParams?: Promise<{
|
||||
category?: string;
|
||||
status?: "all" | "draft" | "published";
|
||||
}>;
|
||||
};
|
||||
|
||||
export default async function RootPortfolioProjectsPage({
|
||||
export default async function AdminPortfolioProjectsPage({
|
||||
searchParams,
|
||||
}: RootPortfolioProjectsPageProps) {
|
||||
}: AdminPortfolioProjectsPageProps) {
|
||||
const resolvedSearchParams = await searchParams;
|
||||
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
@@ -58,7 +58,7 @@ export default async function RootPortfolioProjectsPage({
|
||||
]);
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="portfolio"
|
||||
portfolioChild="projects"
|
||||
@@ -74,6 +74,6 @@ export default async function RootPortfolioProjectsPage({
|
||||
selectedStatus={selectedStatus}
|
||||
/>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import { MediaKind } from "@prisma/client";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { SiteSettingsForm } from "@/components/root/site-settings-form";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { SiteSettingsForm } from "@/components/admin/site-settings-form";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import {
|
||||
getSiteSettings,
|
||||
@@ -29,7 +29,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootSiteSettingsPage() {
|
||||
export default async function AdminSiteSettingsPage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export default async function RootSiteSettingsPage() {
|
||||
]);
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="site-settings"
|
||||
logoutAction={logoutAction}
|
||||
@@ -65,6 +65,6 @@ export default async function RootSiteSettingsPage() {
|
||||
/>
|
||||
</MotionFade>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { ContactProtectionForm } from "@/components/root/contact-protection-form";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { ContactProtectionForm } from "@/components/admin/contact-protection-form";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getContactProtectionFormValues } from "@/lib/app-config";
|
||||
|
||||
@@ -25,7 +25,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootSMTPProtectionPage() {
|
||||
export default async function AdminSMTPProtectionPage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export default async function RootSMTPProtectionPage() {
|
||||
const settings = await getContactProtectionFormValues();
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="smtp"
|
||||
smtpChild="contact-protection"
|
||||
@@ -56,6 +56,6 @@ export default async function RootSMTPProtectionPage() {
|
||||
/>
|
||||
</MotionFade>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { SMTPSettingsForm } from "@/components/root/smtp-settings-form";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { SMTPSettingsForm } from "@/components/admin/smtp-settings-form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { getMailSettingsFormValues } from "@/lib/app-config";
|
||||
@@ -26,7 +26,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootSMTPPage() {
|
||||
export default async function AdminSMTPPage() {
|
||||
if (!(await isAdminAuthenticated())) {
|
||||
redirect("/");
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export default async function RootSMTPPage() {
|
||||
const mailSettings = await getMailSettingsFormValues();
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="smtp"
|
||||
smtpChild="settings"
|
||||
@@ -64,6 +64,6 @@ export default async function RootSMTPPage() {
|
||||
/>
|
||||
</MotionFade>
|
||||
</div>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
|
||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||
import { UiKitShowcase } from "@/components/ui/ui-kit-showcase";
|
||||
|
||||
@@ -9,7 +9,7 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
const copy = {
|
||||
title: "UI Kit",
|
||||
subtitle: "Globale Referenz fuer das visuelle System im Root Bereich.",
|
||||
subtitle: "Globale Referenz fuer das visuelle System im Admin Bereich.",
|
||||
maintenance: "Wartungsmodus",
|
||||
overview: "Uebersicht",
|
||||
uiKit: "UI Kit",
|
||||
@@ -20,7 +20,7 @@ const copy = {
|
||||
backToSite: "Zur Website",
|
||||
};
|
||||
|
||||
export default async function RootUiKitPage() {
|
||||
export default async function AdminUiKitPage() {
|
||||
const authenticated = await isAdminAuthenticated();
|
||||
|
||||
if (!authenticated) {
|
||||
@@ -35,7 +35,7 @@ export default async function RootUiKitPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<RootDashboardShell
|
||||
<AdminDashboardShell
|
||||
copy={copy}
|
||||
active="ui-kit"
|
||||
logoutAction={logoutAction}
|
||||
@@ -45,6 +45,6 @@ export default async function RootUiKitPage() {
|
||||
<MotionFade delay={0.1}>
|
||||
<UiKitShowcase localeKey="de" />
|
||||
</MotionFade>
|
||||
</RootDashboardShell>
|
||||
</AdminDashboardShell>
|
||||
);
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export { metadata } from "../root/layout";
|
||||
export { default } from "../root/layout";
|
||||
export { metadata } from "../_admin/layout";
|
||||
export { default } from "../_admin/layout";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { dynamic } from "../../root/maintenance/page";
|
||||
export { default } from "../../root/maintenance/page";
|
||||
export { dynamic } from "../../_admin/maintenance/page";
|
||||
export { default } from "../../_admin/maintenance/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../root/marquee/page";
|
||||
export { default } from "../../_admin/marquee/page";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { dynamic } from "../../root/media/page";
|
||||
export { default } from "../../root/media/page";
|
||||
export { dynamic } from "../../_admin/media/page";
|
||||
export { default } from "../../_admin/media/page";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { dynamic } from "../root/page";
|
||||
export { default } from "../root/page";
|
||||
export { dynamic } from "../_admin/page";
|
||||
export { default } from "../_admin/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../../root/portfolio/categories/page";
|
||||
export { default } from "../../../_admin/portfolio/categories/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../../root/portfolio/media/page";
|
||||
export { default } from "../../../_admin/portfolio/media/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../root/portfolio/page";
|
||||
export { default } from "../../_admin/portfolio/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../../../root/portfolio/projects/[id]/page";
|
||||
export { default } from "../../../../_admin/portfolio/projects/[id]/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../../../root/portfolio/projects/new/page";
|
||||
export { default } from "../../../../_admin/portfolio/projects/new/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../../root/portfolio/projects/page";
|
||||
export { default } from "../../../_admin/portfolio/projects/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../root/site-settings/page";
|
||||
export { default } from "../../_admin/site-settings/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../../root/smtp/contact-protection/page";
|
||||
export { default } from "../../../_admin/smtp/contact-protection/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../root/smtp/page";
|
||||
export { default } from "../../_admin/smtp/page";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "../../root/ui-kit/page";
|
||||
export { default } from "../../_admin/ui-kit/page";
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { unstable_noStore as noStore } from "next/cache";
|
||||
import localFont from "next/font/local";
|
||||
import Script from "next/script";
|
||||
import { getLocale } from "next-intl/server";
|
||||
import { QueryToastBridge } from "@/components/root/query-toast-bridge";
|
||||
import { QueryToastBridge } from "@/components/admin/query-toast-bridge";
|
||||
import { SoundProvider } from "@/components/sound-provider";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
|
||||
+9
-9
@@ -15,17 +15,17 @@ import Link from "next/link";
|
||||
|
||||
import { DashboardLayout } from "@/components/dashboard/dashboard-layout";
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { SidebarMaintenanceControl } from "@/components/root/sidebar-maintenance-control";
|
||||
import { SidebarMaintenanceControl } from "@/components/admin/sidebar-maintenance-control";
|
||||
import { SoundToggle } from "@/components/sound-toggle";
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { buildSiteUrl } from "@/lib/admin-routing";
|
||||
import { getMaintenanceMode, getSiteSettingsMediaBindings } from "@/lib/app-config";
|
||||
import { getRootNavigation } from "@/lib/root-navigation";
|
||||
import { getAdminNavigation } from "@/lib/admin-navigation";
|
||||
|
||||
import { updateMaintenanceModeAction } from "@/app/root/maintenance/actions";
|
||||
import { updateMaintenanceModeAction } from "@/app/_admin/maintenance/actions";
|
||||
|
||||
type RootDashboardCopy = {
|
||||
type AdminDashboardCopy = {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
overview: string;
|
||||
@@ -41,8 +41,8 @@ type RootDashboardCopy = {
|
||||
backToSite: string;
|
||||
};
|
||||
|
||||
type RootDashboardShellProps = {
|
||||
copy: RootDashboardCopy;
|
||||
type AdminDashboardShellProps = {
|
||||
copy: AdminDashboardCopy;
|
||||
active: "overview" | "maintenance" | "ui-kit" | "portfolio" | "media" | "site-settings" | "smtp" | "marquee";
|
||||
smtpChild?: "settings" | "contact-protection";
|
||||
portfolioChild?: "overview" | "projects" | "new-project" | "categories";
|
||||
@@ -55,7 +55,7 @@ type RootDashboardShellProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export async function RootDashboardShell({
|
||||
export async function AdminDashboardShell({
|
||||
copy,
|
||||
active,
|
||||
smtpChild,
|
||||
@@ -67,12 +67,12 @@ export async function RootDashboardShell({
|
||||
sidebarTopContent,
|
||||
toolbar,
|
||||
children,
|
||||
}: RootDashboardShellProps) {
|
||||
}: AdminDashboardShellProps) {
|
||||
const [mediaBindings, maintenanceEnabled] = await Promise.all([
|
||||
getSiteSettingsMediaBindings(),
|
||||
getMaintenanceMode(),
|
||||
]);
|
||||
const sidebarItems = getRootNavigation(copy, active, smtpChild, portfolioChild);
|
||||
const sidebarItems = getAdminNavigation(copy, active, smtpChild, portfolioChild);
|
||||
const normalizedSidebarItems = sidebarItems.filter(
|
||||
(item) =>
|
||||
item.href !== "/maintenance" &&
|
||||
+1
-1
@@ -28,7 +28,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { createMediaAssetAction, deleteMediaAssetAction } from "@/app/root/media/actions";
|
||||
import { createMediaAssetAction, deleteMediaAssetAction } from "@/app/_admin/media/actions";
|
||||
|
||||
const copy = {
|
||||
addMedia: "Upload Media File",
|
||||
+1
-1
@@ -13,7 +13,7 @@ import {
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
|
||||
import type { deleteCategoryAction, upsertCategoryAction } from "@/app/root/portfolio/actions";
|
||||
import type { deleteCategoryAction, upsertCategoryAction } from "@/app/_admin/portfolio/actions";
|
||||
import { StatsCard } from "@/components/dashboard/stats-card";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { FolderKanban, MoreVertical, Trash2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
import { deleteProjectAction } from "@/app/root/portfolio/actions";
|
||||
import { deleteProjectAction } from "@/app/_admin/portfolio/actions";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
+1
-1
@@ -15,7 +15,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { type ReactNode, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { MediaFieldPicker, type MediaFieldState } from "@/components/root/media-field-picker";
|
||||
import { MediaFieldPicker, type MediaFieldState } from "@/components/admin/media-field-picker";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
+1
-1
@@ -3,7 +3,7 @@ import Link from "next/link";
|
||||
|
||||
import { StatsCard } from "@/components/dashboard/stats-card";
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { PortfolioProjectActions } from "@/components/root/portfolio-project-actions";
|
||||
import { PortfolioProjectActions } from "@/components/admin/portfolio-project-actions";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -11,18 +11,18 @@ import {
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@/components/ui/sheet";
|
||||
import type { RootNavItem } from "@/lib/root-navigation";
|
||||
import type { AdminNavItem } from "@/lib/admin-navigation";
|
||||
|
||||
type DashboardLayoutProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
icon?: LucideIcon;
|
||||
items: RootNavItem[];
|
||||
items: AdminNavItem[];
|
||||
sidebarIconSrc?: string;
|
||||
sidebarBrandHref?: string;
|
||||
sidebarBrandHoverLabel?: string;
|
||||
sidebarTop?: ReactNode;
|
||||
sidebarFooterItems?: RootNavItem[];
|
||||
sidebarFooterItems?: AdminNavItem[];
|
||||
sidebarFooter?: ReactNode;
|
||||
headerActions?: ReactNode;
|
||||
children: ReactNode;
|
||||
|
||||
@@ -66,7 +66,7 @@ export function AppSidebar({
|
||||
<AppCard className="overflow-hidden bg-sidebar text-sidebar-foreground shadow-sidebar">
|
||||
<CardHeader className="pb-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-sidebar-foreground/56">
|
||||
Root Access
|
||||
Admin Access
|
||||
</p>
|
||||
<CardTitle className="text-base font-semibold">{title}</CardTitle>
|
||||
<p className="text-sm leading-6 text-sidebar-foreground/72">{description}</p>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function SiteFooter({ isAdmin = false }: SiteFooterProps) {
|
||||
))}
|
||||
{isAdmin ? (
|
||||
<a href={buildAdminUrl()} className="text-muted-foreground hover:text-foreground">
|
||||
{tNav("root")}
|
||||
{tNav("admin")}
|
||||
</a>
|
||||
) : null}
|
||||
</nav>
|
||||
|
||||
@@ -315,7 +315,7 @@ export function SiteHeader({
|
||||
/>
|
||||
{isAdmin ? (
|
||||
<Button asChild variant="ghost" size="icon" className={desktopControlButtonClassName}>
|
||||
<Link href={buildAdminUrl()} aria-label={t("root")}>
|
||||
<Link href={buildAdminUrl()} aria-label={t("admin")}>
|
||||
<LayoutDashboard className="h-4 w-4 transition-transform duration-300 ease-out hover:scale-110" />
|
||||
</Link>
|
||||
</Button>
|
||||
@@ -465,7 +465,7 @@ export function SiteHeader({
|
||||
/>
|
||||
{isAdmin ? (
|
||||
<Button asChild variant="ghost" size="icon" className={mobileControlButtonClassName}>
|
||||
<Link href={buildAdminUrl()} aria-label={t("root")} onClick={() => setIsOpen(false)}>
|
||||
<Link href={buildAdminUrl()} aria-label={t("admin")} onClick={() => setIsOpen(false)}>
|
||||
<LayoutDashboard className="h-4 w-4 transition-transform duration-300 ease-out hover:scale-110" />
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ services:
|
||||
DATABASE_URL: postgresql://postgres:postgres@db:5432/moh_sass?schema=public
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-change-me}
|
||||
ADMIN_AUTH_SECRET: ${ADMIN_AUTH_SECRET:-change-me-long-secret}
|
||||
ROOT_BASIC_AUTH_USER: ${ROOT_BASIC_AUTH_USER:-root}
|
||||
ROOT_BASIC_AUTH_PASS: ${ROOT_BASIC_AUTH_PASS:-change-me-root}
|
||||
ADMIN_BASIC_AUTH_USER: ${ADMIN_BASIC_AUTH_USER:-root}
|
||||
ADMIN_BASIC_AUTH_PASS: ${ADMIN_BASIC_AUTH_PASS:-change-me-root}
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -15,6 +15,75 @@ function getPassword(): string {
|
||||
return process.env.ADMIN_PASSWORD ?? "";
|
||||
}
|
||||
|
||||
function parseHostname(value: string | undefined): string | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const trimmed = value.trim().toLowerCase();
|
||||
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(trimmed).hostname.toLowerCase();
|
||||
} catch {
|
||||
return trimmed.replace(/^https?:\/\//, "").split("/")[0]?.replace(/:\d+$/, "") || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function isCookieDomainCandidate(hostname: string | undefined): hostname is string {
|
||||
return Boolean(
|
||||
hostname &&
|
||||
hostname !== "localhost" &&
|
||||
!hostname.endsWith(".localhost") &&
|
||||
!/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) &&
|
||||
!hostname.includes(":"),
|
||||
);
|
||||
}
|
||||
|
||||
function getSharedCookieHostname(hostnames: string[]): string | undefined {
|
||||
const validHostnames = hostnames.filter(isCookieDomainCandidate);
|
||||
|
||||
if (validHostnames.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (validHostnames.length === 1) {
|
||||
return validHostnames[0];
|
||||
}
|
||||
|
||||
const reversedSegments = validHostnames.map((hostname) => hostname.split(".").reverse());
|
||||
const sharedSegments: string[] = [];
|
||||
|
||||
for (let index = 0; index < reversedSegments[0].length; index += 1) {
|
||||
const segment = reversedSegments[0][index];
|
||||
|
||||
if (!segment || reversedSegments.some((parts) => parts[index] !== segment)) {
|
||||
break;
|
||||
}
|
||||
|
||||
sharedSegments.push(segment);
|
||||
}
|
||||
|
||||
if (sharedSegments.length < 2) {
|
||||
return validHostnames[0];
|
||||
}
|
||||
|
||||
return sharedSegments.reverse().join(".");
|
||||
}
|
||||
|
||||
function getAdminCookieDomain(): string | undefined {
|
||||
const hostname = getSharedCookieHostname([
|
||||
parseHostname(process.env.NEXT_PUBLIC_SITE_URL),
|
||||
parseHostname(process.env.NEXT_PUBLIC_ADMIN_URL),
|
||||
parseHostname(process.env.ADMIN_HOST),
|
||||
].filter((value): value is string => Boolean(value)));
|
||||
|
||||
return hostname ? `.${hostname}` : undefined;
|
||||
}
|
||||
|
||||
function signValue(value: string): string {
|
||||
return createHmac("sha256", getSecret()).update(value).digest("hex");
|
||||
}
|
||||
@@ -71,6 +140,7 @@ export async function setAdminSessionCookie(): Promise<void> {
|
||||
sameSite: "lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
path: "/",
|
||||
domain: getAdminCookieDomain(),
|
||||
maxAge: 60 * 60 * 8,
|
||||
});
|
||||
}
|
||||
@@ -82,6 +152,7 @@ export async function clearAdminSessionCookie(): Promise<void> {
|
||||
sameSite: "lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
path: "/",
|
||||
domain: getAdminCookieDomain(),
|
||||
maxAge: 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
type RootNavigationCopy = {
|
||||
type AdminNavigationCopy = {
|
||||
overview: string;
|
||||
maintenance: string;
|
||||
uiKit: string;
|
||||
@@ -24,21 +24,21 @@ type RootNavigationCopy = {
|
||||
contactProtection?: string;
|
||||
};
|
||||
|
||||
export type RootNavItem = {
|
||||
export type AdminNavItem = {
|
||||
label: string;
|
||||
href: string;
|
||||
icon: LucideIcon;
|
||||
active?: boolean;
|
||||
expanded?: boolean;
|
||||
children?: RootNavItem[];
|
||||
children?: AdminNavItem[];
|
||||
};
|
||||
|
||||
export function getRootNavigation(
|
||||
copy: RootNavigationCopy,
|
||||
export function getAdminNavigation(
|
||||
copy: AdminNavigationCopy,
|
||||
active: "overview" | "maintenance" | "ui-kit" | "portfolio" | "media" | "site-settings" | "smtp" | "marquee",
|
||||
smtpChild?: "settings" | "contact-protection",
|
||||
portfolioChild?: "overview" | "projects" | "new-project" | "categories",
|
||||
): RootNavItem[] {
|
||||
): AdminNavItem[] {
|
||||
return [
|
||||
{
|
||||
label: copy.overview,
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"about": "معلومات عني",
|
||||
"contact": "تواصل معي",
|
||||
"soon": "قــريــبــاً",
|
||||
"root": "Root",
|
||||
"admin": "Admin",
|
||||
"openMenu": "فتح القائمة",
|
||||
"closeMenu": "إغلاق القائمة",
|
||||
"themeToggle": "تبديل المظهر",
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"about": "Über mich",
|
||||
"contact": "Kontakt",
|
||||
"soon": "Bald",
|
||||
"root": "Root",
|
||||
"admin": "Admin",
|
||||
"openMenu": "Menü öffnen",
|
||||
"closeMenu": "Menü schliessen",
|
||||
"themeToggle": "Theme wechseln",
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"about": "About Me",
|
||||
"contact": "Contact",
|
||||
"soon": "Soon",
|
||||
"root": "Root",
|
||||
"admin": "Admin",
|
||||
"openMenu": "Open menu",
|
||||
"closeMenu": "Close menu",
|
||||
"themeToggle": "Toggle theme",
|
||||
|
||||
+18
-13
@@ -13,12 +13,20 @@ import {
|
||||
|
||||
const intlMiddleware = createMiddleware(routing);
|
||||
|
||||
function isRootBasicAuthConfigured(): boolean {
|
||||
return Boolean(process.env.ROOT_BASIC_AUTH_USER && process.env.ROOT_BASIC_AUTH_PASS);
|
||||
function getAdminBasicAuthUser(): string {
|
||||
return process.env.ADMIN_BASIC_AUTH_USER ?? "";
|
||||
}
|
||||
|
||||
function isRootBasicAuthValid(request: NextRequest): boolean {
|
||||
if (!isRootBasicAuthConfigured()) {
|
||||
function getAdminBasicAuthPass(): string {
|
||||
return process.env.ADMIN_BASIC_AUTH_PASS ?? "";
|
||||
}
|
||||
|
||||
function isAdminBasicAuthConfigured(): boolean {
|
||||
return Boolean(getAdminBasicAuthUser() && getAdminBasicAuthPass());
|
||||
}
|
||||
|
||||
function isAdminBasicAuthValid(request: NextRequest): boolean {
|
||||
if (!isAdminBasicAuthConfigured()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,10 +45,7 @@ function isRootBasicAuthValid(request: NextRequest): boolean {
|
||||
const user = decoded.slice(0, index);
|
||||
const pass = decoded.slice(index + 1);
|
||||
|
||||
return (
|
||||
user === process.env.ROOT_BASIC_AUTH_USER &&
|
||||
pass === process.env.ROOT_BASIC_AUTH_PASS
|
||||
);
|
||||
return user === getAdminBasicAuthUser() && pass === getAdminBasicAuthPass();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
@@ -52,7 +57,7 @@ export default async function middleware(request: NextRequest) {
|
||||
request.headers.get("x-forwarded-host") ?? request.headers.get("host") ?? request.nextUrl.hostname,
|
||||
);
|
||||
const isAdminRequest = isAdminHost(hostname);
|
||||
const rootRobotsHeaders = {
|
||||
const adminRobotsHeaders = {
|
||||
"X-Robots-Tag": "noindex, nofollow, noarchive, nosnippet, noimageindex",
|
||||
};
|
||||
|
||||
@@ -63,12 +68,12 @@ export default async function middleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (isAdminRequest) {
|
||||
if (isRootBasicAuthConfigured() && !isRootBasicAuthValid(request)) {
|
||||
if (isAdminBasicAuthConfigured() && !isAdminBasicAuthValid(request)) {
|
||||
return new NextResponse("Authentication required", {
|
||||
status: 401,
|
||||
headers: {
|
||||
"WWW-Authenticate": 'Basic realm="Root Area", charset="UTF-8"',
|
||||
...rootRobotsHeaders,
|
||||
"WWW-Authenticate": 'Basic realm="Admin Area", charset="UTF-8"',
|
||||
...adminRobotsHeaders,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -77,7 +82,7 @@ export default async function middleware(request: NextRequest) {
|
||||
rewriteUrl.pathname = toInternalAdminPath(pathname);
|
||||
|
||||
const response = NextResponse.rewrite(rewriteUrl);
|
||||
response.headers.set("X-Robots-Tag", rootRobotsHeaders["X-Robots-Tag"]);
|
||||
response.headers.set("X-Robots-Tag", adminRobotsHeaders["X-Robots-Tag"]);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user