1717 * 权限:<AdminGuard required="admin"> 兜底(真正的安全由 pgAdmin 登录把守)。
1818 */
1919
20+ import { useEffect , useState } from "react" ;
2021import Link from "next/link" ;
2122import { 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
3043export default function AdminDatabasePage ( ) {
3144 return (
@@ -36,6 +49,15 @@ export default function AdminDatabasePage() {
3649}
3750
3851function 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