refactor: 拆分SystemManager.tsx(569→308行)提取useLdapManagement自定义Hook封装全部LDAP状态与业务逻辑
This commit is contained in:
1 parent
d234cb4b89
commit
019a9d13f3
2 files changed
+359
-284
No files matched your search
@@ -1,15 +1,15 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { toast } from 'sonner';
|
|
||||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { CheckCircle, XCircle, Users, Shield, FileCode, Settings } from 'lucide-react';
|
import { CheckCircle, XCircle, Users, Shield, FileCode, Settings } from 'lucide-react';
|
||||||
import { userApi, ldapApi } from '@/services/api';
|
import { userApi } from '@/services/api';
|
||||||
import type { Role } from '@/types';
|
import type { Role } from '@/types';
|
||||||
import { SystemUserTab } from './SystemUserTab';
|
import { SystemUserTab } from './SystemUserTab';
|
||||||
import { SystemRolesTab } from './SystemRolesTab';
|
import { SystemRolesTab } from './SystemRolesTab';
|
||||||
import { SystemLogsTab } from './SystemLogsTab';
|
import { SystemLogsTab } from './SystemLogsTab';
|
||||||
import { SystemSettingsTab, type LdapFormData } from './SystemSettingsTab';
|
import { SystemSettingsTab } from './SystemSettingsTab';
|
||||||
|
import { useLdapManagement } from './useLdapManagement';
|
||||||
import type { UserFormData } from './UserFormDialog';
|
import type { UserFormData } from './UserFormDialog';
|
||||||
|
|
||||||
// 后端用户数据转换为前端兼容格式
|
// 后端用户数据转换为前端兼容格式
|
||||||
@@ -49,48 +49,11 @@ export function SystemManager() {
|
|||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [form, setForm] = useState<UserFormData>({ ...emptyForm });
|
const [form, setForm] = useState<UserFormData>({ ...emptyForm });
|
||||||
|
|
||||||
// 批量导入弹窗
|
// CSV 批量导入弹窗
|
||||||
const [importDialogOpen, setImportDialogOpen] = useState<'csv' | 'ldap' | null>(null);
|
const [importDialogOpen, setImportDialogOpen] = useState<'csv' | 'ldap' | null>(null);
|
||||||
const [importPreview, setImportPreview] = useState<any[]>([]);
|
const [importPreview, setImportPreview] = useState<any[]>([]);
|
||||||
const [importing, setImporting] = useState(false);
|
|
||||||
const [importResult, setImportResult] = useState<any>(null);
|
const [importResult, setImportResult] = useState<any>(null);
|
||||||
|
|
||||||
// LDAP 用户列表
|
|
||||||
const [ldapUsers, setLdapUsers] = useState<any[]>([]);
|
|
||||||
const [ldapSelected, setLdapSelected] = useState<Set<string>>(new Set());
|
|
||||||
const [ldapLoading, setLdapLoading] = useState(false);
|
|
||||||
const [ldapImportOUs, setLdapImportOUs] = useState<any[]>([]);
|
|
||||||
const [ldapImportOUDN, setLdapImportOUDN] = useState('');
|
|
||||||
|
|
||||||
// LDAP 配置
|
|
||||||
const [ldapConfig, setLdapConfig] = useState<any>(null);
|
|
||||||
const [ldapForm, setLdapForm] = useState<LdapFormData>({
|
|
||||||
ldap_host: '', ldap_port: 389, use_ldaps: false,
|
|
||||||
admin_dn: '', admin_password: '', base_dn: '', search_filter: '',
|
|
||||||
});
|
|
||||||
const [ldapSaving, setLdapSaving] = useState(false);
|
|
||||||
const [ldapTesting, setLdapTesting] = useState(false);
|
|
||||||
const [ldapTestResult, setLdapTestResult] = useState<string | null>(null);
|
|
||||||
const [ldapMgmtOpen, setLdapMgmtOpen] = useState(false);
|
|
||||||
const [importFeedback, setImportFeedback] = useState<string | null>(null);
|
|
||||||
const [importResultOpen, setImportResultOpen] = useState(false);
|
|
||||||
|
|
||||||
// LDAP 账号管理
|
|
||||||
const [ldapOUs, setLdapOUs] = useState<any[]>([]);
|
|
||||||
const [selectedOUDN, setSelectedOUDN] = useState('');
|
|
||||||
const [selectedLDAPUsers, setSelectedLDAPUsers] = useState<string[]>([]);
|
|
||||||
const [createOUOpen, setCreateOUOpen] = useState(false);
|
|
||||||
const [createUserOpen, setCreateUserOpen] = useState(false);
|
|
||||||
const [ouForm, setOuForm] = useState({ ou_name: '', parent_dn: '', description: '' });
|
|
||||||
const [newUser, setNewUser] = useState({ username: '', password: '', display_name: '', email: '', ou_dn: '' });
|
|
||||||
const [ldapUserTestOpen, setLdapUserTestOpen] = useState(false);
|
|
||||||
const [ldapTestUser, setLdapTestUser] = useState('');
|
|
||||||
const [ldapTestPass, setLdapTestPass] = useState('');
|
|
||||||
const [ldapUserTesting, setLdapUserTesting] = useState(false);
|
|
||||||
const [ldapUserTestResult, setLdapUserTestResult] = useState<any>(null);
|
|
||||||
const [editLdapUser, setEditLdapUser] = useState<any>(null);
|
|
||||||
const [editLdapForm, setEditLdapForm] = useState({ cn: '', mail: '', phone: '', title: '' });
|
|
||||||
|
|
||||||
const loadUsers = async () => {
|
const loadUsers = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -105,16 +68,18 @@ export function SystemManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// LDAP 状态全部由 hook 管理(在 loadUsers 之后调用避免 TDZ) // Logan.Li
|
||||||
|
const ldap = useLdapManagement(loadUsers);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUsers();
|
loadUsers();
|
||||||
loadLdapConfig();
|
ldap.loadLdapConfig();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// LDAP 导入 Dialog 打开时加载 OU 和用户
|
// LDAP 导入 Dialog 打开时加载 OU 和用户
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (importDialogOpen === 'ldap') {
|
if (importDialogOpen === 'ldap') {
|
||||||
ldapApi.listOUs().then(ous => setLdapImportOUs(Array.isArray(ous) ? ous : [])).catch(() => {});
|
ldap.loadImportDialogData();
|
||||||
loadLdapUsers('');
|
|
||||||
}
|
}
|
||||||
}, [importDialogOpen]);
|
}, [importDialogOpen]);
|
||||||
|
|
||||||
@@ -236,7 +201,7 @@ export function SystemManager() {
|
|||||||
|
|
||||||
// 执行 CSV 批量导入
|
// 执行 CSV 批量导入
|
||||||
const handleBatchImport = async () => {
|
const handleBatchImport = async () => {
|
||||||
setImporting(true);
|
ldap.setImporting(true);
|
||||||
try {
|
try {
|
||||||
const res = await userApi.batchImport({ users: importPreview, skip_existing: true });
|
const res = await userApi.batchImport({ users: importPreview, skip_existing: true });
|
||||||
setImportResult(res);
|
setImportResult(res);
|
||||||
@@ -244,236 +209,10 @@ export function SystemManager() {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setImportResult({ code: 500, message: error?.message || '导入失败' });
|
setImportResult({ code: 500, message: error?.message || '导入失败' });
|
||||||
} finally {
|
} finally {
|
||||||
setImporting(false);
|
ldap.setImporting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载 LDAP 用户列表
|
|
||||||
const loadLdapUsers = async (ouDn?: string) => {
|
|
||||||
setLdapLoading(true);
|
|
||||||
try {
|
|
||||||
const res = await ldapApi.listUsers(ouDn);
|
|
||||||
setLdapUsers(Array.isArray(res) ? res : []);
|
|
||||||
setLdapSelected(new Set());
|
|
||||||
} catch {
|
|
||||||
setLdapUsers([]);
|
|
||||||
} finally {
|
|
||||||
setLdapLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 执行 LDAP 导入
|
|
||||||
const handleLdapImport = async () => {
|
|
||||||
let selected: any[] = [];
|
|
||||||
if (selectedLDAPUsers.length > 0) {
|
|
||||||
selected = ldapUsers.filter((u) => selectedLDAPUsers.includes(u.dn));
|
|
||||||
} else {
|
|
||||||
selected = ldapUsers; // 没有选择则导入全部
|
|
||||||
}
|
|
||||||
if (selected.length === 0) return;
|
|
||||||
setImporting(true);
|
|
||||||
try {
|
|
||||||
const mapped = selected.map((u) => ({
|
|
||||||
username: u.uid || '',
|
|
||||||
nickname: u.cn || '',
|
|
||||||
email: u.mail || '',
|
|
||||||
}));
|
|
||||||
const res = await ldapApi.batchImport(mapped);
|
|
||||||
setImportFeedback(res?.message || `成功导入 ${selected.length} 个用户`);
|
|
||||||
setImportResultOpen(true);
|
|
||||||
setSelectedLDAPUsers([]);
|
|
||||||
await loadUsers();
|
|
||||||
} catch (error: any) {
|
|
||||||
setImportFeedback('导入失败: ' + (error?.message || '未知错误'));
|
|
||||||
setImportResultOpen(true);
|
|
||||||
} finally {
|
|
||||||
setImporting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载 LDAP 配置
|
|
||||||
const loadLdapConfig = async () => {
|
|
||||||
try {
|
|
||||||
const res = await ldapApi.getConfig();
|
|
||||||
if (res && res.ldap_host) {
|
|
||||||
setLdapConfig(res);
|
|
||||||
setLdapForm({
|
|
||||||
ldap_host: res.ldap_host || '',
|
|
||||||
ldap_port: res.ldap_port || 389,
|
|
||||||
use_ldaps: res.use_ldaps || false,
|
|
||||||
admin_dn: res.admin_dn || '',
|
|
||||||
admin_password: '',
|
|
||||||
base_dn: res.base_dn || '',
|
|
||||||
search_filter: res.search_filter || '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// No config yet
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存 LDAP 配置
|
|
||||||
const handleLdapSave = async () => {
|
|
||||||
setLdapSaving(true);
|
|
||||||
try {
|
|
||||||
await ldapApi.updateConfig(ldapForm);
|
|
||||||
await loadLdapConfig();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to save LDAP config:', error);
|
|
||||||
} finally {
|
|
||||||
setLdapSaving(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 测试 LDAP 连接
|
|
||||||
const handleLdapTest = async () => {
|
|
||||||
setLdapTesting(true);
|
|
||||||
setLdapTestResult(null);
|
|
||||||
try {
|
|
||||||
await ldapApi.updateConfig(ldapForm);
|
|
||||||
const res = await ldapApi.testConnection();
|
|
||||||
setLdapTestResult(res?.message || (res?.success ? '连接成功' : '连接失败'));
|
|
||||||
} catch (error: any) {
|
|
||||||
setLdapTestResult(error?.message || '连接失败');
|
|
||||||
} finally {
|
|
||||||
setLdapTesting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// LDAP 账号管理函数
|
|
||||||
const loadLdapOUs = async () => {
|
|
||||||
try {
|
|
||||||
const ous = await ldapApi.listOUs();
|
|
||||||
setLdapOUs(ous || []);
|
|
||||||
// 同时加载根 DN 下的用户
|
|
||||||
if (!selectedOUDN && ldapConfig?.base_dn) {
|
|
||||||
setSelectedOUDN(ldapConfig.base_dn);
|
|
||||||
await loadLdapOUUsers(ldapConfig.base_dn);
|
|
||||||
}
|
|
||||||
} catch (e) { console.error('loadOUs', e); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadLdapOUUsers = async (ouDn?: string) => {
|
|
||||||
try {
|
|
||||||
const users = await ldapApi.listUsers(ouDn);
|
|
||||||
setLdapUsers(users || []);
|
|
||||||
} catch (e) { console.error('loadOUUsers', e); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreateOU = async () => {
|
|
||||||
setLdapSaving(true);
|
|
||||||
try {
|
|
||||||
await ldapApi.createOU(ouForm);
|
|
||||||
setCreateOUOpen(false);
|
|
||||||
setOuForm({ ou_name: '', parent_dn: '', description: '' });
|
|
||||||
await loadLdapOUs();
|
|
||||||
} catch (e: any) {
|
|
||||||
const msg = e?.message || '';
|
|
||||||
if (msg.includes('Already Exists') || msg.includes('68')) {
|
|
||||||
toast.error(`OU "${ouForm.ou_name}" 已存在,请使用其他名称`);
|
|
||||||
} else {
|
|
||||||
toast.error('创建失败: ' + msg);
|
|
||||||
}
|
|
||||||
} finally { setLdapSaving(false); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreateLDAPUser = async () => {
|
|
||||||
setLdapSaving(true);
|
|
||||||
try {
|
|
||||||
await ldapApi.createUser({ ...newUser, ou_dn: newUser.ou_dn || selectedOUDN || ldapConfig?.base_dn });
|
|
||||||
setCreateUserOpen(false);
|
|
||||||
setNewUser({ username: '', password: '', display_name: '', email: '', ou_dn: '' });
|
|
||||||
await loadLdapOUUsers(selectedOUDN);
|
|
||||||
} catch (e: any) { toast.error(e?.message || '创建失败'); }
|
|
||||||
finally { setLdapSaving(false); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteLDAPEntry = async (dn: string, silent = false) => {
|
|
||||||
if (!silent) { const ok = await confirm({ title: '删除 LDAP 条目', description: `确定删除 ${dn}?`, confirmText: '删除', variant: 'destructive' }); if (!ok) return; }
|
|
||||||
try {
|
|
||||||
await ldapApi.deleteUser(dn);
|
|
||||||
if (!silent) await loadLdapOUUsers(selectedOUDN);
|
|
||||||
} catch (e: any) { if (!silent) toast.error(e?.message || '删除失败'); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const openEditLdapUser = (user: any) => {
|
|
||||||
setEditLdapUser(user);
|
|
||||||
setEditLdapForm({ cn: user.cn || '', mail: user.mail || '', phone: user.phone || '', title: user.title || '' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdateLdapUser = async () => {
|
|
||||||
if (!editLdapUser) return;
|
|
||||||
try {
|
|
||||||
await ldapApi.updateUser({ dn: editLdapUser.dn, ...editLdapForm });
|
|
||||||
setEditLdapUser(null);
|
|
||||||
await loadLdapOUUsers(selectedOUDN);
|
|
||||||
} catch (e: any) { toast.error(e?.message || '修改失败'); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLdapUserTest = async () => {
|
|
||||||
setLdapUserTesting(true);
|
|
||||||
setLdapUserTestResult(null);
|
|
||||||
try {
|
|
||||||
const res = await ldapApi.testConnection({ test_user: ldapTestUser, test_password: ldapTestPass });
|
|
||||||
setLdapUserTestResult(res);
|
|
||||||
} catch (e: any) {
|
|
||||||
setLdapUserTestResult({ success: false, message: e?.message || '验证失败' });
|
|
||||||
} finally { setLdapUserTesting(false); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Settings tab props (LDAP management pass-through)
|
|
||||||
const settingsTabProps = {
|
|
||||||
ldapConfig,
|
|
||||||
ldapForm,
|
|
||||||
setLdapForm,
|
|
||||||
ldapSaving,
|
|
||||||
ldapTesting,
|
|
||||||
ldapTestResult,
|
|
||||||
handleLdapSave,
|
|
||||||
handleLdapTest,
|
|
||||||
ldapMgmtOpen,
|
|
||||||
setLdapMgmtOpen,
|
|
||||||
ldapOUs,
|
|
||||||
selectedOUDN,
|
|
||||||
setSelectedOUDN,
|
|
||||||
ldapUsers,
|
|
||||||
selectedLDAPUsers,
|
|
||||||
setSelectedLDAPUsers,
|
|
||||||
loadLdapOUs,
|
|
||||||
loadLdapOUUsers,
|
|
||||||
handleLdapImport,
|
|
||||||
importing,
|
|
||||||
importFeedback,
|
|
||||||
importResultOpen,
|
|
||||||
setImportResultOpen,
|
|
||||||
createOUOpen,
|
|
||||||
setCreateOUOpen,
|
|
||||||
ouForm,
|
|
||||||
setOuForm,
|
|
||||||
handleCreateOU,
|
|
||||||
createUserOpen,
|
|
||||||
setCreateUserOpen,
|
|
||||||
newUser,
|
|
||||||
setNewUser,
|
|
||||||
handleCreateLDAPUser,
|
|
||||||
ldapUserTestOpen,
|
|
||||||
setLdapUserTestOpen,
|
|
||||||
ldapTestUser,
|
|
||||||
setLdapTestUser,
|
|
||||||
ldapTestPass,
|
|
||||||
setLdapTestPass,
|
|
||||||
ldapUserTesting,
|
|
||||||
ldapUserTestResult,
|
|
||||||
handleLdapUserTest,
|
|
||||||
editLdapUser,
|
|
||||||
setEditLdapUser,
|
|
||||||
editLdapForm,
|
|
||||||
setEditLdapForm,
|
|
||||||
handleUpdateLdapUser,
|
|
||||||
handleDeleteLDAPEntry,
|
|
||||||
openEditLdapUser,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
@@ -528,21 +267,21 @@ export function SystemManager() {
|
|||||||
setImportDialogOpen={setImportDialogOpen}
|
setImportDialogOpen={setImportDialogOpen}
|
||||||
importPreview={importPreview}
|
importPreview={importPreview}
|
||||||
setImportPreview={setImportPreview}
|
setImportPreview={setImportPreview}
|
||||||
importing={importing}
|
importing={ldap.importing}
|
||||||
importResult={importResult}
|
importResult={importResult}
|
||||||
setImportResult={setImportResult}
|
setImportResult={setImportResult}
|
||||||
handleCsvUpload={handleCsvUpload}
|
handleCsvUpload={handleCsvUpload}
|
||||||
handleBatchImport={handleBatchImport}
|
handleBatchImport={handleBatchImport}
|
||||||
ldapUsers={ldapUsers}
|
ldapUsers={ldap.ldapUsers}
|
||||||
ldapSelected={ldapSelected}
|
ldapSelected={ldap.ldapSelected}
|
||||||
setLdapSelected={setLdapSelected}
|
setLdapSelected={ldap.setLdapSelected}
|
||||||
ldapLoading={ldapLoading}
|
ldapLoading={ldap.ldapLoading}
|
||||||
ldapImportOUs={ldapImportOUs}
|
ldapImportOUs={ldap.ldapImportOUs}
|
||||||
ldapImportOUDN={ldapImportOUDN}
|
ldapImportOUDN={ldap.ldapImportOUDN}
|
||||||
setLdapImportOUDN={setLdapImportOUDN}
|
setLdapImportOUDN={ldap.setLdapImportOUDN}
|
||||||
loadLdapUsers={loadLdapUsers}
|
loadLdapUsers={ldap.loadLdapUsers}
|
||||||
handleLdapImport={handleLdapImport}
|
handleLdapImport={ldap.handleLdapImport}
|
||||||
setLdapUsers={setLdapUsers}
|
setLdapUsers={ldap.setLdapUsers}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
@@ -561,7 +300,7 @@ export function SystemManager() {
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="settings" className="mt-4">
|
<TabsContent value="settings" className="mt-4">
|
||||||
<SystemSettingsTab {...settingsTabProps} />
|
<SystemSettingsTab {...ldap.settingsTabProps} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,336 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||||
|
import { ldapApi } from '@/services/api';
|
||||||
|
import type { LdapFormData } from './SystemSettingsTab';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useLdapManagement — 封装 SystemManager 中所有 LDAP 相关状态与业务逻辑
|
||||||
|
*
|
||||||
|
* 包含三大块:
|
||||||
|
* 1. LDAP 配置管理(连接信息读写 + 测试连接)
|
||||||
|
* 2. LDAP 账号管理(OU/用户 CRUD + 属性编辑 + 认证测试)
|
||||||
|
* 3. LDAP 用户导入(导入弹窗用:OU 下拉 + 用户列表 + 批量导入)
|
||||||
|
*
|
||||||
|
* 另外持有 `importing` 状态,供 CSV 批量导入与 LDAP 导入共用。
|
||||||
|
*
|
||||||
|
* @param onUsersChanged 导入用户后回调(刷新本地用户列表)
|
||||||
|
* // Logan.Li
|
||||||
|
*/
|
||||||
|
export function useLdapManagement(onUsersChanged: () => Promise<void> | void) {
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
|
// ===== LDAP 用户导入(用户管理 Tab 的导入弹窗用) =====
|
||||||
|
const [ldapUsers, setLdapUsers] = useState<any[]>([]);
|
||||||
|
const [ldapSelected, setLdapSelected] = useState<Set<string>>(new Set());
|
||||||
|
const [ldapLoading, setLdapLoading] = useState(false);
|
||||||
|
const [ldapImportOUs, setLdapImportOUs] = useState<any[]>([]);
|
||||||
|
const [ldapImportOUDN, setLdapImportOUDN] = useState('');
|
||||||
|
|
||||||
|
// ===== LDAP 配置 =====
|
||||||
|
const [ldapConfig, setLdapConfig] = useState<any>(null);
|
||||||
|
const [ldapForm, setLdapForm] = useState<LdapFormData>({
|
||||||
|
ldap_host: '', ldap_port: 389, use_ldaps: false,
|
||||||
|
admin_dn: '', admin_password: '', base_dn: '', search_filter: '',
|
||||||
|
});
|
||||||
|
const [ldapSaving, setLdapSaving] = useState(false);
|
||||||
|
const [ldapTesting, setLdapTesting] = useState(false);
|
||||||
|
const [ldapTestResult, setLdapTestResult] = useState<string | null>(null);
|
||||||
|
const [ldapMgmtOpen, setLdapMgmtOpen] = useState(false);
|
||||||
|
const [importFeedback, setImportFeedback] = useState<string | null>(null);
|
||||||
|
const [importResultOpen, setImportResultOpen] = useState(false);
|
||||||
|
|
||||||
|
// ===== LDAP 账号管理 =====
|
||||||
|
const [ldapOUs, setLdapOUs] = useState<any[]>([]);
|
||||||
|
const [selectedOUDN, setSelectedOUDN] = useState('');
|
||||||
|
const [selectedLDAPUsers, setSelectedLDAPUsers] = useState<string[]>([]);
|
||||||
|
const [createOUOpen, setCreateOUOpen] = useState(false);
|
||||||
|
const [createUserOpen, setCreateUserOpen] = useState(false);
|
||||||
|
const [ouForm, setOuForm] = useState({ ou_name: '', parent_dn: '', description: '' });
|
||||||
|
const [newUser, setNewUser] = useState({ username: '', password: '', display_name: '', email: '', ou_dn: '' });
|
||||||
|
const [ldapUserTestOpen, setLdapUserTestOpen] = useState(false);
|
||||||
|
const [ldapTestUser, setLdapTestUser] = useState('');
|
||||||
|
const [ldapTestPass, setLdapTestPass] = useState('');
|
||||||
|
const [ldapUserTesting, setLdapUserTesting] = useState(false);
|
||||||
|
const [ldapUserTestResult, setLdapUserTestResult] = useState<any>(null);
|
||||||
|
const [editLdapUser, setEditLdapUser] = useState<any>(null);
|
||||||
|
const [editLdapForm, setEditLdapForm] = useState({ cn: '', mail: '', phone: '', title: '' });
|
||||||
|
|
||||||
|
// 导入进行中状态(CSV 与 LDAP 导入共用)
|
||||||
|
const [importing, setImporting] = useState(false);
|
||||||
|
|
||||||
|
// ===== LDAP 配置相关 handler =====
|
||||||
|
|
||||||
|
/** 加载 LDAP 配置 // Logan.Li */
|
||||||
|
const loadLdapConfig = async () => {
|
||||||
|
try {
|
||||||
|
const res = await ldapApi.getConfig();
|
||||||
|
if (res && res.ldap_host) {
|
||||||
|
setLdapConfig(res);
|
||||||
|
setLdapForm({
|
||||||
|
ldap_host: res.ldap_host || '',
|
||||||
|
ldap_port: res.ldap_port || 389,
|
||||||
|
use_ldaps: res.use_ldaps || false,
|
||||||
|
admin_dn: res.admin_dn || '',
|
||||||
|
admin_password: '',
|
||||||
|
base_dn: res.base_dn || '',
|
||||||
|
search_filter: res.search_filter || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 暂无配置
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 保存 LDAP 配置 // Logan.Li */
|
||||||
|
const handleLdapSave = async () => {
|
||||||
|
setLdapSaving(true);
|
||||||
|
try {
|
||||||
|
await ldapApi.updateConfig(ldapForm);
|
||||||
|
await loadLdapConfig();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save LDAP config:', error);
|
||||||
|
} finally {
|
||||||
|
setLdapSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 测试 LDAP 连接 // Logan.Li */
|
||||||
|
const handleLdapTest = async () => {
|
||||||
|
setLdapTesting(true);
|
||||||
|
setLdapTestResult(null);
|
||||||
|
try {
|
||||||
|
await ldapApi.updateConfig(ldapForm);
|
||||||
|
const res = await ldapApi.testConnection();
|
||||||
|
setLdapTestResult(res?.message || (res?.success ? '连接成功' : '连接失败'));
|
||||||
|
} catch (error: any) {
|
||||||
|
setLdapTestResult(error?.message || '连接失败');
|
||||||
|
} finally {
|
||||||
|
setLdapTesting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== LDAP 账号管理相关 handler =====
|
||||||
|
|
||||||
|
/** 加载指定 OU 下的用户 // Logan.Li */
|
||||||
|
const loadLdapOUUsers = async (ouDn?: string) => {
|
||||||
|
try {
|
||||||
|
const users = await ldapApi.listUsers(ouDn);
|
||||||
|
setLdapUsers(users || []);
|
||||||
|
} catch (e) { console.error('loadOUUsers', e); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 加载 OU 列表(含根 DN 用户) // Logan.Li */
|
||||||
|
const loadLdapOUs = async () => {
|
||||||
|
try {
|
||||||
|
const ous = await ldapApi.listOUs();
|
||||||
|
setLdapOUs(ous || []);
|
||||||
|
// 同时加载根 DN 下的用户
|
||||||
|
if (!selectedOUDN && ldapConfig?.base_dn) {
|
||||||
|
setSelectedOUDN(ldapConfig.base_dn);
|
||||||
|
await loadLdapOUUsers(ldapConfig.base_dn);
|
||||||
|
}
|
||||||
|
} catch (e) { console.error('loadOUs', e); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 创建 OU // Logan.Li */
|
||||||
|
const handleCreateOU = async () => {
|
||||||
|
setLdapSaving(true);
|
||||||
|
try {
|
||||||
|
await ldapApi.createOU(ouForm);
|
||||||
|
setCreateOUOpen(false);
|
||||||
|
setOuForm({ ou_name: '', parent_dn: '', description: '' });
|
||||||
|
await loadLdapOUs();
|
||||||
|
} catch (e: any) {
|
||||||
|
const msg = e?.message || '';
|
||||||
|
if (msg.includes('Already Exists') || msg.includes('68')) {
|
||||||
|
toast.error(`OU "${ouForm.ou_name}" 已存在,请使用其他名称`);
|
||||||
|
} else {
|
||||||
|
toast.error('创建失败: ' + msg);
|
||||||
|
}
|
||||||
|
} finally { setLdapSaving(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 创建 LDAP 用户 // Logan.Li */
|
||||||
|
const handleCreateLDAPUser = async () => {
|
||||||
|
setLdapSaving(true);
|
||||||
|
try {
|
||||||
|
await ldapApi.createUser({ ...newUser, ou_dn: newUser.ou_dn || selectedOUDN || ldapConfig?.base_dn });
|
||||||
|
setCreateUserOpen(false);
|
||||||
|
setNewUser({ username: '', password: '', display_name: '', email: '', ou_dn: '' });
|
||||||
|
await loadLdapOUUsers(selectedOUDN);
|
||||||
|
} catch (e: any) { toast.error(e?.message || '创建失败'); }
|
||||||
|
finally { setLdapSaving(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 删除 LDAP 条目 // Logan.Li */
|
||||||
|
const handleDeleteLDAPEntry = async (dn: string, silent = false) => {
|
||||||
|
if (!silent) {
|
||||||
|
const ok = await confirm({ title: '删除 LDAP 条目', description: `确定删除 ${dn}?`, confirmText: '删除', variant: 'destructive' });
|
||||||
|
if (!ok) return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await ldapApi.deleteUser(dn);
|
||||||
|
if (!silent) await loadLdapOUUsers(selectedOUDN);
|
||||||
|
} catch (e: any) { if (!silent) toast.error(e?.message || '删除失败'); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 打开 LDAP 用户编辑 // Logan.Li */
|
||||||
|
const openEditLdapUser = (user: any) => {
|
||||||
|
setEditLdapUser(user);
|
||||||
|
setEditLdapForm({ cn: user.cn || '', mail: user.mail || '', phone: user.phone || '', title: user.title || '' });
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 更新 LDAP 用户属性 // Logan.Li */
|
||||||
|
const handleUpdateLdapUser = async () => {
|
||||||
|
if (!editLdapUser) return;
|
||||||
|
try {
|
||||||
|
await ldapApi.updateUser({ dn: editLdapUser.dn, ...editLdapForm });
|
||||||
|
setEditLdapUser(null);
|
||||||
|
await loadLdapOUUsers(selectedOUDN);
|
||||||
|
} catch (e: any) { toast.error(e?.message || '修改失败'); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** LDAP 账号验证测试 // Logan.Li */
|
||||||
|
const handleLdapUserTest = async () => {
|
||||||
|
setLdapUserTesting(true);
|
||||||
|
setLdapUserTestResult(null);
|
||||||
|
try {
|
||||||
|
const res = await ldapApi.testConnection({ test_user: ldapTestUser, test_password: ldapTestPass });
|
||||||
|
setLdapUserTestResult(res);
|
||||||
|
} catch (e: any) {
|
||||||
|
setLdapUserTestResult({ success: false, message: e?.message || '验证失败' });
|
||||||
|
} finally { setLdapUserTesting(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== LDAP 用户导入相关 handler =====
|
||||||
|
|
||||||
|
/** 加载 LDAP 用户列表(导入弹窗用) // Logan.Li */
|
||||||
|
const loadLdapUsers = async (ouDn?: string) => {
|
||||||
|
setLdapLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await ldapApi.listUsers(ouDn);
|
||||||
|
setLdapUsers(Array.isArray(res) ? res : []);
|
||||||
|
setLdapSelected(new Set());
|
||||||
|
} catch {
|
||||||
|
setLdapUsers([]);
|
||||||
|
} finally {
|
||||||
|
setLdapLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 执行 LDAP 批量导入 // Logan.Li */
|
||||||
|
const handleLdapImport = async () => {
|
||||||
|
let selected: any[] = [];
|
||||||
|
if (selectedLDAPUsers.length > 0) {
|
||||||
|
selected = ldapUsers.filter((u) => selectedLDAPUsers.includes(u.dn));
|
||||||
|
} else {
|
||||||
|
selected = ldapUsers; // 没有选择则导入全部
|
||||||
|
}
|
||||||
|
if (selected.length === 0) return;
|
||||||
|
setImporting(true);
|
||||||
|
try {
|
||||||
|
const mapped = selected.map((u) => ({
|
||||||
|
username: u.uid || '',
|
||||||
|
nickname: u.cn || '',
|
||||||
|
email: u.mail || '',
|
||||||
|
}));
|
||||||
|
const res = await ldapApi.batchImport(mapped);
|
||||||
|
setImportFeedback(res?.message || `成功导入 ${selected.length} 个用户`);
|
||||||
|
setImportResultOpen(true);
|
||||||
|
setSelectedLDAPUsers([]);
|
||||||
|
await onUsersChanged();
|
||||||
|
} catch (error: any) {
|
||||||
|
setImportFeedback('导入失败: ' + (error?.message || '未知错误'));
|
||||||
|
setImportResultOpen(true);
|
||||||
|
} finally {
|
||||||
|
setImporting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** LDAP 导入弹窗打开时加载 OU 和用户列表 // Logan.Li */
|
||||||
|
const loadImportDialogData = () => {
|
||||||
|
ldapApi.listOUs()
|
||||||
|
.then((ous) => setLdapImportOUs(Array.isArray(ous) ? ous : []))
|
||||||
|
.catch(() => {});
|
||||||
|
loadLdapUsers('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 构造传递给 SystemSettingsTab 的 props 对象 =====
|
||||||
|
const settingsTabProps = {
|
||||||
|
ldapConfig,
|
||||||
|
ldapForm,
|
||||||
|
setLdapForm,
|
||||||
|
ldapSaving,
|
||||||
|
ldapTesting,
|
||||||
|
ldapTestResult,
|
||||||
|
handleLdapSave,
|
||||||
|
handleLdapTest,
|
||||||
|
ldapMgmtOpen,
|
||||||
|
setLdapMgmtOpen,
|
||||||
|
ldapOUs,
|
||||||
|
selectedOUDN,
|
||||||
|
setSelectedOUDN,
|
||||||
|
ldapUsers,
|
||||||
|
selectedLDAPUsers,
|
||||||
|
setSelectedLDAPUsers,
|
||||||
|
loadLdapOUs,
|
||||||
|
loadLdapOUUsers,
|
||||||
|
handleLdapImport,
|
||||||
|
importing,
|
||||||
|
importFeedback,
|
||||||
|
importResultOpen,
|
||||||
|
setImportResultOpen,
|
||||||
|
createOUOpen,
|
||||||
|
setCreateOUOpen,
|
||||||
|
ouForm,
|
||||||
|
setOuForm,
|
||||||
|
handleCreateOU,
|
||||||
|
createUserOpen,
|
||||||
|
setCreateUserOpen,
|
||||||
|
newUser,
|
||||||
|
setNewUser,
|
||||||
|
handleCreateLDAPUser,
|
||||||
|
ldapUserTestOpen,
|
||||||
|
setLdapUserTestOpen,
|
||||||
|
ldapTestUser,
|
||||||
|
setLdapTestUser,
|
||||||
|
ldapTestPass,
|
||||||
|
setLdapTestPass,
|
||||||
|
ldapUserTesting,
|
||||||
|
ldapUserTestResult,
|
||||||
|
handleLdapUserTest,
|
||||||
|
editLdapUser,
|
||||||
|
setEditLdapUser,
|
||||||
|
editLdapForm,
|
||||||
|
setEditLdapForm,
|
||||||
|
handleUpdateLdapUser,
|
||||||
|
handleDeleteLDAPEntry,
|
||||||
|
openEditLdapUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// LDAP 用户导入(用户管理 Tab)
|
||||||
|
ldapUsers,
|
||||||
|
setLdapUsers,
|
||||||
|
ldapSelected,
|
||||||
|
setLdapSelected,
|
||||||
|
ldapLoading,
|
||||||
|
ldapImportOUs,
|
||||||
|
ldapImportOUDN,
|
||||||
|
setLdapImportOUDN,
|
||||||
|
loadLdapUsers,
|
||||||
|
handleLdapImport,
|
||||||
|
loadImportDialogData,
|
||||||
|
// 导入状态(CSV + LDAP 共用)
|
||||||
|
importing,
|
||||||
|
setImporting,
|
||||||
|
importFeedback,
|
||||||
|
importResultOpen,
|
||||||
|
setImportResultOpen,
|
||||||
|
// 初始化
|
||||||
|
loadLdapConfig,
|
||||||
|
// 系统设置 Tab
|
||||||
|
settingsTabProps,
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in new issue
Block a user