From 14abdff875f5cfc23eafb7135580625a9ec5b8ad Mon Sep 17 00:00:00 2001 From: aiyimickey <39365912+aiyimickey@users.noreply.github.com> Date: Mon, 18 May 2026 17:33:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=88=B0=20assets/issue=20+=20=E8=87=AA?= =?UTF-8?q?=E9=80=82=E5=BA=94=E5=B8=83=E5=B1=80=20+=20=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=90=8E=E8=87=AA=E5=8A=A8=E6=89=B9=E9=87=8F=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api/auth/me PUT + api/admin/users PATCH:写 LLDAP 后同步更新 assets/issue SQLite - deploy-ai.sh:OA 部署后自动执行批量 email 同步(新服务器首次部署填充历史数据) - scripts/sync-emails-to-sites.js:批量同步工具脚本 - 全站自适应:移除 maxWidth 硬限制,Form 用 auto-fit grid,表格 overflow-x auto --- scripts/sync-emails-to-sites.js | 37 ++++++++++++++++++++++ src/app/admin/create-user/page.tsx | 11 ++++--- src/app/admin/create-user/role-manager.tsx | 4 +-- src/app/api/admin/users/route.ts | 9 ++++++ src/app/api/auth/me/route.ts | 9 ++++++ src/components/HeaderUI.tsx | 2 +- 6 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 scripts/sync-emails-to-sites.js diff --git a/scripts/sync-emails-to-sites.js b/scripts/sync-emails-to-sites.js new file mode 100644 index 0000000..14ed6a1 --- /dev/null +++ b/scripts/sync-emails-to-sites.js @@ -0,0 +1,37 @@ +// 批量同步 LLDAP email → assets / issue 本地用户表 +// 每次 OA 部署后自动执行,确保新服务器历史数据也能填充 +const { exec } = require('child_process') +const { promisify } = require('util') +const e = promisify(exec) + +const SITES = [ + { name: 'assets', db: process.env.ASSETS_DB_PATH || '/data/other-sites/assets/assets.db' }, + { name: 'issue', db: process.env.ISSUE_DB_PATH || '/data/other-sites/issue/issue.db' }, +] + +async function main() { + const r = await e( + `docker exec lldap sqlite3 /data/users.db "SELECT user_id, email FROM users WHERE email != '';"` + ) + const lines = r.stdout.trim().split('\n').filter(Boolean) + let synced = 0 + + for (const line of lines) { + const [user, mail] = line.split('|') + const su = user.replace(/'/g, "''") + const sm = (mail || '').replace(/'/g, "''") + + for (const site of SITES) { + try { + await e( + `sqlite3 "${site.db}" "UPDATE users SET email = '${sm}', updated_at = datetime('now', '+8 hours') WHERE username = '${su}';"` + ) + } catch {} + } + synced++ + console.log(` ${user} → ${mail}`) + } + console.log(`已同步 ${synced} 个用户邮箱`) +} + +main().catch(err => { console.error(err.message); process.exit(1) }) diff --git a/src/app/admin/create-user/page.tsx b/src/app/admin/create-user/page.tsx index 2c443d9..fc221d9 100644 --- a/src/app/admin/create-user/page.tsx +++ b/src/app/admin/create-user/page.tsx @@ -9,10 +9,11 @@ interface LdapUser { username: string; email: string; displayName: string; creat const s = { wrap: { minHeight: '100vh', background: 'var(--bg)' } as React.CSSProperties, - main: { padding: '32px 40px', maxWidth: 1440 } as React.CSSProperties, - tabBar: { display: 'flex', gap: 0, borderBottom: '1px solid var(--border)', marginBottom: 32 } as React.CSSProperties, + main: { padding: 'clamp(16px, 3vw, 48px)' } as React.CSSProperties, + tabBar: { display: 'flex', gap: 0, borderBottom: '1px solid var(--border)', marginBottom: 32, overflowX: 'auto' as any, whiteSpace: 'nowrap' as any } as React.CSSProperties, content: {} as React.CSSProperties, - grid2: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 } as React.CSSProperties, + grid2: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 20 } as React.CSSProperties, + tableWrap: { overflowX: 'auto' as any } as React.CSSProperties, field: { marginBottom: 20 } as React.CSSProperties, label: { display: 'block', fontSize: 13, fontWeight: 500, color: 'var(--text-secondary)', marginBottom: 6 } as React.CSSProperties, input: { width: '100%', height: 44, padding: '0 14px', border: '1px solid var(--border)', borderRadius: 8, background: 'var(--bg-card)', color: 'var(--text)', fontSize: 14, outline: 'none', boxSizing: 'border-box' as any }, @@ -200,7 +201,7 @@ export default function AdminUsersPage() { {users.length === 0 ? (

