diff --git a/cmd/inp2ps/main.go b/cmd/inp2ps/main.go index 673126f..b532d98 100644 --- a/cmd/inp2ps/main.go +++ b/cmd/inp2ps/main.go @@ -357,8 +357,9 @@ func main() { ExpiresAt int64 `json:"expires_at"` Role string `json:"role"` Status int `json:"status"` + TenantID int64 `json:"tenant_id"` Subnet string `json:"subnet"` - }{0, "ok", sessionToken, "session", exp, u.Role, u.Status, ""} + }{0, "ok", sessionToken, "session", exp, u.Role, u.Status, u.TenantID, ""} if ten != nil { resp.Subnet = ten.Subnet } @@ -581,6 +582,17 @@ func main() { // Server uptime + detailed stats mux.HandleFunc("/api/v1/stats", tenantMiddleware(func(w http.ResponseWriter, r *http.Request) { nodes := srv.GetOnlineNodes() + // tenant-scoped stats + tenantID := getTenantID(r) + if tenantID > 0 { + filtered := make([]*server.NodeInfo, 0, len(nodes)) + for _, n := range nodes { + if n.TenantID == tenantID { + filtered = append(filtered, n) + } + } + nodes = filtered + } coneCount, symmCount, unknCount := 0, 0, 0 relayCount := 0 for _, n := range nodes { @@ -596,7 +608,12 @@ func main() { relayCount++ } } - sdwan := srv.GetSDWAN() + var sdwan protocol.SDWANConfig + if tenantID > 0 { + sdwan = srv.GetSDWANTenant(tenantID) + } else { + sdwan = srv.GetSDWAN() + } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{ "nodes": len(nodes), diff --git a/web/index.html b/web/index.html index 5c3886b..956e378 100644 --- a/web/index.html +++ b/web/index.html @@ -361,10 +361,11 @@ createApp({ const userForm = ref({ role:'operator', email:'', password:'' }); const tokenType = ref(''); + const tenantId = ref(0); 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 isAdmin = computed(() => role.value === 'admin' && tenantId.value === 1); 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(); @@ -416,6 +417,7 @@ createApp({ role.value = d.role || ''; status.value = d.status ?? 1; tokenType.value = d.token_type || 'session'; + tenantId.value = d.tenant_id || 0; if (status.value !== 1) throw new Error('账号已停用'); loggedIn.value = true; await refreshAll(); @@ -433,6 +435,7 @@ createApp({ loggedIn.value = false; role.value = ''; tokenType.value = ''; + tenantId.value = 0; stopTimer(); }; @@ -667,7 +670,7 @@ createApp({ }); return { - buildVersion, tab, filteredTabs, loggedIn, busy, msg, msgType, role, status, tokenType, + buildVersion, tab, filteredTabs, loggedIn, busy, msg, msgType, role, status, tokenType, tenantId, loginUser, loginPass, loginErr, refreshSec, health, stats, nodes, nodeKeyword, filteredNodes, sd, connectForm, tenants, activeTenant, keys, users, enrolls, tenantForm, keyForm, userForm,