Umsetzung der Ausbaustufen 2.1, 3.6, 3.8, 3.9, 4.1, 4.2, 4.4, 4.6, 5.1–5.4 (Details in AUSBAUSTUFEN.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
2.5 KiB
Go
86 lines
2.5 KiB
Go
package web
|
||
|
||
// Nutzungsübersicht je Projekt (AUSBAUSTUFEN.md 4.6).
|
||
//
|
||
// Für alle mit Zugang zum Projekt, auch mit Leserecht: Die Seite zeigt nur
|
||
// Zahlen und Namen von Zugängen, und die stehen ohnehin schon im Protokoll,
|
||
// das jede Rolle sehen darf.
|
||
|
||
import (
|
||
"net/http"
|
||
"time"
|
||
|
||
"munin/internal/store"
|
||
)
|
||
|
||
// Wie viele Einträge die Listen auf der Seite höchstens zeigen. Die Seite
|
||
// soll einen Überblick geben, keine vollständige Aufstellung — wer mehr
|
||
// braucht, findet sie über die Eintragsliste oder den Graph.
|
||
const (
|
||
nutzungListenLaenge = 15
|
||
nutzungKeysLaenge = 5
|
||
)
|
||
|
||
// usageData sind die Daten der Nutzungsseite.
|
||
type usageData struct {
|
||
Summary store.UsageSummary
|
||
WindowDays int
|
||
Untagged []store.EntryRef
|
||
Missing []store.MissingLink
|
||
MissingMore int // wie viele Verweise ins Leere über die Liste hinaus
|
||
Keys []store.APIKey
|
||
}
|
||
|
||
// handleProjectUsage zeigt die Nutzungsübersicht eines Projekts.
|
||
func (s *Server) handleProjectUsage(w http.ResponseWriter, r *http.Request) {
|
||
ws, _ := WorkspaceFrom(r)
|
||
project, _ := ProjectFrom(r)
|
||
ctx := r.Context()
|
||
|
||
alle, err := ws.Store.UsageSummaries(ctx, project.ID, time.Now())
|
||
if err != nil {
|
||
s.serverError(w, "Nutzung konnte nicht geladen werden", err)
|
||
return
|
||
}
|
||
|
||
ohneSchlagwort, err := ws.Store.UntaggedEntries(ctx, project.ID, nutzungListenLaenge)
|
||
if err != nil {
|
||
s.serverError(w, "Einträge ohne Schlagwort konnten nicht geladen werden", err)
|
||
return
|
||
}
|
||
|
||
insLeere, err := ws.Store.MissingLinks(ctx, project.ID)
|
||
if err != nil {
|
||
s.serverError(w, "Verweise konnten nicht geladen werden", err)
|
||
return
|
||
}
|
||
|
||
keys, err := ws.Store.RecentlyUsedKeys(ctx, project.ID, nutzungKeysLaenge)
|
||
if err != nil {
|
||
s.serverError(w, "Zugänge konnten nicht geladen werden", err)
|
||
return
|
||
}
|
||
|
||
daten := usageData{
|
||
// Ein Projekt ohne Einträge und Schreibvorgänge fehlt in der map —
|
||
// dann liefert alle[project.ID] den Nullwert, also überall 0.
|
||
Summary: alle[project.ID],
|
||
WindowDays: store.UsageWindowDays,
|
||
Untagged: ohneSchlagwort,
|
||
Missing: insLeere,
|
||
Keys: keys,
|
||
}
|
||
|
||
// Die Verweise ins Leere kommen vollständig (die Gesamtzahl ist selbst
|
||
// eine Aussage), angezeigt werden nur die meistgenannten.
|
||
if len(daten.Missing) > nutzungListenLaenge {
|
||
daten.MissingMore = len(daten.Missing) - nutzungListenLaenge
|
||
daten.Missing = daten.Missing[:nutzungListenLaenge]
|
||
}
|
||
|
||
s.render(w, r, "nutzung.html", http.StatusOK, pageData{
|
||
Title: "Nutzung – " + project.Name,
|
||
Bereich: "nutzung",
|
||
Data: daten,
|
||
})
|
||
}
|