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>
67 lines
1.6 KiB
Go
67 lines
1.6 KiB
Go
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)
|
|
}
|
|
}
|