'use client'; import { useState, useEffect } from 'react'; import { getSession, clearSession, hasPermission, type Role, type Permission } from '@/lib/store/auth-store'; import { SettingsSection } from './SettingsSection'; import { Icons } from '@/components/ui/Icon'; import { LogOut, Shield, Info } from 'lucide-react'; interface AccountInfo { name: string; role: Role; customPermissions?: string[]; } interface ConfigEntry { password: string; name: string; role: Role; customPermissions: Permission[]; } const ALL_PERMISSIONS: { key: Permission; label: string }[] = [ { key: 'source_management', label: '视频源管理' }, { key: 'account_management', label: '账户管理' }, { key: 'danmaku_api', label: '弹幕 API' }, { key: 'data_management', label: '数据管理' }, { key: 'player_settings', label: '播放器设置' }, { key: 'danmaku_appearance', label: '弹幕外观' }, { key: 'iptv_access', label: 'IPTV 访问' }, { key: 'iptv_source_management', label: 'IPTV 自定义源管理' }, { key: 'iptv_builtin_sources', label: 'IPTV 内置源' }, ]; const ROLE_PERMISSIONS: Record = { super_admin: ['source_management', 'account_management', 'danmaku_api', 'data_management', 'player_settings', 'danmaku_appearance', 'view_settings', 'iptv_access', 'iptv_source_management', 'iptv_builtin_sources'], admin: ['player_settings', 'danmaku_appearance', 'view_settings', 'iptv_access', 'iptv_source_management', 'iptv_builtin_sources'], viewer: ['view_settings'], }; export function AccountSettings() { const [session, setSessionState] = useState>(null); const [hasAuth, setHasAuth] = useState(false); const [accounts, setAccounts] = useState([]); const [showConfigGen, setShowConfigGen] = useState(false); const [configEntries, setConfigEntries] = useState([]); const [copied, setCopied] = useState(false); const [removedAccounts, setRemovedAccounts] = useState>(new Set()); const [hasAdminPassword, setHasAdminPassword] = useState(false); useEffect(() => { setSessionState(getSession()); fetch('/api/auth') .then(res => res.json()) .then(data => setHasAuth(data.hasAuth)) .catch(() => { }); // Fetch account list for admins fetch('/api/auth/accounts') .then(res => res.json()) .then(data => { if (data.accounts) setAccounts(data.accounts); if (data.hasAdminPassword) setHasAdminPassword(data.hasAdminPassword); }) .catch(() => { }); }, []); const handleLogout = () => { clearSession(); window.location.reload(); }; const canManageAccounts = hasPermission('account_management'); // Config generator helpers const addConfigEntry = () => { setConfigEntries([...configEntries, { password: '', name: '', role: 'viewer', customPermissions: [] }]); }; const updateConfigEntry = (index: number, field: keyof ConfigEntry, value: string) => { const updated = [...configEntries]; updated[index] = { ...updated[index], [field]: value }; setConfigEntries(updated); }; const toggleConfigPermission = (index: number, perm: Permission) => { const updated = [...configEntries]; const entry = updated[index]; const perms = entry.customPermissions || []; if (perms.includes(perm)) { entry.customPermissions = perms.filter(p => p !== perm); } else { entry.customPermissions = [...perms, perm]; } setConfigEntries(updated); }; const removeConfigEntry = (index: number) => { setConfigEntries(configEntries.filter((_, i) => i !== index)); }; const generateAccountsString = () => { return configEntries .filter(e => e.password.trim() && e.name.trim()) .map(e => { let str = `${e.password}:${e.name}`; const hasCustomPerms = e.customPermissions && e.customPermissions.length > 0; if (e.role !== 'viewer' || hasCustomPerms) { str += ':' + e.role; } if (hasCustomPerms) { str += ':' + e.customPermissions.join('|'); } return str; }) .join(','); }; const handleCopy = () => { const str = generateAccountsString(); navigator.clipboard.writeText(str).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); }; // Load existing accounts into config generator (without passwords) const loadExistingAccounts = () => { // Filter out removed accounts and the standalone admin password account const existingEntries: ConfigEntry[] = accounts .filter((_, i) => !removedAccounts.has(i)) .filter(a => !(a.name === '超级管理员' && hasAdminPassword)) .map(a => ({ password: '', name: a.name, role: a.role, customPermissions: (a.customPermissions || []) as Permission[], })); setConfigEntries(existingEntries); setShowConfigGen(true); }; // Remove account from visible list and track removal const handleRemoveAccount = (index: number) => { setRemovedAccounts(prev => { const next = new Set(prev); next.add(index); return next; }); }; // Get visible accounts (excluding removed ones) const visibleAccounts = accounts.filter((_, i) => !removedAccounts.has(i)); if (!hasAuth && !session) return null; return (
{/* Current User Info */} {session && (
{session.name.charAt(0)}

{session.name}

{session.role === 'super_admin' ? '超级管理员' : session.role === 'admin' ? '管理员' : '观众'}
)} {/* Account List (Account managers only) */} {canManageAccounts && visibleAccounts.length > 0 && (

已配置的账户

{accounts.map((account, index) => { if (removedAccounts.has(index)) return null; return (
{account.name.charAt(0)}
{account.name}
{account.role === 'super_admin' ? '超级管理员' : account.role === 'admin' ? '管理员' : '观众'}
); })}
{/* Notice when accounts have been removed */} {removedAccounts.size > 0 && (

已标记移除 {removedAccounts.size} 个账户。请使用下方配置生成器生成新的 ACCOUNTS 环境变量值并更新部署配置。

)}
)} {/* Config Generator (Account managers only) */} {canManageAccounts && (

配置生成器

{!showConfigGen && accounts.length > 0 && ( )}
{showConfigGen && (

添加账户条目后,将生成的 ACCOUNTS 环境变量值复制到部署配置中。 {configEntries.some(e => !e.password && e.name) && ( 注意:导入的账户需要重新输入密码。 )}

{/* Entry List */} {configEntries.map((entry, index) => (
updateConfigEntry(index, 'password', e.target.value)} className={`flex-1 px-3 py-1.5 bg-[var(--glass-bg)] border rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] placeholder:text-[var(--text-color-secondary)]/50 focus:outline-none focus:border-[var(--accent-color)] ${!entry.password && entry.name ? 'border-amber-500/50' : 'border-[var(--glass-border)]' }`} /> updateConfigEntry(index, 'name', e.target.value)} className="flex-1 px-3 py-1.5 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] text-sm text-[var(--text-color)] placeholder:text-[var(--text-color-secondary)]/50 focus:outline-none focus:border-[var(--accent-color)]" />
{/* Custom permissions: show only those not in the selected role */} {(() => { const rolePerms = ROLE_PERMISSIONS[entry.role] || []; const extraPerms = ALL_PERMISSIONS.filter(p => !rolePerms.includes(p.key)); if (extraPerms.length === 0) return null; return (
{extraPerms.map(p => { const checked = entry.customPermissions?.includes(p.key) ?? false; return ( ); })}
); })()}
))} {/* Generated Output */} {configEntries.length > 0 && configEntries.some(e => e.password && e.name) && (
{generateAccountsString()}
)}
)}
)} {/* Config Notice */}

账户通过环境变量配置:

ADMIN_PASSWORD — 单管理员密码

ACCOUNTS — 多账户(密码:名称[:角色[:权限1|权限2]])

); }