package retentionapi import ( "encoding/json" "net/http" "time" "github.com/jackc/pgx/v5/pgxpool" "gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient" "gitea.perlbach24.de/scripte/nexarch/archive/internal/retentionengine" ) // retentionConfigurePermission ist das bei Core RBAC-02/RBAC-06 // geprüfte Recht für alle RET-06-API-Endpunkte. const retentionConfigurePermission = "retention.configure" // Mount registriert alle RET-06-API-Endpunkte auf mux, jeweils hinter // RequireRBAC (RET-08, echter RBAC-06-Aufruf) — Akzeptanzkriterium 3: // Änderungen an Fristen sind nur berechtigten Rollen zugänglich. func Mount(mux *http.ServeMux, pool *pgxpool.Pool, rbac *rbacclient.Client) { mux.HandleFunc("POST /retention-classes", RequireRBAC(rbac, retentionConfigurePermission, configureHandler(pool))) mux.HandleFunc("POST /retention-classes/{class}/deactivate", RequireRBAC(rbac, retentionConfigurePermission, deactivateHandler(pool))) mux.HandleFunc("GET /retention-classes", RequireRBAC(rbac, retentionConfigurePermission, listHandler(pool))) mux.HandleFunc("GET /retention-classes/preview", RequireRBAC(rbac, retentionConfigurePermission, previewHandler(pool))) } type configureRequest struct { RetentionClass string `json:"retention_class"` Duration string `json:"duration"` } // configureHandler: Aufbewahrungsklasse anlegen ODER ändern // (Akzeptanzkriterium 1) — `retentionengine.ConfigureClassRule` ist ein // UPSERT, eine Änderung wirkt erst ab jetzt auf künftige // Stichtagsberechnungen (Pflichtprüfung: nicht rückwirkend). func configureHandler(pool *pgxpool.Pool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req configureRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "ungültiger request-body: "+err.Error(), http.StatusBadRequest) return } if req.RetentionClass == "" || req.Duration == "" { http.Error(w, "retention_class und duration sind pflichtfelder", http.StatusBadRequest) return } if err := retentionengine.ConfigureClassRule(r.Context(), pool, req.RetentionClass, req.Duration); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } } // deactivateHandler: Aufbewahrungsklasse deaktivieren (Akzeptanzkriterium 1). func deactivateHandler(pool *pgxpool.Pool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { class := r.PathValue("class") if err := retentionengine.DeactivateClassRule(r.Context(), pool, class); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } w.WriteHeader(http.StatusOK) } } // listHandler liefert alle konfigurierten Aufbewahrungsklassen (aktiv // und deaktiviert) — Grundlage der künftigen Konfigurationsoberfläche. func listHandler(pool *pgxpool.Pool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { rules, err := retentionengine.ListClassRules(r.Context(), pool) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(rules) } } // previewHandler liefert die Vorschauliste bald ablaufender Objekte // (Akzeptanzkriterium 2: Standard 30 Tage, per `days`-Query-Parameter // überschreibbar). Nutzt DIESELBE `ListExpiringObjects`-Funktion wie // der periodische Job (RET-02) — Pflichtprüfung: Vorschauliste stimmt // mit dem Ergebnis des periodischen Jobs überein (keine zweite, // abweichende Implementierung). func previewHandler(pool *pgxpool.Pool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { days := 30 asOf := time.Now().UTC().AddDate(0, 0, days) objects, err := retentionengine.ListExpiringObjects(r.Context(), pool, asOf) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(objects) } }