创建租户
@@ -297,7 +335,8 @@ createApp({
const tab = ref('dashboard');
const tabs = [
{id:'dashboard',name:'仪表盘'},{id:'nodes',name:'节点'},{id:'sdwan',name:'SDWAN'},{id:'p2p',name:'P2P'},
- {id:'tenants',name:'租户'},{id:'apikeys',name:'API Key'},{id:'users',name:'用户'},{id:'enroll',name:'Enroll'}
+ {id:'tenants',name:'租户'},{id:'apikeys',name:'API Key'},{id:'users',name:'用户'},{id:'enroll',name:'Enroll'},
+ {id:'settings',name:'高级设置'},{id:'audit',name:'审计日志'}
];
const loggedIn = ref(false), busy = ref(false), msg = ref(''), msgType = ref('ok');
@@ -315,8 +354,11 @@ createApp({
const userForm = ref({ role:'operator', email:'', password:'' });
const tokenType = ref('');
+ const settings = ref({ advanced_impersonate:'0', advanced_force_network:'0', advanced_cross_tenant:'0' });
+ const audit = ref([]);
+ const auditTenant = ref('');
const isAdmin = computed(() => role.value === 'admin');
- const filteredTabs = computed(() => isAdmin.value ? tabs : tabs.filter(t => !['tenants','apikeys','users','enroll'].includes(t.id)));
+ const filteredTabs = computed(() => isAdmin.value ? tabs : tabs.filter(t => !['tenants','apikeys','users','enroll','settings','audit'].includes(t.id)));
const filteredNodes = computed(() => {
const k = (nodeKeyword.value || '').trim().toLowerCase();
if (!k) return nodes.value;
@@ -392,6 +434,10 @@ createApp({
const nd = await api('/api/v1/nodes');
nodes.value = nd.nodes || [];
sd.value = await api('/api/v1/sdwans');
+ if (isAdmin.value) {
+ try { settings.value = await api('/api/v1/admin/settings'); } catch(_) {}
+ try { const a = await api('/api/v1/admin/audit?limit=50'); audit.value = a.logs || []; } catch(_) {}
+ }
} catch (e) {
toast(e.message || '刷新失败', 'error');
} finally {
@@ -545,6 +591,27 @@ createApp({
try { const d = await api('/api/v1/tenants/enroll'); enrolls.value = d.enrolls || []; }
catch(e){ toast(e.message, 'error'); }
};
+
+ const saveSettings = async () => {
+ if (!isAdmin.value) return;
+ try {
+ for (const k of Object.keys(settings.value || {})) {
+ await api('/api/v1/admin/settings', { method:'POST', body: JSON.stringify({ key: k, value: String(settings.value[k]) }) });
+ }
+ toast('高级设置已保存');
+ settings.value = await api('/api/v1/admin/settings');
+ } catch (e) { toast(e.message, 'error'); }
+ };
+
+ const loadAudit = async () => {
+ if (!isAdmin.value) return;
+ try {
+ const q = auditTenant.value ? `?tenant=${auditTenant.value}&limit=100` : '?limit=100';
+ const d = await api('/api/v1/admin/audit' + q);
+ audit.value = d.logs || [];
+ toast('审计日志已刷新');
+ } catch (e) { toast(e.message, 'error'); }
+ };
const createEnroll = async () => {
try {
const d = await api('/api/v1/tenants/enroll', { method:'POST', body:'{}' });
@@ -590,6 +657,7 @@ createApp({
loginUser, loginPass, loginErr, refreshSec,
health, stats, nodes, nodeKeyword, filteredNodes, sd, connectForm,
tenants, activeTenant, keys, users, enrolls, tenantForm, keyForm, userForm,
+ settings, audit, auditTenant,
natText, uptime, fmtTime,
login, logout, refreshAll, saveSDWAN, addSDWANNode, removeSDWANNode, addSubnetProxy, removeSubnetProxy, autoAssignIPs,
kickNode, renameNode, changeNodeIP, openAppManager, pushAppConfigs, openConnect, doConnect,
@@ -597,6 +665,7 @@ createApp({
createKey, loadKeys, setKeyStatus,
createUser, loadUsers, setUserStatus, resetUserPassword, deleteUser,
createEnroll, loadEnrolls, setEnrollStatus, consumeEnroll,
+ saveSettings, loadAudit,
updateCharts
};
}