加载中...

) : ( - +
@@ -238,7 +239,7 @@ export default function AdminUsersPage() { ))} -
用户名
+ )} )} diff --git a/src/app/admin/create-user/role-manager.tsx b/src/app/admin/create-user/role-manager.tsx index 8d15763..14e26a9 100644 --- a/src/app/admin/create-user/role-manager.tsx +++ b/src/app/admin/create-user/role-manager.tsx @@ -95,7 +95,7 @@ export default function RoleManager({ setResult }: Props) { - +
@@ -129,7 +129,7 @@ export default function RoleManager({ setResult }: Props) { ))} -
用户名显示名邮箱资产管理工单跟踪
+ ) } diff --git a/src/app/api/admin/users/route.ts b/src/app/api/admin/users/route.ts index 14a2f97..8d621ee 100644 --- a/src/app/api/admin/users/route.ts +++ b/src/app/api/admin/users/route.ts @@ -99,6 +99,15 @@ export async function PATCH(request: Request) { { timeout: 5000 } ) + // 同步更新 assets / issue 本地用户表 + const assetsDb = process.env.ASSETS_DB_PATH || '/Users/niuniu/programs/docker/assets-ai/data/assets.db' + const issueDb = process.env.ISSUE_DB_PATH || '/Users/niuniu/programs/docker/issue-ai/data/issue.db' + for (const dbPath of [assetsDb, issueDb]) { + try { + await execAsync(`sqlite3 "${dbPath}" "UPDATE users SET email = '${safeEmail}', updated_at = datetime('now', '+8 hours') WHERE username = '${safeUser}';"`, { timeout: 3000 }) + } catch {} + } + return NextResponse.json({ success: true, username, email: email || '' }) } catch (e) { return NextResponse.json({ error: '修改失败' }, { status: 500 }) diff --git a/src/app/api/auth/me/route.ts b/src/app/api/auth/me/route.ts index a7943df..5ef8da0 100644 --- a/src/app/api/auth/me/route.ts +++ b/src/app/api/auth/me/route.ts @@ -65,6 +65,15 @@ export async function PUT(request: Request) { { timeout: 5000 } ) + // 同步更新 assets / issue 本地用户表 + const assetsDb = process.env.ASSETS_DB_PATH || '/Users/niuniu/programs/docker/assets-ai/data/assets.db' + const issueDb = process.env.ISSUE_DB_PATH || '/Users/niuniu/programs/docker/issue-ai/data/issue.db' + for (const dbPath of [assetsDb, issueDb]) { + try { + await execAsync(`sqlite3 "${dbPath}" "UPDATE users SET email = '${safeEmail}', updated_at = datetime('now', '+8 hours') WHERE username = '${safeUser}';"`, { timeout: 3000 }) + } catch {} + } + return NextResponse.json({ success: true, email: email || '' }) } catch (e) { const msg = e instanceof Error ? e.message : '修改失败' diff --git a/src/components/HeaderUI.tsx b/src/components/HeaderUI.tsx index db74203..2fff8ea 100644 --- a/src/components/HeaderUI.tsx +++ b/src/components/HeaderUI.tsx @@ -12,7 +12,7 @@ export default function HeaderUI({ displayName, isAdmin, backLabel }: Props) { position: 'sticky', top: 0, zIndex: 50, height: 56, background: 'var(--bg-card)', borderBottom: '1px solid var(--border)', }}> -
+
{/* 左侧 */} {backLabel ? (