feat: integrate Grafana dashboards into BOC DashboardPage
- Add Infrastructure Health section with CPU/Memory/Disk panels - Add Service Status section with PM2/Docker panels - Create GrafanaPanel component for iframe embedding - Build passes successfully
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"boc/tenant"
|
||||
)
|
||||
|
||||
// TenantHandler hanterar multi-tenancy API
|
||||
type TenantHandler struct {
|
||||
manager *tenant.Manager
|
||||
}
|
||||
|
||||
func NewTenantHandler(db *sql.DB) *TenantHandler {
|
||||
return &TenantHandler{
|
||||
manager: tenant.NewManager(db),
|
||||
}
|
||||
}
|
||||
|
||||
// ListTenants returnerar alla verksamheter användaren har tillgång till
|
||||
func (h *TenantHandler) ListTenants(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Filtrera baserat på användarens behörigheter
|
||||
tenants, err := h.manager.ListTenants(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, `{"error":"failed to list tenants"}`, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"tenants": tenants,
|
||||
"total": len(tenants),
|
||||
})
|
||||
}
|
||||
|
||||
// GetTenant returnerar detaljer för en specifik verksamhet
|
||||
func (h *TenantHandler) GetTenant(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID := r.URL.Query().Get("id")
|
||||
if tenantID == "" {
|
||||
http.Error(w, `{"error":"tenant id required"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
t, err := h.manager.GetTenant(r.Context(), tenantID)
|
||||
if err != nil {
|
||||
http.Error(w, `{"error":"tenant not found"}`, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(t)
|
||||
}
|
||||
|
||||
// GetTenantHierarchy returnerar koncernstruktur
|
||||
func (h *TenantHandler) GetTenantHierarchy(w http.ResponseWriter, r *http.Request) {
|
||||
// Hämta alla koncerner (root tenants)
|
||||
rows, err := h.manager.ListTenants(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, `{"error":"failed to get hierarchy"}`, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Bygg hierarki
|
||||
type HierarchyNode struct {
|
||||
*tenant.Tenant
|
||||
Children []*HierarchyNode `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
// Simplifierad: returnera flat lista för nu
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"tenants": rows,
|
||||
})
|
||||
}
|
||||
|
||||
// SwitchTenant byter aktiv verksamhet för användaren
|
||||
func (h *TenantHandler) SwitchTenant(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
TenantID string `json:"tenant_id"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Verifiera att tenant finns och är aktiv
|
||||
t, err := h.manager.GetTenant(r.Context(), req.TenantID)
|
||||
if err != nil {
|
||||
http.Error(w, `{"error":"tenant not found or inactive"}`, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Uppdatera användarens session med ny tenant
|
||||
// TODO: Logga tenant-byte i audit log
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"success": true,
|
||||
"tenant_id": t.ID,
|
||||
"tenant_name": t.Name,
|
||||
"message": "Switched to " + t.Name,
|
||||
})
|
||||
}
|
||||
|
||||
// GetTenantSummary returnerar sammanfattning för dashboard
|
||||
func (h *TenantHandler) GetTenantSummary(w http.ResponseWriter, r *http.Request) {
|
||||
tenantID := r.URL.Query().Get("tenant_id")
|
||||
if tenantID == "" {
|
||||
tenantID = "default" // Fallback
|
||||
}
|
||||
|
||||
// För nu, returnera mock-data
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"tenant_id": tenantID,
|
||||
"active_users": 5,
|
||||
"customers": 3,
|
||||
"open_deals": 3,
|
||||
"employees": 6,
|
||||
"pipeline_value": 5250000,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user