package httpserver import ( "context" "net/http" "strings" "git.yuntuo.hk/k2safe/orbit/internal/fault" "git.yuntuo.hk/k2safe/orbit/internal/retention" "git.yuntuo.hk/k2safe/orbit/platform/auth" ) // RegisterConsoleRetention 注册保留策略配置、候选预览和只读归档导出。 // 所有接口只接受控制台 HttpOnly 会话,不接受业务 Bearer token。 func RegisterConsoleRetention(server *http.Server, consoleAuth RetentionConsoleAuth, store retention.Store, archive retention.ArchiveStore) { if server == nil || consoleAuth == nil || store == nil { return } mux, ok := server.Handler.(*http.ServeMux) if !ok { return } check := func(w http.ResponseWriter, r *http.Request) (auth.Token, bool) { t, ok := consoleCookieToken(r) if !ok || consoleAuth.ValidateSession(r.Context(), t) != nil { writeError(w, "", fault.Unauthorized) return auth.Token{}, false } return t, true } mux.HandleFunc("GET /console/retention", func(w http.ResponseWriter, r *http.Request) { t, ok := check(w, r) if !ok { return } p, err := store.GetRetentionPolicy(r.Context(), t.NamespaceID) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": p}) }) mux.HandleFunc("PUT /console/retention", func(w http.ResponseWriter, r *http.Request) { t, ok := check(w, r) if !ok { return } var in retention.UpdateInput if err := decodeBody(r, &in); err != nil { writeError(w, "", err) return } keys := r.Header.Values("Idempotency-Key") if len(keys) != 1 { writeError(w, "", fault.Invalid("exactly one Idempotency-Key is required")) return } p, err := store.UpdateRetentionPolicy(r.Context(), t, keys[0], in) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": p}) }) if archive == nil { return } mux.HandleFunc("GET /console/retention/preview", func(w http.ResponseWriter, r *http.Request) { t, ok := check(w, r) if !ok { return } p, err := archive.PreviewRetention(r.Context(), t) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": p}) }) mux.HandleFunc("POST /console/retention/export", func(w http.ResponseWriter, r *http.Request) { t, ok := check(w, r) if !ok { return } var in retention.ArchiveInput if err := decodeBody(r, &in); err != nil { writeError(w, "", err) return } if strings.TrimSpace(in.Kind) == "" { writeError(w, "", fault.Invalid("archive kind is required")) return } snapshot, err := archive.ExportRetention(r.Context(), t, in) if err != nil { writeError(w, "", err) return } writeConsoleJSON(w, 200, map[string]any{"data": snapshot}) }) } // RetentionConsoleAuth keeps route registration independent from the concrete DB auth type. type RetentionConsoleAuth interface { ValidateSession(context.Context, auth.Token) error }