package web import ( "net/http" ) type betreiberDashboardData struct { Title string Nav navData AccountCount int RecentAuditCount int } // handleBetreiberDashboard zeigt eine kurze Übersicht als Einstieg in // den Plattform-Bereich (Ebene 5 — nur Betreiber, technisch getrennt // vom Mandantenbereich). func (s *Server) handleBetreiberDashboard(w http.ResponseWriter, r *http.Request) { ctx := r.Context() accounts, err := s.store.ListAccounts(ctx) if err != nil { http.Error(w, "Accounts konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } auditLog, err := s.store.ListAuditLog(ctx, 5) if err != nil { http.Error(w, "Audit-Log konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } data := betreiberDashboardData{ Title: "Plattform", Nav: navFor(r), AccountCount: len(accounts), RecentAuditCount: len(auditLog), } if err := s.templates.ExecuteTemplate(w, "betreiber-dashboard", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } } type betreiberAccountListItem struct { ID string Name string CreatedAt string } type betreiberAccountListData struct { Title string Nav navData Accounts []betreiberAccountListItem } // handleBetreiberAccountList listet alle Mandanten der Plattform. func (s *Server) handleBetreiberAccountList(w http.ResponseWriter, r *http.Request) { accounts, err := s.store.ListAccounts(r.Context()) if err != nil { http.Error(w, "Accounts konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } data := betreiberAccountListData{Title: "Accounts", Nav: navFor(r)} for _, a := range accounts { data.Accounts = append(data.Accounts, betreiberAccountListItem{ ID: a.ID, Name: a.Name, CreatedAt: a.CreatedAt.Format("02.01.2006 15:04"), }) } if err := s.templates.ExecuteTemplate(w, "betreiber-accounts", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } } type betreiberUserView struct { Email string Role string } type betreiberAccountDetailData struct { Title string Nav navData AccountID string Name string Users []betreiberUserView CreatedAt string } // handleBetreiberAccountDetail zeigt einen Mandanten mit seinen Logins. func (s *Server) handleBetreiberAccountDetail(w http.ResponseWriter, r *http.Request) { ctx := r.Context() acc, err := s.store.GetAccount(ctx, r.PathValue("id")) if err != nil { http.Error(w, "Account nicht gefunden", http.StatusNotFound) return } users, err := s.store.ListUsersForAccount(ctx, acc.ID) if err != nil { http.Error(w, "Nutzer konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } data := betreiberAccountDetailData{ Title: "Account", Nav: navFor(r), AccountID: acc.ID, Name: acc.Name, CreatedAt: acc.CreatedAt.Format("02.01.2006 15:04"), } for _, u := range users { data.Users = append(data.Users, betreiberUserView{Email: u.Email, Role: u.Role}) } if err := s.templates.ExecuteTemplate(w, "betreiber-account-detail", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } } type betreiberAuditEntryView struct { CreatedAt string Action string TargetType string TargetID string Details string } type betreiberAuditLogData struct { Title string Nav navData Entries []betreiberAuditEntryView } // handleBetreiberAuditLog zeigt das plattformweite Audit-Log. func (s *Server) handleBetreiberAuditLog(w http.ResponseWriter, r *http.Request) { entries, err := s.store.ListAuditLog(r.Context(), 200) if err != nil { http.Error(w, "Audit-Log konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError) return } data := betreiberAuditLogData{Title: "Audit-Log", Nav: navFor(r)} for _, e := range entries { data.Entries = append(data.Entries, betreiberAuditEntryView{ CreatedAt: e.CreatedAt.Format("02.01.2006 15:04:05"), Action: e.Action, TargetType: e.TargetType, TargetID: e.TargetID, Details: e.Details, }) } if err := s.templates.ExecuteTemplate(w, "betreiber-audit-log", data); err != nil { http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError) } }