Skip to content

Commit 62f25fe

Browse files
committed
feat(admin): /admin/database 页面嵌入 pgAdmin iframe
管理员用一个主站入口进 pgAdmin 做备份/恢复/查表/跑 SQL,不再打开 api.involutionhell.com:8082 这种裸页面。pgAdmin 本身的 UI 风格跟主站不搭, 但用户明确说"管理员不配享受好 UI",优先接通能力。 - 新增 app/admin/database/page.tsx:AdminGuard 兜底权限,iframe src 走 https://api.involutionhell.com/admin/pgadmin/(可由 NEXT_PUBLIC_PGADMIN_URL 覆盖) - /admin 首页加"数据库管理"入口卡片 真实的权限/流量控制在后端 compose + Caddy 那边(见 involutionhell-backend#12): Caddy 反向代理 /admin/pgadmin/* 到 127.0.0.1:8082,剥 X-Frame-Options, 下发 CSP frame-ancestors 放行 involutionhell.com 主域。
1 parent 6a72d76 commit 62f25fe

2 files changed

Lines changed: 94 additions & 1 deletion

File tree

app/admin/database/page.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"use client";
2+
3+
/**
4+
* /admin/database — 数据库管理后台(iframe 嵌入 pgAdmin)。
5+
*
6+
* 权限:<AdminGuard required="admin"> 兜底,非 admin 直接 403;
7+
* 真正的操作权限由 pgAdmin 自身登录(环境变量里的 PGADMIN_EMAIL / PGADMIN_PASSWORD)
8+
* 把守,前端这层只是"路由可见"。
9+
*
10+
* 流量:
11+
* 浏览器 → involutionhell.com/admin/database
12+
* └─ iframe src="https://api.involutionhell.com/admin/pgadmin/"
13+
* └─ Caddy /admin/pgadmin/* → 127.0.0.1:8082(pgAdmin 容器)
14+
*
15+
* pgAdmin 容器环境里设了 SCRIPT_NAME=/admin/pgadmin,
16+
* Caddy 响应里剥掉 X-Frame-Options 并换成 CSP frame-ancestors 放行本站主域。
17+
*
18+
* 为什么不把 pgAdmin 和主站 UI 做统一风格:
19+
* 用户明确说"管理员不配享受好 UI"——优先把基础能力接通,视觉一致性排最后。
20+
*/
21+
22+
import { AdminGuard } from "../events/AdminGuard";
23+
24+
// pgAdmin 所在路径。默认打 production Caddy,dev 如果要本地联调可以用
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/";
29+
30+
export default function AdminDatabasePage() {
31+
return (
32+
<AdminGuard>
33+
<AdminDatabaseInner />
34+
</AdminGuard>
35+
);
36+
}
37+
38+
function AdminDatabaseInner() {
39+
return (
40+
<main className="pt-24 pb-0 bg-[var(--background)] min-h-screen flex flex-col">
41+
<div className="max-w-6xl w-full mx-auto px-6 lg:px-8 pb-4">
42+
<header className="border-t-4 border-[var(--foreground)] pt-6 mb-4">
43+
<div className="font-mono text-[10px] uppercase tracking-[0.3em] text-neutral-500">
44+
Admin · Database
45+
</div>
46+
<h1 className="font-serif text-2xl md:text-3xl font-black uppercase mt-2 tracking-tight">
47+
数据库管理
48+
</h1>
49+
<p className="mt-2 text-xs text-neutral-600 dark:text-neutral-400 leading-relaxed">
50+
下方嵌入的是 pgAdmin。首次进入要用{" "}
51+
<code className="font-mono text-[11px] bg-neutral-200 dark:bg-neutral-800 px-1">
52+
PGADMIN_EMAIL
53+
</code>{" "}
54+
/{" "}
55+
<code className="font-mono text-[11px] bg-neutral-200 dark:bg-neutral-800 px-1">
56+
PGADMIN_PASSWORD
57+
</code>{" "}
58+
登录(在{" "}
59+
<code className="font-mono text-[11px] bg-neutral-200 dark:bg-neutral-800 px-1">
60+
.env
61+
</code>{" "}
62+
里)。 左树自动预注册了 &ldquo;InvolutionHell (local)&rdquo;
63+
连接,双击即连。 备份/恢复在数据库右键菜单里;定时备份落在{" "}
64+
<code className="font-mono text-[11px] bg-neutral-200 dark:bg-neutral-800 px-1">
65+
Storage → backups/
66+
</code>
67+
68+
</p>
69+
</header>
70+
</div>
71+
72+
{/* iframe 占满剩余视口,便于操作。高度用 calc 减去 header 高度约 220px。 */}
73+
<div className="flex-1 border-t border-[var(--foreground)]">
74+
<iframe
75+
src={PGADMIN_URL}
76+
title="pgAdmin"
77+
className="w-full h-[calc(100vh-220px)] min-h-[600px] border-0"
78+
// 不加 sandbox:pgAdmin 依赖自己的 cookie + localStorage 保持登录态,
79+
// 用 sandbox 会屏蔽掉,功能直接残废。允许同源脚本和 form 提交。
80+
referrerPolicy="no-referrer-when-downgrade"
81+
/>
82+
</div>
83+
</main>
84+
);
85+
}

app/admin/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ function AdminHomeInner() {
4949
href="/admin/events"
5050
badge="Admin+"
5151
/>
52+
<AdminCard
53+
title="数据库管理"
54+
description="嵌入 pgAdmin,用按钮完成备份 / 恢复 / 查表 / 跑 SQL。"
55+
href="/admin/database"
56+
badge="Admin+"
57+
/>
5258
{isSuperadmin && (
5359
<AdminCard
5460
title="用户管理"
@@ -88,7 +94,9 @@ function AdminCard({
8894
>
8995
<div
9096
className={`font-mono text-[10px] uppercase tracking-[0.3em] mb-2 ${
91-
accent ? "text-[#CC0000] group-hover:text-white" : "text-neutral-500 group-hover:text-[var(--background)]"
97+
accent
98+
? "text-[#CC0000] group-hover:text-white"
99+
: "text-neutral-500 group-hover:text-[var(--background)]"
92100
}`}
93101
>
94102
{badge}

0 commit comments

Comments
 (0)