CI / changes (push) Successful in 6s
CI / commitlint (push) Skipped
CI / openapi (push) Successful in 27s
CI / web (push) Successful in 51s
CI / go (push) Successful in 2m19s
CI / bird2 (push) Successful in 13s
CI / release (push) Successful in 4m24s
Added support for portal JWT authentication, enabling single sign-on (SSO) capabilities. Updated the application to handle JWT claims for user permissions and roles, enhancing security and access control. Refactored relevant components and API routes to accommodate the new authentication flow, ensuring a seamless user experience. Updated documentation to reflect the new authentication requirements and configurations. Co-authored-by: Cursor <[email protected]>
102 lines
3.2 KiB
Go
102 lines
3.2 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"evobgp/internal/pipeline"
|
|
)
|
|
|
|
func (s *Server) resolveRevisionRetentionMinutesQuery(r *http.Request, tenantID string) (minutes int, ok bool) {
|
|
if raw := r.URL.Query().Get("retention_minutes"); raw != "" {
|
|
n, err := strconv.Atoi(raw)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
if n < pipeline.RevisionMinTTLMin || n > pipeline.RevisionMaxTTLMin {
|
|
return 0, false
|
|
}
|
|
return n, true
|
|
}
|
|
return s.defaultRevisionRetentionMinutes(tenantID)
|
|
}
|
|
|
|
func (s *Server) defaultRevisionRetentionMinutes(tenantID string) (int, bool) {
|
|
settings, err := s.store.ListGlobalSettings(tenantID)
|
|
if err != nil {
|
|
return pipeline.ClampRevisionRetentionMinutes(0), true
|
|
}
|
|
return pipeline.ClampRevisionRetentionMinutes(pipeline.RevisionRetentionMinutesFromSettings(settings)), true
|
|
}
|
|
|
|
func (s *Server) resolveRevisionRetentionMinutesBody(r *http.Request, tenantID string) (minutes int, ok bool) {
|
|
var body struct {
|
|
RetentionMinutes *int `json:"retention_minutes"`
|
|
}
|
|
if r.Body != nil && r.ContentLength != 0 {
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
return 0, false
|
|
}
|
|
if body.RetentionMinutes != nil {
|
|
m := *body.RetentionMinutes
|
|
if m < pipeline.RevisionMinTTLMin || m > pipeline.RevisionMaxTTLMin {
|
|
return 0, false
|
|
}
|
|
return m, true
|
|
}
|
|
}
|
|
return s.defaultRevisionRetentionMinutes(tenantID)
|
|
}
|
|
|
|
func (s *Server) handleRevisionPruneEstimate(w http.ResponseWriter, r *http.Request) {
|
|
a, ok := authFromContext(r.Context())
|
|
if !ok || !s.requirePerm(w, a, "bgp:operations:read") {
|
|
return
|
|
}
|
|
minutes, valid := s.resolveRevisionRetentionMinutesQuery(r, a.TenantID)
|
|
if !valid {
|
|
writeProblem(w, http.StatusUnprocessableEntity, "Unprocessable Entity", "retention_minutes must be an integer in range 15..43200")
|
|
return
|
|
}
|
|
cutoff := pipeline.RevisionCutoffFromMinutes(minutes)
|
|
est, err := s.store.EstimateRevisionPrune(a.TenantID, cutoff, minutes)
|
|
if err != nil {
|
|
writeInternalError(w, "revision prune estimate", err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"retention_minutes": est.RetentionMinutes,
|
|
"cutoff_at": est.CutoffAt.UTC().Format(time.RFC3339Nano),
|
|
"revision_count": est.RevisionCount,
|
|
"prefix_row_count": est.PrefixRowCount,
|
|
"orphan_snapshot_count": est.OrphanSnapshotCount,
|
|
"bytes_estimate": est.BytesEstimate,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleRevisionPrune(w http.ResponseWriter, r *http.Request) {
|
|
a, ok := authFromContext(r.Context())
|
|
if !ok || !s.requirePerm(w, a, "bgp:operations:admin") {
|
|
return
|
|
}
|
|
minutes, valid := s.resolveRevisionRetentionMinutesBody(r, a.TenantID)
|
|
if !valid {
|
|
writeProblem(w, http.StatusUnprocessableEntity, "Unprocessable Entity", "retention_minutes must be an integer in range 15..43200")
|
|
return
|
|
}
|
|
cutoff := pipeline.RevisionCutoffFromMinutes(minutes)
|
|
res, err := s.store.PruneRevisionsWithStats(a.TenantID, cutoff)
|
|
if err != nil {
|
|
writeInternalError(w, "revision prune", err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"deleted_revisions": res.DeletedRevisions,
|
|
"deleted_prefix_snapshots": res.DeletedPrefixSnapshots,
|
|
"deleted_prefix_rows": res.DeletedPrefixRows,
|
|
"bytes_estimate": res.BytesEstimate,
|
|
})
|
|
}
|