Phases 5, 6, 8: OCI pull/push paths and admin REST API
Phase 5 (OCI pull): internal/oci/ package with manifest GET/HEAD by tag/digest, blob GET/HEAD with repo membership check, tag listing with OCI pagination, catalog listing. Multi-segment repo names via parseOCIPath() right-split routing. DB query layer in internal/db/repository.go. Phase 6 (OCI push): blob uploads (monolithic and chunked) with uploadManager tracking in-progress BlobWriters, manifest push implementing full ARCHITECTURE.md §5 flow in a single SQLite transaction (create repo, upsert manifest, populate manifest_blobs, atomic tag move). Digest verification on both blob commit and manifest push-by-digest. Phase 8 (admin REST): /v1 endpoints for auth (login/logout/health), repository management (list/detail/delete), policy CRUD with engine reload, audit log listing with filters, GC trigger/status stubs. RequireAdmin middleware, platform-standard error format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
66
internal/server/admin_gc.go
Normal file
66
internal/server/admin_gc.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// GCLastRun records the result of the last GC run.
|
||||
type GCLastRun struct {
|
||||
StartedAt string `json:"started_at"`
|
||||
CompletedAt string `json:"completed_at,omitempty"`
|
||||
BlobsRemoved int `json:"blobs_removed"`
|
||||
BytesFreed int64 `json:"bytes_freed"`
|
||||
}
|
||||
|
||||
// GCState tracks the current state of garbage collection.
|
||||
type GCState struct {
|
||||
mu sync.Mutex
|
||||
Running bool `json:"running"`
|
||||
LastRun *GCLastRun `json:"last_run,omitempty"`
|
||||
}
|
||||
|
||||
type gcStatusResponse struct {
|
||||
Running bool `json:"running"`
|
||||
LastRun *GCLastRun `json:"last_run,omitempty"`
|
||||
}
|
||||
|
||||
type gcTriggerResponse struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// AdminTriggerGCHandler handles POST /v1/gc.
|
||||
func AdminTriggerGCHandler(state *GCState) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, _ *http.Request) {
|
||||
state.mu.Lock()
|
||||
if state.Running {
|
||||
state.mu.Unlock()
|
||||
writeAdminError(w, http.StatusConflict, "garbage collection already running")
|
||||
return
|
||||
}
|
||||
state.Running = true
|
||||
state.mu.Unlock()
|
||||
|
||||
// GC engine is Phase 9 -- for now, just mark as running and return.
|
||||
// The actual GC goroutine will be wired up in Phase 9.
|
||||
gcID := uuid.New().String()
|
||||
|
||||
writeJSON(w, http.StatusAccepted, gcTriggerResponse{ID: gcID})
|
||||
}
|
||||
}
|
||||
|
||||
// AdminGCStatusHandler handles GET /v1/gc/status.
|
||||
func AdminGCStatusHandler(state *GCState) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, _ *http.Request) {
|
||||
state.mu.Lock()
|
||||
resp := gcStatusResponse{
|
||||
Running: state.Running,
|
||||
LastRun: state.LastRun,
|
||||
}
|
||||
state.mu.Unlock()
|
||||
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user