[dev] support reverse proxy subpaths

This commit is contained in:
gaoxizhi
2026-07-29 00:17:41 +08:00
parent 1f1b45767a
commit 1741c6c957
9 changed files with 26 additions and 8 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<link rel="icon" type="image/svg+xml" href="%BASE_URL%logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex" />
<title>Certimate - Your Trusted Partner in SSL Automation</title>
+3 -1
View File
@@ -1,3 +1,5 @@
import { resolveAppPath } from "@/utils/url";
interface BaseProvider<P> {
type: P;
name: string;
@@ -296,7 +298,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
{
type: type,
name: name,
icon: icon,
icon: resolveAppPath(icon),
usages: usages,
builtin: builtin === "builtin",
},
+2 -1
View File
@@ -25,6 +25,7 @@ import { APP_DOCUMENT_URL, APP_REPO_URL } from "@/domain/app";
import { useTriggerElement } from "@/hooks";
import { getAuthStore } from "@/repository/admin";
import { isBrowserHappy } from "@/utils/browser";
import { resolveAppPath } from "@/utils/url";
const ConsoleLayout = () => {
const navigate = useNavigate();
@@ -231,7 +232,7 @@ const SiderMenu = memo(({ collapsed, onSelect }: { collapsed?: boolean; onSelect
<>
<div className="h-[64px] w-full overflow-hidden px-4 py-2 max-md:py-0">
<div className="flex size-full items-center justify-around gap-2">
<img src="/logo.svg" className="size-[36px]" />
<img src={resolveAppPath("/logo.svg")} className="size-[36px]" />
<Show when={!collapsed}>
<span className="w-[81px] truncate text-base leading-[64px] font-semibold">Certimate</span>
<AppVersion.LinkButton className="text-xs" />
+2 -1
View File
@@ -14,6 +14,7 @@ import { useAntdForm, useBrowserTheme } from "@/hooks";
import { authWithPassword } from "@/repository/admin";
import { unwrapErrMsg } from "@/utils/error";
import { resolveAppPath } from "@/utils/url";
const Login = () => {
const navigage = useNavigate();
@@ -74,7 +75,7 @@ const Login = () => {
<Card className="w-120 max-w-full rounded-md shadow-md max-sm:size-full max-sm:rounded-none">
<div className="px-4 py-8">
<div className="mb-12 flex items-center justify-center">
<img src="/logo.svg" className="w-16" />
<img src={resolveAppPath("/logo.svg")} className="w-16" />
</div>
<Form {...formProps} form={formInst} disabled={formPending} layout="vertical" validateTrigger="onBlur">
+2 -1
View File
@@ -6,6 +6,7 @@ import { produce } from "immer";
import { useAppLocaleMenuItems } from "@/components/AppLocale";
import { useAppThemeMenuItems } from "@/components/AppTheme";
import { useAppSettings, useBrowserTheme } from "@/hooks";
import { resolveAppPath } from "@/utils/url";
const SettingsAppearance = () => {
const { t } = useTranslation();
@@ -58,7 +59,7 @@ const SettingsAppearanceTheme = ({ className, style }: { className?: string; sty
{themeItems.map((item) => (
<div className="relative max-w-44 flex-1/3 max-md:flex-1/2 max-sm:flex-1" key={item.key}>
<div className="overflow-hidden rounded-lg border border-solid" style={{ borderColor: themeToken.colorBorder }}>
<img className="mb-2 w-full" src={`/imgs/themes/${item.key}.png`} />
<img className="mb-2 w-full" src={resolveAppPath(`/imgs/themes/${item.key}.png`)} />
<div className="mb-2 px-2">
<Radio value={item.key}>{item.label}</Radio>
</div>
+3 -2
View File
@@ -16,6 +16,7 @@ import {
} from "@/domain/workflow";
import { save as saveWorkflow } from "@/repository/workflow";
import { unwrapErrMsg } from "@/utils/error";
import { resolveAppPath } from "@/utils/url";
const TEMPLATE_KEY_BLANK = "blank" as const;
const TEMPLATE_KEY_STANDARD = "standard" as const;
@@ -34,13 +35,13 @@ const WorkflowNew = () => {
key: TEMPLATE_KEY_STANDARD,
name: t("workflow.new.templates.template.standard.title"),
description: t("workflow.new.templates.template.standard.description"),
image: "/imgs/workflow/tpl-standard.png",
image: resolveAppPath("/imgs/workflow/tpl-standard.png"),
},
{
key: TEMPLATE_KEY_CERTTEST,
name: t("workflow.new.templates.template.certtest.title"),
description: t("workflow.new.templates.template.certtest.description"),
image: "/imgs/workflow/tpl-certtest.png",
image: resolveAppPath("/imgs/workflow/tpl-certtest.png"),
},
];
const [templateSelectKey, setTemplateSelectKey] = useState<TemplateKeys>();
+3 -1
View File
@@ -1,9 +1,11 @@
import PocketBase from "pocketbase";
import { APP_BASE_PATH } from "@/utils/url";
let pb: PocketBase;
export const getPocketBase = () => {
if (pb) return pb;
pb = new PocketBase("/");
pb = new PocketBase(APP_BASE_PATH);
pb.afterSend = (res, data) => {
if ((res.status === 401 || res.status === 403) && pb.authStore?.isValid) {
pb.authStore.clear();
+8
View File
@@ -0,0 +1,8 @@
const appBaseUrl = new URL(import.meta.env.BASE_URL, document.baseURI);
// PocketBase 会把自身的 /api 路径拼接到这里,因此基础路径必须以斜杠结尾。
export const APP_BASE_PATH = appBaseUrl.pathname;
export const resolveAppPath = (path: string) => {
return new URL(path.replace(/^\/+/, ""), appBaseUrl).pathname;
};
+2
View File
@@ -52,6 +52,8 @@ export default defineConfig(({ command }) => {
}
return {
// 使用相对基础路径,让同一份构建产物可以部署在根路径或任意反向代理子路径下。
base: "./",
define: {
__APP_VERSION__: JSON.stringify(appVersion),
},