Add dedicated local admin domain routing
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
NEXT_PUBLIC_SITE_URL="http://mohfarawati.localhost:3000"
|
||||||
|
NEXT_PUBLIC_ADMIN_URL="http://rootmohfarawati.localhost:3000"
|
||||||
|
ADMIN_HOST="rootmohfarawati.localhost"
|
||||||
@@ -25,7 +25,8 @@ ps:
|
|||||||
docker compose ps
|
docker compose ps
|
||||||
|
|
||||||
port:
|
port:
|
||||||
@echo "https://mohfarawati.de"
|
@echo "Site: $${NEXT_PUBLIC_SITE_URL:-https://mohfarawati.de}"
|
||||||
|
@echo "Admin: $${NEXT_PUBLIC_ADMIN_URL:-https://root.mohfarawati.de}"
|
||||||
|
|
||||||
clean-orphans:
|
clean-orphans:
|
||||||
docker compose up -d --remove-orphans
|
docker compose up -d --remove-orphans
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { revalidatePath } from "next/cache";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { routing } from "@/i18n/routing";
|
import { routing } from "@/i18n/routing";
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { getLocalizedPath } from "@/lib/locale";
|
import { getLocalizedPath } from "@/lib/locale";
|
||||||
import { setMaintenanceMode } from "@/lib/app-config";
|
import { setMaintenanceMode } from "@/lib/app-config";
|
||||||
@@ -12,7 +12,7 @@ import { setMaintenanceMode } from "@/lib/app-config";
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export async function updateMaintenanceModeAction(formData: FormData) {
|
|||||||
await ensureAdmin();
|
await ensureAdmin();
|
||||||
|
|
||||||
const nextValue = formData.get("enabled") === "true";
|
const nextValue = formData.get("enabled") === "true";
|
||||||
const redirectPath = String(formData.get("redirectPath") ?? "/");
|
const redirectPath = String(formData.get("redirectPath") ?? getAdminAppPath("/"));
|
||||||
const redirectUrl = new URL(redirectPath, "http://localhost");
|
const redirectUrl = new URL(redirectPath, "http://localhost");
|
||||||
redirectUrl.searchParams.set(
|
redirectUrl.searchParams.set(
|
||||||
"success",
|
"success",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { MotionFade } from "@/components/motion-fade";
|
import { MotionFade } from "@/components/motion-fade";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMaintenanceMode } from "@/lib/app-config";
|
import { getMaintenanceMode } from "@/lib/app-config";
|
||||||
import { AppCard } from "@/components/ui/app-card";
|
import { AppCard } from "@/components/ui/app-card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
@@ -31,7 +32,7 @@ export default async function AdminMaintenancePage() {
|
|||||||
const authenticated = await isAdminAuthenticated();
|
const authenticated = await isAdminAuthenticated();
|
||||||
|
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const maintenanceEnabled = await getMaintenanceMode();
|
const maintenanceEnabled = await getMaintenanceMode();
|
||||||
@@ -39,7 +40,7 @@ export default async function AdminMaintenancePage() {
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { revalidatePath } from "next/cache";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
||||||
|
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { updateMarqueeSettings } from "@/lib/app-config";
|
import { updateMarqueeSettings } from "@/lib/app-config";
|
||||||
import { routing } from "@/i18n/routing";
|
import { routing } from "@/i18n/routing";
|
||||||
@@ -13,7 +13,7 @@ import { getLocalizedPath } from "@/lib/locale";
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,13 +72,13 @@ export async function saveMarqueeSettingsAction(formData: FormData) {
|
|||||||
await updateMarqueeSettings(settings);
|
await updateMarqueeSettings(settings);
|
||||||
await revalidateMarqueePages();
|
await revalidateMarqueePages();
|
||||||
|
|
||||||
redirect(withMessage("/marquee", "success", "Marquee gespeichert."));
|
redirect(withMessage(getAdminAppPath("/marquee"), "success", "Marquee gespeichert."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = error instanceof Error ? error.message : "Marquee konnte nicht gespeichert werden.";
|
const message = error instanceof Error ? error.message : "Marquee konnte nicht gespeichert werden.";
|
||||||
redirect(withMessage("/marquee", "error", message));
|
redirect(withMessage(getAdminAppPath("/marquee"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { MarqueeSettingsForm } from "@/components/admin/marquee-settings-form";
|
|||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { MotionFade } from "@/components/motion-fade";
|
import { MotionFade } from "@/components/motion-fade";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMarqueeSettings } from "@/lib/app-config";
|
import { getMarqueeSettings } from "@/lib/app-config";
|
||||||
|
|
||||||
import { saveMarqueeSettingsAction } from "./actions";
|
import { saveMarqueeSettingsAction } from "./actions";
|
||||||
@@ -27,14 +28,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminMarqueePage() {
|
export default async function AdminMarqueePage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const marqueeSettings = await getMarqueeSettings();
|
const marqueeSettings = await getMarqueeSettings();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { revalidatePath } from "next/cache";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
||||||
|
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { countMediaUsageReferences, getMediaAssetById } from "@/lib/media";
|
import { countMediaUsageReferences, getMediaAssetById } from "@/lib/media";
|
||||||
import { createStandaloneMediaAsset, deleteMediaAssetAndFile } from "@/lib/media-service";
|
import { createStandaloneMediaAsset, deleteMediaAssetAndFile } from "@/lib/media-service";
|
||||||
@@ -15,7 +15,7 @@ import { prisma } from "@/lib/prisma";
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,14 +47,14 @@ export async function createMediaAssetAction(formData: FormData) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
revalidateMediaPages();
|
revalidateMediaPages();
|
||||||
redirect(withMessage("/media", "success", "Datei gespeichert."));
|
redirect(withMessage(getAdminAppPath("/media"), "success", "Datei gespeichert."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = error instanceof Error ? error.message : "Datei konnte nicht gespeichert werden.";
|
const message = error instanceof Error ? error.message : "Datei konnte nicht gespeichert werden.";
|
||||||
redirect(withMessage("/media", "error", message));
|
redirect(withMessage(getAdminAppPath("/media"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +67,13 @@ export async function deleteMediaAssetAction(formData: FormData) {
|
|||||||
const asset = await getMediaAssetById(assetId);
|
const asset = await getMediaAssetById(assetId);
|
||||||
|
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
redirect(withMessage("/media", "error", "Datei nicht gefunden."));
|
redirect(withMessage(getAdminAppPath("/media"), "error", "Datei nicht gefunden."));
|
||||||
}
|
}
|
||||||
|
|
||||||
const usageCount = await countMediaUsageReferences(asset.id);
|
const usageCount = await countMediaUsageReferences(asset.id);
|
||||||
|
|
||||||
if (usageCount > 0) {
|
if (usageCount > 0) {
|
||||||
redirect(withMessage("/media", "error", "Datei wird noch verwendet."));
|
redirect(withMessage(getAdminAppPath("/media"), "error", "Datei wird noch verwendet."));
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.mediaAsset.delete({
|
await prisma.mediaAsset.delete({
|
||||||
@@ -90,13 +90,13 @@ export async function deleteMediaAssetAction(formData: FormData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
revalidateMediaPages();
|
revalidateMediaPages();
|
||||||
redirect(withMessage("/media", "success", "Datei geloescht."));
|
redirect(withMessage(getAdminAppPath("/media"), "success", "Datei geloescht."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = error instanceof Error ? error.message : "Datei konnte nicht geloescht werden.";
|
const message = error instanceof Error ? error.message : "Datei konnte nicht geloescht werden.";
|
||||||
redirect(withMessage("/media", "error", message));
|
redirect(withMessage(getAdminAppPath("/media"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { MediaLibraryManager } from "@/components/admin/media-library-manager";
|
import { MediaLibraryManager } from "@/components/admin/media-library-manager";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getAdminMediaAssets } from "@/lib/media";
|
import { getAdminMediaAssets } from "@/lib/media";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
@@ -22,14 +23,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminMediaPage() {
|
export default async function AdminMediaPage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaAssets = await getAdminMediaAssets();
|
const mediaAssets = await getAdminMediaAssets();
|
||||||
|
|||||||
+6
-5
@@ -20,6 +20,7 @@ import {
|
|||||||
resetAdminFailedAttempts,
|
resetAdminFailedAttempts,
|
||||||
setAdminSessionCookie,
|
setAdminSessionCookie,
|
||||||
} from "@/lib/admin-auth";
|
} from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMaintenanceMode } from "@/lib/app-config";
|
import { getMaintenanceMode } from "@/lib/app-config";
|
||||||
import { getAdminMediaAssets } from "@/lib/media";
|
import { getAdminMediaAssets } from "@/lib/media";
|
||||||
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
||||||
@@ -76,28 +77,28 @@ export default async function AdminPage({ searchParams }: AdminPageProps) {
|
|||||||
const currentLockState = await getAdminLockState();
|
const currentLockState = await getAdminLockState();
|
||||||
|
|
||||||
if (currentLockState.locked) {
|
if (currentLockState.locked) {
|
||||||
redirect("/?error=locked");
|
redirect(`${getAdminAppPath("/")}?error=locked`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAdminAuthConfigured() || !isPasswordValid(password)) {
|
if (!isAdminAuthConfigured() || !isPasswordValid(password)) {
|
||||||
const failState = await registerFailedAdminAttempt();
|
const failState = await registerFailedAdminAttempt();
|
||||||
if (failState.locked) {
|
if (failState.locked) {
|
||||||
redirect("/?error=locked");
|
redirect(`${getAdminAppPath("/")}?error=locked`);
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect("/?error=invalid");
|
redirect(`${getAdminAppPath("/")}?error=invalid`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await resetAdminFailedAttempts();
|
await resetAdminFailedAttempts();
|
||||||
await setAdminSessionCookie();
|
await setAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { isRedirectError } from "next/dist/client/components/redirect-error";
|
|||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
import { routing } from "@/i18n/routing";
|
import { routing } from "@/i18n/routing";
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { deleteEntityMediaUsages, replaceEntityMediaUsages } from "@/lib/media";
|
import { deleteEntityMediaUsages, replaceEntityMediaUsages } from "@/lib/media";
|
||||||
import { resolveMediaSelection } from "@/lib/media-service";
|
import { resolveMediaSelection } from "@/lib/media-service";
|
||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ async function removeManagedPaths(paths: string[]) {
|
|||||||
export async function upsertCategoryAction(formData: FormData) {
|
export async function upsertCategoryAction(formData: FormData) {
|
||||||
await ensureAdmin();
|
await ensureAdmin();
|
||||||
|
|
||||||
const redirectPath = getRedirectPath(formData, "/portfolio/categories");
|
const redirectPath = getRedirectPath(formData, getAdminAppPath("/portfolio/categories"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = categoryInputSchema.parse({
|
const parsed = categoryInputSchema.parse({
|
||||||
@@ -157,7 +157,7 @@ export async function upsertCategoryAction(formData: FormData) {
|
|||||||
export async function deleteCategoryAction(formData: FormData) {
|
export async function deleteCategoryAction(formData: FormData) {
|
||||||
await ensureAdmin();
|
await ensureAdmin();
|
||||||
|
|
||||||
const redirectPath = getRedirectPath(formData, "/portfolio/categories");
|
const redirectPath = getRedirectPath(formData, getAdminAppPath("/portfolio/categories"));
|
||||||
const id = String(formData.get("id") ?? "");
|
const id = String(formData.get("id") ?? "");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -192,8 +192,8 @@ export async function saveProjectAction(formData: FormData) {
|
|||||||
await ensureAdmin();
|
await ensureAdmin();
|
||||||
|
|
||||||
const fallbackRedirect = String(formData.get("id") ?? "").trim()
|
const fallbackRedirect = String(formData.get("id") ?? "").trim()
|
||||||
? `/portfolio/projects/${String(formData.get("id") ?? "").trim()}`
|
? getAdminAppPath(`/portfolio/projects/${String(formData.get("id") ?? "").trim()}`)
|
||||||
: "/portfolio/projects/new";
|
: getAdminAppPath("/portfolio/projects/new");
|
||||||
const redirectPath = getRedirectPath(formData, fallbackRedirect);
|
const redirectPath = getRedirectPath(formData, fallbackRedirect);
|
||||||
const uploadedPaths: string[] = [];
|
const uploadedPaths: string[] = [];
|
||||||
const createdMediaAssetIds: string[] = [];
|
const createdMediaAssetIds: string[] = [];
|
||||||
@@ -526,7 +526,7 @@ export async function saveProjectAction(formData: FormData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
withMessage(`/portfolio/projects/${projectResult.project.id}`, "success", "Projekt gespeichert."),
|
withMessage(getAdminAppPath(`/portfolio/projects/${projectResult.project.id}`), "success", "Projekt gespeichert."),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
@@ -579,7 +579,7 @@ export async function deleteProjectAction(formData: FormData) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!project) {
|
if (!project) {
|
||||||
redirect(withMessage("/portfolio", "error", "Project not found."));
|
redirect(withMessage(getAdminAppPath("/portfolio"), "error", "Project not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.portfolioProject.delete({
|
await prisma.portfolioProject.delete({
|
||||||
@@ -596,12 +596,12 @@ export async function deleteProjectAction(formData: FormData) {
|
|||||||
revalidatePath(getLocalizedPath(locale, `/portfolio/${project.slug}`));
|
revalidatePath(getLocalizedPath(locale, `/portfolio/${project.slug}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect(withMessage("/portfolio", "success", "Project deleted."));
|
redirect(withMessage(getAdminAppPath("/portfolio"), "success", "Project deleted."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect(withMessage("/portfolio", "error", "Unable to delete project."));
|
redirect(withMessage(getAdminAppPath("/portfolio"), "error", "Unable to delete project."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { PortfolioCategoriesManager } from "@/components/admin/portfolio-categories-manager";
|
import { PortfolioCategoriesManager } from "@/components/admin/portfolio-categories-manager";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getAdminPortfolioCategories } from "@/lib/portfolio";
|
import { getAdminPortfolioCategories } from "@/lib/portfolio";
|
||||||
|
|
||||||
import { deleteCategoryAction, upsertCategoryAction } from "../actions";
|
import { deleteCategoryAction, upsertCategoryAction } from "../actions";
|
||||||
@@ -24,14 +25,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminPortfolioCategoriesPage() {
|
export default async function AdminPortfolioCategoriesPage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const categories = await getAdminPortfolioCategories();
|
const categories = await getAdminPortfolioCategories();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
|
|
||||||
export default function AdminPortfolioMediaRedirectPage() {
|
export default function AdminPortfolioMediaRedirectPage() {
|
||||||
redirect("/media");
|
redirect(getAdminAppPath("/media"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { PortfolioProjectsOverview } from "@/components/admin/portfolio-projects-overview";
|
import { PortfolioProjectsOverview } from "@/components/admin/portfolio-projects-overview";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
@@ -33,14 +34,14 @@ export default async function AdminPortfolioPage({ searchParams }: AdminPortfoli
|
|||||||
const resolvedSearchParams = await searchParams;
|
const resolvedSearchParams = await searchParams;
|
||||||
|
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCategory = resolvedSearchParams?.category && resolvedSearchParams.category !== "__all__"
|
const selectedCategory = resolvedSearchParams?.category && resolvedSearchParams.category !== "__all__"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMediaOptions } from "@/lib/media";
|
import { getMediaOptions } from "@/lib/media";
|
||||||
import {
|
import {
|
||||||
getActivePortfolioCategories,
|
getActivePortfolioCategories,
|
||||||
@@ -53,14 +54,14 @@ export default async function AdminPortfolioProjectPage({
|
|||||||
params,
|
params,
|
||||||
}: AdminPortfolioProjectPageProps) {
|
}: AdminPortfolioProjectPageProps) {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
@@ -72,7 +73,7 @@ export default async function AdminPortfolioProjectPage({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (!project) {
|
if (!project) {
|
||||||
redirect("/portfolio?error=Project+not+found.");
|
redirect(`${getAdminAppPath("/portfolio")}?error=Project+not+found.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -92,7 +93,7 @@ export default async function AdminPortfolioProjectPage({
|
|||||||
mediaOptions={mediaOptions}
|
mediaOptions={mediaOptions}
|
||||||
project={project}
|
project={project}
|
||||||
formId="portfolio-project-form"
|
formId="portfolio-project-form"
|
||||||
redirectPath={`/portfolio/projects/${project.id}`}
|
redirectPath={getAdminAppPath(`/portfolio/projects/${project.id}`)}
|
||||||
/>
|
/>
|
||||||
</MotionFade>
|
</MotionFade>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { MotionFade } from "@/components/motion-fade";
|
|||||||
import { PortfolioProjectForm } from "@/components/admin/portfolio-project-form";
|
import { PortfolioProjectForm } from "@/components/admin/portfolio-project-form";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMediaOptions } from "@/lib/media";
|
import { getMediaOptions } from "@/lib/media";
|
||||||
import { getActivePortfolioCategories } from "@/lib/portfolio";
|
import { getActivePortfolioCategories } from "@/lib/portfolio";
|
||||||
|
|
||||||
@@ -26,14 +27,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminNewPortfolioProjectPage() {
|
export default async function AdminNewPortfolioProjectPage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const [categories, mediaOptions] = await Promise.all([
|
const [categories, mediaOptions] = await Promise.all([
|
||||||
@@ -57,7 +58,7 @@ export default async function AdminNewPortfolioProjectPage() {
|
|||||||
categories={categories}
|
categories={categories}
|
||||||
mediaOptions={mediaOptions}
|
mediaOptions={mediaOptions}
|
||||||
formId="portfolio-project-form"
|
formId="portfolio-project-form"
|
||||||
redirectPath="/portfolio/projects/new"
|
redirectPath={getAdminAppPath("/portfolio/projects/new")}
|
||||||
/>
|
/>
|
||||||
</MotionFade>
|
</MotionFade>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { PortfolioProjectsOverview } from "@/components/admin/portfolio-projects-overview";
|
import { PortfolioProjectsOverview } from "@/components/admin/portfolio-projects-overview";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
import { getAdminPortfolioCategories, getAdminPortfolioProjects } from "@/lib/portfolio";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
@@ -33,14 +34,14 @@ export default async function AdminPortfolioProjectsPage({
|
|||||||
const resolvedSearchParams = await searchParams;
|
const resolvedSearchParams = await searchParams;
|
||||||
|
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedCategory = resolvedSearchParams?.category && resolvedSearchParams.category !== "__all__"
|
const selectedCategory = resolvedSearchParams?.category && resolvedSearchParams.category !== "__all__"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
SITE_SETTINGS_LOGO_LIGHT_FIELD_KEY,
|
SITE_SETTINGS_LOGO_LIGHT_FIELD_KEY,
|
||||||
updateSiteSettings,
|
updateSiteSettings,
|
||||||
} from "@/lib/app-config";
|
} from "@/lib/app-config";
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { PAGE_TITLE_TOKEN, type SiteSettings } from "@/lib/site-settings";
|
import { PAGE_TITLE_TOKEN, type SiteSettings } from "@/lib/site-settings";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { replaceEntityMediaUsages } from "@/lib/media";
|
import { replaceEntityMediaUsages } from "@/lib/media";
|
||||||
@@ -28,7 +28,7 @@ import { prisma } from "@/lib/prisma";
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ export async function saveSiteSettingsAction(formData: FormData) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await revalidateSiteSettingsPages();
|
await revalidateSiteSettingsPages();
|
||||||
redirect(withMessage("/site-settings", "success", "Einstellungen gespeichert."));
|
redirect(withMessage(getAdminAppPath("/site-settings"), "success", "Einstellungen gespeichert."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -291,6 +291,6 @@ export async function saveSiteSettingsAction(formData: FormData) {
|
|||||||
? error.message
|
? error.message
|
||||||
: "Einstellungen konnten nicht gespeichert werden.";
|
: "Einstellungen konnten nicht gespeichert werden.";
|
||||||
|
|
||||||
redirect(withMessage("/site-settings", "error", message));
|
redirect(withMessage(getAdminAppPath("/site-settings"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { MotionFade } from "@/components/motion-fade";
|
|||||||
import { SiteSettingsForm } from "@/components/admin/site-settings-form";
|
import { SiteSettingsForm } from "@/components/admin/site-settings-form";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import {
|
import {
|
||||||
getSiteSettings,
|
getSiteSettings,
|
||||||
getSiteSettingsMediaBindings,
|
getSiteSettingsMediaBindings,
|
||||||
@@ -31,14 +32,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminSiteSettingsPage() {
|
export default async function AdminSiteSettingsPage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const [siteSettings, mediaBindings, mediaOptions] = await Promise.all([
|
const [siteSettings, mediaBindings, mediaOptions] = await Promise.all([
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { revalidatePath } from "next/cache";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
||||||
|
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { isCheckedFormValue } from "@/lib/form-data";
|
import { isCheckedFormValue } from "@/lib/form-data";
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +17,7 @@ import type { MailSettings } from "@/lib/mail-settings";
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ export async function saveMailSettingsAction(formData: FormData) {
|
|||||||
|
|
||||||
await updateMailSettings(nextMailSettings);
|
await updateMailSettings(nextMailSettings);
|
||||||
revalidatePath(toInternalAdminPath("/smtp"));
|
revalidatePath(toInternalAdminPath("/smtp"));
|
||||||
redirect(withMessage("/smtp", "success", "SMTP Einstellungen gespeichert."));
|
redirect(withMessage(getAdminAppPath("/smtp"), "success", "SMTP Einstellungen gespeichert."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -90,7 +90,7 @@ export async function saveMailSettingsAction(formData: FormData) {
|
|||||||
? error.message
|
? error.message
|
||||||
: "SMTP Einstellungen konnten nicht gespeichert werden.";
|
: "SMTP Einstellungen konnten nicht gespeichert werden.";
|
||||||
|
|
||||||
redirect(withMessage("/smtp", "error", message));
|
redirect(withMessage(getAdminAppPath("/smtp"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ export async function sendTestEmailAction() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await sendTestEmail();
|
await sendTestEmail();
|
||||||
redirect(withMessage("/smtp", "success", "Test-E-Mail gesendet."));
|
redirect(withMessage(getAdminAppPath("/smtp"), "success", "Test-E-Mail gesendet."));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isRedirectError(error)) {
|
if (isRedirectError(error)) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -110,6 +110,6 @@ export async function sendTestEmailAction() {
|
|||||||
? error.message
|
? error.message
|
||||||
: "Test-E-Mail konnte nicht gesendet werden.";
|
: "Test-E-Mail konnte nicht gesendet werden.";
|
||||||
|
|
||||||
redirect(withMessage("/smtp", "error", message));
|
redirect(withMessage(getAdminAppPath("/smtp"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { revalidatePath } from "next/cache";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
import { isRedirectError } from "next/dist/client/components/redirect-error";
|
||||||
|
|
||||||
import { toInternalAdminPath } from "@/lib/admin-routing";
|
import { getAdminAppPath, toInternalAdminPath } from "@/lib/admin-routing";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
import { isCheckedFormValue } from "@/lib/form-data";
|
import { isCheckedFormValue } from "@/lib/form-data";
|
||||||
import {
|
import {
|
||||||
@@ -16,7 +16,7 @@ import type { ContactProtectionSettings } from "@/lib/contact-protection";
|
|||||||
async function ensureAdmin() {
|
async function ensureAdmin() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ export async function saveContactProtectionSettingsAction(formData: FormData) {
|
|||||||
revalidatePath("/en/contact");
|
revalidatePath("/en/contact");
|
||||||
redirect(
|
redirect(
|
||||||
withMessage(
|
withMessage(
|
||||||
"/smtp/contact-protection",
|
getAdminAppPath("/smtp/contact-protection"),
|
||||||
"success",
|
"success",
|
||||||
"Contact Protection gespeichert.",
|
"Contact Protection gespeichert.",
|
||||||
),
|
),
|
||||||
@@ -107,6 +107,6 @@ export async function saveContactProtectionSettingsAction(formData: FormData) {
|
|||||||
? error.message
|
? error.message
|
||||||
: "Contact Protection konnte nicht gespeichert werden.";
|
: "Contact Protection konnte nicht gespeichert werden.";
|
||||||
|
|
||||||
redirect(withMessage("/smtp/contact-protection", "error", message));
|
redirect(withMessage(getAdminAppPath("/smtp/contact-protection"), "error", message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { MotionFade } from "@/components/motion-fade";
|
|||||||
import { ContactProtectionForm } from "@/components/admin/contact-protection-form";
|
import { ContactProtectionForm } from "@/components/admin/contact-protection-form";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getContactProtectionFormValues } from "@/lib/app-config";
|
import { getContactProtectionFormValues } from "@/lib/app-config";
|
||||||
|
|
||||||
import { saveContactProtectionSettingsAction } from "./actions";
|
import { saveContactProtectionSettingsAction } from "./actions";
|
||||||
@@ -27,14 +28,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminSMTPProtectionPage() {
|
export default async function AdminSMTPProtectionPage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = await getContactProtectionFormValues();
|
const settings = await getContactProtectionFormValues();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
|||||||
import { SMTPSettingsForm } from "@/components/admin/smtp-settings-form";
|
import { SMTPSettingsForm } from "@/components/admin/smtp-settings-form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMailSettingsFormValues } from "@/lib/app-config";
|
import { getMailSettingsFormValues } from "@/lib/app-config";
|
||||||
|
|
||||||
import { saveMailSettingsAction, sendTestEmailAction } from "./actions";
|
import { saveMailSettingsAction, sendTestEmailAction } from "./actions";
|
||||||
@@ -28,14 +29,14 @@ const copy = {
|
|||||||
|
|
||||||
export default async function AdminSMTPPage() {
|
export default async function AdminSMTPPage() {
|
||||||
if (!(await isAdminAuthenticated())) {
|
if (!(await isAdminAuthenticated())) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const mailSettings = await getMailSettingsFormValues();
|
const mailSettings = await getMailSettingsFormValues();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { MotionFade } from "@/components/motion-fade";
|
import { MotionFade } from "@/components/motion-fade";
|
||||||
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
import { AdminDashboardShell } from "@/components/admin/admin-dashboard-shell";
|
||||||
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { UiKitShowcase } from "@/components/ui/ui-kit-showcase";
|
import { UiKitShowcase } from "@/components/ui/ui-kit-showcase";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
@@ -24,14 +25,14 @@ export default async function AdminUiKitPage() {
|
|||||||
const authenticated = await isAdminAuthenticated();
|
const authenticated = await isAdminAuthenticated();
|
||||||
|
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logoutAction() {
|
async function logoutAction() {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
await clearAdminSessionCookie();
|
await clearAdminSessionCookie();
|
||||||
redirect("/");
|
redirect(getAdminAppPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,2 +1,19 @@
|
|||||||
export { metadata } from "../_admin/layout";
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
robots: {
|
||||||
|
index: false,
|
||||||
|
follow: false,
|
||||||
|
nocache: true,
|
||||||
|
googleBot: {
|
||||||
|
index: false,
|
||||||
|
follow: false,
|
||||||
|
noimageindex: true,
|
||||||
|
"max-image-preview": "none",
|
||||||
|
"max-snippet": -1,
|
||||||
|
"max-video-preview": -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export { default } from "../_admin/layout";
|
export { default } from "../_admin/layout";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { dynamic } from "../../_admin/maintenance/page";
|
export const dynamic = "force-dynamic";
|
||||||
export { default } from "../../_admin/maintenance/page";
|
export { default } from "../../_admin/maintenance/page";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { dynamic } from "../../_admin/media/page";
|
export const dynamic = "force-dynamic";
|
||||||
export { default } from "../../_admin/media/page";
|
export { default } from "../../_admin/media/page";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { dynamic } from "../_admin/page";
|
export const dynamic = "force-dynamic";
|
||||||
export { default } from "../_admin/page";
|
export { default } from "../_admin/page";
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
robots: {
|
||||||
|
index: false,
|
||||||
|
follow: false,
|
||||||
|
nocache: true,
|
||||||
|
googleBot: {
|
||||||
|
index: false,
|
||||||
|
follow: false,
|
||||||
|
noimageindex: true,
|
||||||
|
"max-image-preview": "none",
|
||||||
|
"max-snippet": -1,
|
||||||
|
"max-video-preview": -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export { default } from "../_admin/layout";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
export { default } from "../../_admin/maintenance/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../_admin/marquee/page";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
export { default } from "../../_admin/media/page";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
export { default } from "../_admin/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../../_admin/portfolio/categories/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../../_admin/portfolio/media/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../_admin/portfolio/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../../../_admin/portfolio/projects/[id]/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../../../_admin/portfolio/projects/new/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../../_admin/portfolio/projects/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../_admin/site-settings/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../../_admin/smtp/contact-protection/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../_admin/smtp/page";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../../_admin/ui-kit/page";
|
||||||
@@ -19,7 +19,7 @@ import { SidebarMaintenanceControl } from "@/components/admin/sidebar-maintenanc
|
|||||||
import { SoundToggle } from "@/components/sound-toggle";
|
import { SoundToggle } from "@/components/sound-toggle";
|
||||||
import { ThemeToggle } from "@/components/theme-toggle";
|
import { ThemeToggle } from "@/components/theme-toggle";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { buildSiteUrl } from "@/lib/admin-routing";
|
import { buildSiteUrl, getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getMaintenanceMode, getSiteSettingsMediaBindings } from "@/lib/app-config";
|
import { getMaintenanceMode, getSiteSettingsMediaBindings } from "@/lib/app-config";
|
||||||
import { getAdminNavigation } from "@/lib/admin-navigation";
|
import { getAdminNavigation } from "@/lib/admin-navigation";
|
||||||
|
|
||||||
@@ -75,12 +75,12 @@ export async function AdminDashboardShell({
|
|||||||
const sidebarItems = getAdminNavigation(copy, active, smtpChild, portfolioChild);
|
const sidebarItems = getAdminNavigation(copy, active, smtpChild, portfolioChild);
|
||||||
const normalizedSidebarItems = sidebarItems.filter(
|
const normalizedSidebarItems = sidebarItems.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.href !== "/maintenance" &&
|
item.href !== getAdminAppPath("/maintenance") &&
|
||||||
item.href !== "/ui-kit" &&
|
item.href !== getAdminAppPath("/ui-kit") &&
|
||||||
item.href !== "/smtp" &&
|
item.href !== getAdminAppPath("/smtp") &&
|
||||||
item.href !== "/marquee",
|
item.href !== getAdminAppPath("/marquee"),
|
||||||
);
|
);
|
||||||
const footerSidebarItems = ["/marquee", "/smtp"]
|
const footerSidebarItems = [getAdminAppPath("/marquee"), getAdminAppPath("/smtp")]
|
||||||
.map((href) => sidebarItems.find((item) => item.href === href))
|
.map((href) => sidebarItems.find((item) => item.href === href))
|
||||||
.filter((item): item is NonNullable<typeof item> => Boolean(item));
|
.filter((item): item is NonNullable<typeof item> => Boolean(item));
|
||||||
const headerIcon =
|
const headerIcon =
|
||||||
@@ -126,7 +126,7 @@ export async function AdminDashboardShell({
|
|||||||
variant={active === "ui-kit" ? "default" : "outline"}
|
variant={active === "ui-kit" ? "default" : "outline"}
|
||||||
className="w-full justify-between"
|
className="w-full justify-between"
|
||||||
>
|
>
|
||||||
<Link href="/ui-kit">
|
<Link href={getAdminAppPath("/ui-kit")}>
|
||||||
{copy.uiKit}
|
{copy.uiKit}
|
||||||
<SwatchBook className="h-4 w-4" />
|
<SwatchBook className="h-4 w-4" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { AppCard } from "@/components/ui/app-card";
|
|||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { CardContent } from "@/components/ui/card";
|
import { CardContent } from "@/components/ui/card";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
import { getLocalizedPath } from "@/lib/locale";
|
import { getLocalizedPath } from "@/lib/locale";
|
||||||
import { getLocalizedValue, type PortfolioCategoryView, type PortfolioProjectView } from "@/lib/portfolio";
|
import { getLocalizedValue, type PortfolioCategoryView, type PortfolioProjectView } from "@/lib/portfolio";
|
||||||
|
|
||||||
@@ -46,13 +47,13 @@ export function PortfolioProjectsOverview({
|
|||||||
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button asChild>
|
<Button asChild>
|
||||||
<Link href="/portfolio/projects/new">
|
<Link href={getAdminAppPath("/portfolio/projects/new")}>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
{copy.newProject}
|
{copy.newProject}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild variant="outline">
|
<Button asChild variant="outline">
|
||||||
<Link href="/portfolio/categories">
|
<Link href={getAdminAppPath("/portfolio/categories")}>
|
||||||
<Tags className="h-4 w-4" />
|
<Tags className="h-4 w-4" />
|
||||||
{copy.newCategory}
|
{copy.newCategory}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -62,7 +63,7 @@ export function PortfolioProjectsOverview({
|
|||||||
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button asChild variant={selectedCategory === "" ? "default" : "outline"}>
|
<Button asChild variant={selectedCategory === "" ? "default" : "outline"}>
|
||||||
<Link href="/portfolio">{copy.all}</Link>
|
<Link href={getAdminAppPath("/portfolio")}>{copy.all}</Link>
|
||||||
</Button>
|
</Button>
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<Button
|
<Button
|
||||||
@@ -70,7 +71,7 @@ export function PortfolioProjectsOverview({
|
|||||||
asChild
|
asChild
|
||||||
variant={selectedCategory === category.id ? "default" : "outline"}
|
variant={selectedCategory === category.id ? "default" : "outline"}
|
||||||
>
|
>
|
||||||
<Link href={`/portfolio?category=${category.id}`}>
|
<Link href={`${getAdminAppPath("/portfolio")}?category=${category.id}`}>
|
||||||
{category.name.de || category.name.en || category.name.ar}
|
{category.name.de || category.name.en || category.name.ar}
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
+14
-13
@@ -11,6 +11,7 @@ import {
|
|||||||
Type,
|
Type,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { getAdminAppPath } from "@/lib/admin-routing";
|
||||||
|
|
||||||
type AdminNavigationCopy = {
|
type AdminNavigationCopy = {
|
||||||
overview: string;
|
overview: string;
|
||||||
@@ -42,56 +43,56 @@ export function getAdminNavigation(
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: copy.overview,
|
label: copy.overview,
|
||||||
href: "/",
|
href: getAdminAppPath("/"),
|
||||||
icon: LayoutDashboard,
|
icon: LayoutDashboard,
|
||||||
active: active === "overview",
|
active: active === "overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.maintenance,
|
label: copy.maintenance,
|
||||||
href: "/maintenance",
|
href: getAdminAppPath("/maintenance"),
|
||||||
icon: ShieldAlert,
|
icon: ShieldAlert,
|
||||||
active: active === "maintenance",
|
active: active === "maintenance",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.uiKit,
|
label: copy.uiKit,
|
||||||
href: "/ui-kit",
|
href: getAdminAppPath("/ui-kit"),
|
||||||
icon: SwatchBook,
|
icon: SwatchBook,
|
||||||
active: active === "ui-kit",
|
active: active === "ui-kit",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.media,
|
label: copy.media,
|
||||||
href: "/media",
|
href: getAdminAppPath("/media"),
|
||||||
icon: ImageIcon,
|
icon: ImageIcon,
|
||||||
active: active === "media",
|
active: active === "media",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.siteSettings,
|
label: copy.siteSettings,
|
||||||
href: "/site-settings",
|
href: getAdminAppPath("/site-settings"),
|
||||||
icon: Globe2,
|
icon: Globe2,
|
||||||
active: active === "site-settings",
|
active: active === "site-settings",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.marquee ?? "Marquee",
|
label: copy.marquee ?? "Marquee",
|
||||||
href: "/marquee",
|
href: getAdminAppPath("/marquee"),
|
||||||
icon: Type,
|
icon: Type,
|
||||||
active: active === "marquee",
|
active: active === "marquee",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.smtp ?? "SMTP",
|
label: copy.smtp ?? "SMTP",
|
||||||
href: "/smtp",
|
href: getAdminAppPath("/smtp"),
|
||||||
icon: Mail,
|
icon: Mail,
|
||||||
active: active === "smtp" && !smtpChild,
|
active: active === "smtp" && !smtpChild,
|
||||||
expanded: active === "smtp",
|
expanded: active === "smtp",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: copy.smtp ?? "SMTP",
|
label: copy.smtp ?? "SMTP",
|
||||||
href: "/smtp",
|
href: getAdminAppPath("/smtp"),
|
||||||
icon: Mail,
|
icon: Mail,
|
||||||
active: smtpChild === "settings" || (!smtpChild && active === "smtp"),
|
active: smtpChild === "settings" || (!smtpChild && active === "smtp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.contactProtection ?? "Contact Protection",
|
label: copy.contactProtection ?? "Contact Protection",
|
||||||
href: "/smtp/contact-protection",
|
href: getAdminAppPath("/smtp/contact-protection"),
|
||||||
icon: ShieldAlert,
|
icon: ShieldAlert,
|
||||||
active: smtpChild === "contact-protection",
|
active: smtpChild === "contact-protection",
|
||||||
},
|
},
|
||||||
@@ -99,26 +100,26 @@ export function getAdminNavigation(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: copy.portfolio,
|
label: copy.portfolio,
|
||||||
href: "/portfolio",
|
href: getAdminAppPath("/portfolio"),
|
||||||
icon: FolderKanban,
|
icon: FolderKanban,
|
||||||
active: active === "portfolio" && !portfolioChild,
|
active: active === "portfolio" && !portfolioChild,
|
||||||
expanded: active === "portfolio",
|
expanded: active === "portfolio",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: "Projects",
|
label: "Projects",
|
||||||
href: "/portfolio",
|
href: getAdminAppPath("/portfolio"),
|
||||||
icon: FolderKanban,
|
icon: FolderKanban,
|
||||||
active: portfolioChild === "overview" || portfolioChild === "projects",
|
active: portfolioChild === "overview" || portfolioChild === "projects",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Add Project",
|
label: "Add Project",
|
||||||
href: "/portfolio/projects/new",
|
href: getAdminAppPath("/portfolio/projects/new"),
|
||||||
icon: PlusSquare,
|
icon: PlusSquare,
|
||||||
active: portfolioChild === "new-project",
|
active: portfolioChild === "new-project",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Add Category",
|
label: "Add Category",
|
||||||
href: "/portfolio/categories",
|
href: getAdminAppPath("/portfolio/categories"),
|
||||||
icon: Tags,
|
icon: Tags,
|
||||||
active: portfolioChild === "categories",
|
active: portfolioChild === "categories",
|
||||||
},
|
},
|
||||||
|
|||||||
+56
-1
@@ -1,6 +1,7 @@
|
|||||||
const DEFAULT_ADMIN_HOST = "root.mohfarawati.de";
|
const DEFAULT_ADMIN_HOST = "root.mohfarawati.de";
|
||||||
const DEFAULT_ADMIN_URL = `https://${DEFAULT_ADMIN_HOST}`;
|
const DEFAULT_ADMIN_URL = `https://${DEFAULT_ADMIN_HOST}`;
|
||||||
const DEFAULT_SITE_URL = "https://mohfarawati.de";
|
const DEFAULT_SITE_URL = "https://mohfarawati.de";
|
||||||
|
const DEV_ADMIN_PREFIX = "/root";
|
||||||
|
|
||||||
export const INTERNAL_ADMIN_PREFIX = "/admin-internal";
|
export const INTERNAL_ADMIN_PREFIX = "/admin-internal";
|
||||||
|
|
||||||
@@ -16,10 +17,32 @@ function normalizeBaseUrl(url: string): string {
|
|||||||
return url.replace(/\/+$/, "");
|
return url.replace(/\/+$/, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getAdminHost(): string {
|
export function getAdminHost(): string {
|
||||||
return (process.env.ADMIN_HOST ?? DEFAULT_ADMIN_HOST).trim().toLowerCase();
|
return (process.env.ADMIN_HOST ?? DEFAULT_ADMIN_HOST).trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSiteHost(): string {
|
||||||
|
return parseHostname(process.env.NEXT_PUBLIC_SITE_URL ?? DEFAULT_SITE_URL) ?? "localhost";
|
||||||
|
}
|
||||||
|
|
||||||
export function getRequestHostname(hostHeader?: string | null): string {
|
export function getRequestHostname(hostHeader?: string | null): string {
|
||||||
return (hostHeader ?? "")
|
return (hostHeader ?? "")
|
||||||
.split(",")[0]
|
.split(",")[0]
|
||||||
@@ -32,10 +55,18 @@ export function isAdminHost(hostname: string): boolean {
|
|||||||
return hostname === getAdminHost();
|
return hostname === getAdminHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasDedicatedAdminHost(): boolean {
|
||||||
|
return getAdminHost() !== getSiteHost();
|
||||||
|
}
|
||||||
|
|
||||||
export function isLegacyAdminPath(pathname: string): boolean {
|
export function isLegacyAdminPath(pathname: string): boolean {
|
||||||
return pathname === "/root" || pathname.startsWith("/root/");
|
return pathname === "/root" || pathname.startsWith("/root/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDevelopmentAdminPath(pathname: string): boolean {
|
||||||
|
return pathname === DEV_ADMIN_PREFIX || pathname.startsWith(`${DEV_ADMIN_PREFIX}/`);
|
||||||
|
}
|
||||||
|
|
||||||
export function isInternalAdminPath(pathname: string): boolean {
|
export function isInternalAdminPath(pathname: string): boolean {
|
||||||
return pathname === INTERNAL_ADMIN_PREFIX || pathname.startsWith(`${INTERNAL_ADMIN_PREFIX}/`);
|
return pathname === INTERNAL_ADMIN_PREFIX || pathname.startsWith(`${INTERNAL_ADMIN_PREFIX}/`);
|
||||||
}
|
}
|
||||||
@@ -50,12 +81,36 @@ export function toInternalAdminPath(pathname: string): string {
|
|||||||
return `${INTERNAL_ADMIN_PREFIX}${normalizedPathname}`;
|
return `${INTERNAL_ADMIN_PREFIX}${normalizedPathname}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fromDevelopmentAdminPath(pathname: string): string {
|
||||||
|
if (pathname === DEV_ADMIN_PREFIX) {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathname.startsWith(`${DEV_ADMIN_PREFIX}/`)) {
|
||||||
|
return pathname.slice(DEV_ADMIN_PREFIX.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathname;
|
||||||
|
}
|
||||||
|
|
||||||
export function getAdminBaseUrl(): string {
|
export function getAdminBaseUrl(): string {
|
||||||
return normalizeBaseUrl(process.env.NEXT_PUBLIC_ADMIN_URL ?? DEFAULT_ADMIN_URL);
|
return normalizeBaseUrl(process.env.NEXT_PUBLIC_ADMIN_URL ?? DEFAULT_ADMIN_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAdminAppPath(pathname = "/"): string {
|
||||||
|
const normalizedPathname = normalizePathname(pathname);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "production" && !hasDedicatedAdminHost()) {
|
||||||
|
return normalizedPathname === "/"
|
||||||
|
? DEV_ADMIN_PREFIX
|
||||||
|
: `${DEV_ADMIN_PREFIX}${normalizedPathname}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedPathname;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildAdminUrl(pathname = "/"): string {
|
export function buildAdminUrl(pathname = "/"): string {
|
||||||
return `${getAdminBaseUrl()}${normalizePathname(pathname)}`;
|
return `${getAdminBaseUrl()}${getAdminAppPath(pathname)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSiteBaseUrl(): string {
|
export function getSiteBaseUrl(): string {
|
||||||
|
|||||||
+30
-5
@@ -4,8 +4,12 @@ import type { NextRequest } from "next/server";
|
|||||||
|
|
||||||
import { routing } from "./i18n/routing";
|
import { routing } from "./i18n/routing";
|
||||||
import {
|
import {
|
||||||
|
fromDevelopmentAdminPath,
|
||||||
|
getAdminBaseUrl,
|
||||||
getRequestHostname,
|
getRequestHostname,
|
||||||
|
isDevelopmentAdminPath,
|
||||||
isAdminHost,
|
isAdminHost,
|
||||||
|
hasDedicatedAdminHost,
|
||||||
isInternalAdminPath,
|
isInternalAdminPath,
|
||||||
isLegacyAdminPath,
|
isLegacyAdminPath,
|
||||||
toInternalAdminPath,
|
toInternalAdminPath,
|
||||||
@@ -53,15 +57,35 @@ function isAdminBasicAuthValid(request: NextRequest): boolean {
|
|||||||
|
|
||||||
export default async function middleware(request: NextRequest) {
|
export default async function middleware(request: NextRequest) {
|
||||||
const { pathname } = request.nextUrl;
|
const { pathname } = request.nextUrl;
|
||||||
|
const isDevelopmentAdminRequest =
|
||||||
|
process.env.NODE_ENV !== "production" && isDevelopmentAdminPath(pathname);
|
||||||
const hostname = getRequestHostname(
|
const hostname = getRequestHostname(
|
||||||
request.headers.get("x-forwarded-host") ?? request.headers.get("host") ?? request.nextUrl.hostname,
|
request.headers.get("host") ?? request.headers.get("x-forwarded-host") ?? request.nextUrl.hostname,
|
||||||
);
|
);
|
||||||
const isAdminRequest = isAdminHost(hostname);
|
const isAdminRequest = isDevelopmentAdminRequest || isAdminHost(hostname);
|
||||||
|
const hasDedicatedAdminHostname = hasDedicatedAdminHost();
|
||||||
const adminRobotsHeaders = {
|
const adminRobotsHeaders = {
|
||||||
"X-Robots-Tag": "noindex, nofollow, noarchive, nosnippet, noimageindex",
|
"X-Robots-Tag": "noindex, nofollow, noarchive, nosnippet, noimageindex",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isInternalAdminPath(pathname) || isLegacyAdminPath(pathname)) {
|
if (
|
||||||
|
isDevelopmentAdminRequest &&
|
||||||
|
hasDedicatedAdminHostname &&
|
||||||
|
!isAdminHost(hostname)
|
||||||
|
) {
|
||||||
|
const redirectUrl = new URL(getAdminBaseUrl());
|
||||||
|
redirectUrl.pathname = fromDevelopmentAdminPath(pathname);
|
||||||
|
redirectUrl.search = request.nextUrl.search;
|
||||||
|
return NextResponse.redirect(redirectUrl, 308);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLegacyAdminPath(pathname) && process.env.NODE_ENV === "production") {
|
||||||
|
return new NextResponse("Not Found", {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInternalAdminPath(pathname) && process.env.NODE_ENV === "production" && !isAdminRequest) {
|
||||||
return new NextResponse("Not Found", {
|
return new NextResponse("Not Found", {
|
||||||
status: 404,
|
status: 404,
|
||||||
});
|
});
|
||||||
@@ -79,8 +103,9 @@ export default async function middleware(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rewriteUrl = request.nextUrl.clone();
|
const rewriteUrl = request.nextUrl.clone();
|
||||||
rewriteUrl.pathname = toInternalAdminPath(pathname);
|
rewriteUrl.pathname = toInternalAdminPath(
|
||||||
|
isDevelopmentAdminRequest ? fromDevelopmentAdminPath(pathname) : pathname,
|
||||||
|
);
|
||||||
const response = NextResponse.rewrite(rewriteUrl);
|
const response = NextResponse.rewrite(rewriteUrl);
|
||||||
response.headers.set("X-Robots-Tag", adminRobotsHeaders["X-Robots-Tag"]);
|
response.headers.set("X-Robots-Tag", adminRobotsHeaders["X-Robots-Tag"]);
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
Reference in New Issue
Block a user