Skip to content

Commit 7f75b76

Browse files
committed
feat(admin/database): hostname=localhost 时按钮自动指本地 pgAdmin
开发时访问 localhost:3010/admin/database 点按钮会直接打 prod api.involutionhell.com,需要 cookie 但 localhost 登录时 cookie 写不到 .involutionhell.com 域,只能卡 401。 改成客户端挂载后读 window.location.hostname: - localhost / 127.0.0.1 → http://localhost:8082/admin/pgadmin/ (要求开发者先 ssh -L 8082:127.0.0.1:8082 server 引端口) - 其他 → 原来的公网 URL(走 Caddy forward_auth 链) NEXT_PUBLIC_PGADMIN_URL 仍然最高优先级,想覆盖任何时候都能覆盖。 useEffect 里 setState 走 Promise.resolve 异步化,绕开 React "cascading renders" lint 规则。
1 parent 1a485d6 commit 7f75b76

1 file changed

Lines changed: 31 additions & 8 deletions

File tree

app/admin/database/page.tsx

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,28 @@
1717
* 权限:<AdminGuard required="admin"> 兜底(真正的安全由 pgAdmin 登录把守)。
1818
*/
1919

20+
import { useEffect, useState } from "react";
2021
import Link from "next/link";
2122
import { AdminGuard } from "../events/AdminGuard";
2223

23-
// pgAdmin 的公网地址。Vercel 生产走 Caddy 代理的 api.involutionhell.com,
24-
// 本地 dev 需要手动 SSH port-forward 8082 到自己机器、然后设
25-
// NEXT_PUBLIC_PGADMIN_URL=http://localhost:8082/admin/pgadmin/。
26-
const PGADMIN_URL =
27-
process.env.NEXT_PUBLIC_PGADMIN_URL ??
28-
"https://api.involutionhell.com/admin/pgadmin/";
24+
// pgAdmin URL 选择逻辑(客户端运行时决定,否则 SSR 拿不到 hostname):
25+
// 1. NEXT_PUBLIC_PGADMIN_URL 显式覆盖(最高优先级)
26+
// 2. 浏览器 hostname 是 localhost / 127.0.0.1 → 走本地 http://localhost:8082/admin/pgadmin/
27+
// (要求开发者先 ssh -L 8082:127.0.0.1:8082 server 把端口引到本机)
28+
// 3. 其他情况 → 公网入口 https://api.involutionhell.com/admin/pgadmin/
29+
// (需要 Caddy forward_auth + cookie,prod 正常使用路径)
30+
const PROD_PGADMIN_URL = "https://api.involutionhell.com/admin/pgadmin/";
31+
const LOCAL_PGADMIN_URL = "http://localhost:8082/admin/pgadmin/";
32+
33+
function pickPgadminUrl(hostname: string | null): string {
34+
if (process.env.NEXT_PUBLIC_PGADMIN_URL) {
35+
return process.env.NEXT_PUBLIC_PGADMIN_URL;
36+
}
37+
if (hostname === "localhost" || hostname === "127.0.0.1") {
38+
return LOCAL_PGADMIN_URL;
39+
}
40+
return PROD_PGADMIN_URL;
41+
}
2942

3043
export default function AdminDatabasePage() {
3144
return (
@@ -36,6 +49,15 @@ export default function AdminDatabasePage() {
3649
}
3750

3851
function AdminDatabaseInner() {
52+
// 客户端挂载后拿 hostname 选 URL。首屏 SSR 先渲染 prod URL 占位,useEffect
53+
// 里按实际 hostname 刷成 localhost 版(如果需要)。dev 不做 SSR 不影响。
54+
// setState 走 Promise.resolve 异步化,避开 "cascading renders" lint 规则。
55+
const [pgadminUrl, setPgadminUrl] = useState(PROD_PGADMIN_URL);
56+
useEffect(() => {
57+
const next = pickPgadminUrl(window.location.hostname);
58+
Promise.resolve().then(() => setPgadminUrl(next));
59+
}, []);
60+
3961
return (
4062
<main className="pt-32 pb-16 bg-[var(--background)] min-h-screen">
4163
<div className="max-w-3xl mx-auto px-6 lg:px-8">
@@ -65,7 +87,8 @@ function AdminDatabaseInner() {
6587
</header>
6688

6789
<Link
68-
href={PGADMIN_URL}
90+
key={pgadminUrl}
91+
href={pgadminUrl}
6992
target="_blank"
7093
rel="noopener noreferrer"
7194
className="block border border-[var(--foreground)] p-8 hover:bg-[var(--foreground)] hover:text-[var(--background)] transition-colors group"
@@ -78,7 +101,7 @@ function AdminDatabaseInner() {
78101
打开 pgAdmin →
79102
</h2>
80103
<p className="text-sm leading-relaxed opacity-80 font-mono break-all">
81-
{PGADMIN_URL}
104+
{pgadminUrl}
82105
</p>
83106
</Link>
84107

0 commit comments

Comments
 (0)