Files
tfm_ainventory/frontend/hooks/useAdmin.ts

339 lines
11 KiB
TypeScript

import { useState, useEffect } from 'react';
import { inventoryApi } from '@/lib/api';
import { toast } from 'react-hot-toast';
export function useAdmin() {
const [users, setUsers] = useState<any[]>([]);
const [categories, setCategories] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [ldapConfig, setLdapConfig] = useState<any>({
ldap_enabled: false,
server_uri: '',
base_dn: '',
user_template: '',
groups_dn: '',
use_tls: false,
role_mappings: []
});
const [testingLdap, setTestingLdap] = useState(false);
const [editingUser, setEditingUser] = useState<any | null>(null);
const [editUserForm, setEditUserForm] = useState({ username: '', password: '', role: 'user' });
const [editingCategory, setEditingCategory] = useState<any | null>(null);
const [editCatForm, setEditCatForm] = useState({ name: '', description: '' });
const [backups, setBackups] = useState<any[]>([]);
const [dbStats, setDbStats] = useState({ backup_count: 0, total_size_bytes: 0 });
const [dbSettings, setDbSettings] = useState({ retention_count: 10, schedule_hour: 3, schedule_freq_days: 1 });
const [isBackingUp, setIsBackingUp] = useState(false);
const [isImporting, setIsImporting] = useState(false);
const [aiPrompt, setAiPrompt] = useState("");
const [aiConfig, setAiConfig] = useState<any>(null);
const [aiKeys, setAiKeys] = useState({ gemini: '', claude: '' });
const [isSavingPrompt, setIsSavingPrompt] = useState(false);
const [isSavingKeys, setIsSavingKeys] = useState(false);
const [isTestingKeys, setIsTestingKeys] = useState<{gemini: boolean, claude: boolean}>(
{gemini: false, claude: false}
);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
setLoading(true);
try {
const [u, c, l, b, s, st, promptRes, ai] = await Promise.all([
inventoryApi.getUsers(),
inventoryApi.getCategories(),
inventoryApi.getLdapConfig(),
inventoryApi.getDbBackups(),
inventoryApi.getDbStats(),
inventoryApi.getDbSettings(),
inventoryApi.getAiPrompt(),
inventoryApi.getAiConfig()
]);
setUsers(u);
setCategories(c);
if (l && l.server_uri) setLdapConfig(l);
setBackups(b);
setDbStats(s);
setDbSettings(st);
setAiPrompt(promptRes.value);
setAiConfig(ai);
} catch (err: any) {
console.error(err);
toast.error("Failed to load admin data");
} finally {
setLoading(false);
}
};
const handleAddUser = async () => {
const name = window.prompt("Enter new username:");
if (!name) return;
const pwd = window.prompt("Enter password for " + name + ":");
if (!pwd) return;
try {
await inventoryApi.createUser({ username: name, password: pwd, role: 'user' });
toast.success("User created successfully");
loadData();
} catch (err: any) {
toast.error("Failed to create user");
}
};
const handleDeleteUser = async (id: number, username: string) => {
if (username === 'Admin') return;
if (!window.confirm(`Delete user ${username}?`)) return;
try {
await inventoryApi.deleteUser(id);
toast.success("User removed");
loadData();
} catch (err: any) {
toast.error("Delete failed");
}
};
const handleUpdateUserSubmit = async () => {
if (!editingUser) return;
try {
const payload: any = { username: editUserForm.username, role: editUserForm.role };
if (editUserForm.password) payload.password = editUserForm.password;
await inventoryApi.updateUser(editingUser.id, payload);
toast.success("User updated successfully");
setEditingUser(null);
loadData();
} catch (err: any) {
toast.error("Update failed");
}
};
const handleUpdateAiProvider = async (provider: string) => {
try {
await inventoryApi.updateAiProvider(provider);
toast.success(`AI provider switched to ${provider}`);
loadData();
} catch (err: any) {
toast.error("Failed to switch AI provider");
}
};
const handleSaveAiKeys = async () => {
if (!aiKeys.gemini && !aiKeys.claude) {
toast.error("Enter at least one API key to save");
return;
}
setIsSavingKeys(true);
try {
await inventoryApi.updateAiKeys({
gemini_api_key: aiKeys.gemini || undefined,
claude_api_key: aiKeys.claude || undefined
});
toast.success("AI Configuration keys updated successfully");
setAiKeys({ gemini: '', claude: '' });
loadData();
} catch (err: any) {
toast.error("Failed to update AI keys");
} finally {
setIsSavingKeys(false);
}
};
const handleTestAiKey = async (provider: 'gemini' | 'claude') => {
const keyToTest = provider === 'gemini' ? aiKeys.gemini : aiKeys.claude;
setIsTestingKeys(prev => ({ ...prev, [provider]: true }));
try {
const res = await inventoryApi.testAiKey(provider, keyToTest);
toast.success(res.message || `${provider.toUpperCase()} connection successful!`);
} catch (err: any) {
const msg = err.response?.data?.detail || `Failed to test ${provider} key`;
toast.error(msg);
} finally {
setIsTestingKeys(prev => ({ ...prev, [provider]: false }));
}
};
const handleAddCategory = async () => {
const name = window.prompt("New category name:");
if (!name) return;
const desc = window.prompt("Description (optional):");
try {
await inventoryApi.createCategory({ name, description: desc });
toast.success("Category added");
loadData();
} catch (err: any) {
toast.error("Failed to add category");
}
};
const handleUpdateCategorySubmit = async () => {
if (!editingCategory) return;
try {
await inventoryApi.updateCategory(editingCategory.id, editCatForm);
toast.success("Category updated");
setEditingCategory(null);
loadData();
} catch (err: any) {
toast.error("Update failed");
}
};
const handleDeleteCategory = async (id: number, name: string) => {
if (!window.confirm(`Delete category ${name}?`)) return;
try {
await inventoryApi.deleteCategory(id);
toast.success("Category removed");
loadData();
} catch (err: any) {
toast.error(err.response?.data?.detail || "Delete failed");
}
};
const handleCreateBackup = async () => {
setIsBackingUp(true);
try {
await inventoryApi.triggerBackup();
toast.success("Snapshot created successfully");
loadData();
} catch (err: any) {
toast.error("Backup failed");
} finally {
setIsBackingUp(false);
}
};
const handleUpdateDbSettings = async (newSettings: any) => {
try {
await inventoryApi.updateDbSettings(newSettings);
toast.success("System policy updated");
setDbSettings(newSettings);
} catch (err: any) {
toast.error("Failed to update settings");
}
};
const handleUpdatePrompt = async () => {
setIsSavingPrompt(true);
try {
await inventoryApi.updateAiPrompt(aiPrompt);
toast.success("AI Extraction Prompt updated");
} catch (err: any) {
toast.error("Failed to update AI prompt");
} finally {
setIsSavingPrompt(false);
}
};
const handleUpdateLdap = async () => {
setLoading(true);
try {
await inventoryApi.updateLdapConfig(ldapConfig);
toast.success("Enterprise configuration updated");
} catch (err: any) {
toast.error("Failed to update LDAP config");
} finally {
setLoading(false);
}
};
const handleTestLdap = async () => {
setTestingLdap(true);
try {
const res = await inventoryApi.testLdapConnection(ldapConfig);
if (res.status === 'success') {
toast.success(res.message || "LDAP Connection Successful!");
} else {
toast.error(`LDAP Error: ${res.message || "Unknown error"}`);
}
} catch (err: any) {
toast.error(`Connection failed: ${err.response?.data?.detail || err.message}`);
} finally {
setTestingLdap(false);
}
};
const handleRestore = async (filename: string) => {
if (!window.confirm(`DANGEROUS: Restore database from ${filename}? Current data will be replaced. A rollback snapshot will be created automatically.`)) return;
const loadingToast = toast.loading("Restoring database...");
try {
await inventoryApi.restoreDatabase(filename);
toast.success("Database restored! Reloading system...", { id: loadingToast });
setTimeout(() => window.location.reload(), 2000);
} catch (err: any) {
toast.error("Restore failed", { id: loadingToast });
}
};
const handleExportDb = async () => {
try {
const blob = await inventoryApi.exportDb();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `inventory_backup_${new Date().toISOString().split('T')[0]}.db`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
toast.success("Database exported successfully");
} catch (err: any) {
toast.error("Export failed");
}
};
const handleImportDb = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
if (!window.confirm("DANGEROUS: You are about to REPLACE the entire database with this file. All current data will be lost. Proceed?")) {
e.target.value = '';
return;
}
setIsImporting(true);
const formData = new FormData();
formData.append('file', file);
const loadingToast = toast.loading("Importing database...");
try {
await inventoryApi.importDb(formData);
toast.success("Database imported! Reloading system...", { id: loadingToast });
setTimeout(() => window.location.reload(), 2000);
} catch (err: any) {
toast.error("Import failed: " + (err.response?.data?.detail || err.message), { id: loadingToast });
} finally {
setIsImporting(false);
e.target.value = '';
}
};
const handleLogout = () => {
import('@/lib/auth').then(m => m.clearAuth());
window.location.href = '/login';
};
return {
users, categories, loading,
ldapConfig, setLdapConfig, testingLdap,
editingUser, setEditingUser, editUserForm, setEditUserForm,
editingCategory, setEditingCategory, editCatForm, setEditCatForm,
backups, dbStats, dbSettings, isBackingUp, isImporting,
aiPrompt, setAiPrompt, aiConfig, aiKeys, setAiKeys,
isSavingPrompt, isSavingKeys, isTestingKeys,
refreshData: loadData,
handleAddUser, handleDeleteUser, handleUpdateUserSubmit,
handleUpdateLdap, handleTestLdap,
handleUpdateAiProvider, handleSaveAiKeys, handleTestAiKey, handleUpdatePrompt,
handleAddCategory, handleUpdateCategorySubmit, handleDeleteCategory,
handleCreateBackup, handleUpdateDbSettings, handleRestore, handleExportDb, handleImportDb,
handleLogout
};
}