// POST /api/admin/sync-users — 将用户同步到指定站点(docker exec 写 DB) import { NextResponse } from 'next/server' import { cookies } from 'next/headers' import { execFileSync } from 'child_process' import { verifySharedJwt } from '@/lib/jwt' import { isLldapAdmin } from '@/lib/ldap' import { queryLldap, esc } from '@/lib/lldap-db' const SITES: Record = { assets: ['assets-ai', '/app/data/assets.db'], issue: ['issue-ai', '/app/data/issue.db'], monitor: ['monitor-ai', '/app/data/monitor.db'], } function syncToSite(container: string, dbPath: string, username: string, displayName: string, email: string): boolean { try { const su = esc(username); const sd = esc(displayName); const se = esc(email) const d = new Date() const now = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}` execFileSync('docker', ['exec', '-i', container, 'sqlite3', dbPath], { input: `INSERT OR IGNORE INTO users (username, display_name, email, role, is_active, created_at, updated_at) VALUES ('${su}', '${sd}', '${se}', 'viewer', 1, '${now}', '${now}');`, timeout: 5000, }) return true } catch { return false } } export async function POST(request: Request) { const cookieStore = await cookies() const token = cookieStore.get('tlyq_session')?.value if (!token) return NextResponse.json({ error: '未登录' }, { status: 401 }) const session = verifySharedJwt(token) if (!session || !(await isLldapAdmin(session.username))) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) } const { usernames, targetSite } = await request.json() if (!usernames || !Array.isArray(usernames) || usernames.length === 0) { return NextResponse.json({ error: '请指定要同步的用户' }, { status: 400 }) } // 从 LLDAP 获取用户信息 const userInfo: Record = {} try { const safeNames = usernames.map(u => `'${esc(u)}'`).join(',') const out = queryLldap(`SELECT user_id, display_name, email FROM users WHERE user_id IN (${safeNames})`) out.split('\n').filter(Boolean).forEach(line => { const [uid, dn, em] = line.split('|') userInfo[uid] = { displayName: dn || uid, email: em || '' } }) } catch {} const sites = targetSite && SITES[targetSite] ? { [targetSite]: SITES[targetSite] } : targetSite ? null : SITES if (!sites) return NextResponse.json({ error: '无效的站点' }, { status: 400 }) const results: Record> = {} for (const [site, [cName, cPath]] of Object.entries(sites)) { results[site] = {} for (const username of usernames) { const info = userInfo[username] || { displayName: username, email: '' } results[site][username] = syncToSite(cName, cPath, username, info.displayName, info.email) } } return NextResponse.json({ success: true, results }) }