96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import path from "node:path";
|
|
import type { Metadata } from "next";
|
|
import { redirect } from "next/navigation";
|
|
import { auth } from "@/app/(auth)/auth";
|
|
import { PascalEditorFrame } from "@/components/pascal-editor/pascal-editor-frame";
|
|
import { getSiteSettings } from "@/lib/db/queries";
|
|
|
|
const LOCAL_PASCAL_EDITOR_SRC = "/vendor/pascal-editor/index.html";
|
|
const DEV_SELF_HOSTED_PASCAL_EDITOR_SRC = "http://localhost:3002";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
export const metadata: Metadata = {
|
|
title: "建筑编辑",
|
|
};
|
|
export const runtime = "nodejs";
|
|
|
|
export default async function PascalEditorPage() {
|
|
const [session, siteSettings] = await Promise.all([auth(), getSiteSettings()]);
|
|
if (!session?.user?.id) {
|
|
redirect("/login?callbackUrl=%2Fpascal-editor");
|
|
}
|
|
|
|
const configuredSrc = normalizePascalEditorUrl(
|
|
process.env.NEXT_PUBLIC_PASCAL_EDITOR_URL ||
|
|
process.env.PASCAL_EDITOR_URL ||
|
|
""
|
|
);
|
|
const hasLocalEditor = existsSync(
|
|
path.join(process.cwd(), "public/vendor/pascal-editor/index.html")
|
|
);
|
|
const shouldUseDevSelfHosted =
|
|
process.env.NODE_ENV === "development" && !configuredSrc && !hasLocalEditor;
|
|
const hasDevSelfHostedEditor =
|
|
shouldUseDevSelfHosted &&
|
|
(await isHttpUrlReachable(DEV_SELF_HOSTED_PASCAL_EDITOR_SRC));
|
|
const src =
|
|
configuredSrc ||
|
|
(hasLocalEditor ? LOCAL_PASCAL_EDITOR_SRC : "") ||
|
|
(hasDevSelfHostedEditor ? DEV_SELF_HOSTED_PASCAL_EDITOR_SRC : "") ||
|
|
LOCAL_PASCAL_EDITOR_SRC;
|
|
const isReady = Boolean(configuredSrc || hasLocalEditor || hasDevSelfHostedEditor);
|
|
|
|
return (
|
|
<PascalEditorFrame
|
|
fallbackSrc={LOCAL_PASCAL_EDITOR_SRC}
|
|
isReady={isReady}
|
|
sourceLabel={
|
|
hasDevSelfHostedEditor
|
|
? "本机 Pascal Editor"
|
|
: configuredSrc
|
|
? "Pascal Editor"
|
|
: "本地 Pascal Editor"
|
|
}
|
|
selfHostedSrc={DEV_SELF_HOSTED_PASCAL_EDITOR_SRC}
|
|
siteLogo={siteSettings.siteLogo}
|
|
siteLogoDark={siteSettings.siteLogoDark}
|
|
siteName={siteSettings.siteName}
|
|
src={src}
|
|
user={session.user}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function normalizePascalEditorUrl(value: string) {
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
|
|
if (trimmed.startsWith("/")) {
|
|
return trimmed;
|
|
}
|
|
|
|
try {
|
|
const url = new URL(trimmed);
|
|
return url.protocol === "http:" || url.protocol === "https:"
|
|
? url.toString()
|
|
: "";
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
async function isHttpUrlReachable(value: string) {
|
|
try {
|
|
const response = await fetch(value, {
|
|
cache: "no-store",
|
|
signal: AbortSignal.timeout(3000),
|
|
});
|
|
return response.ok;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|