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>
61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type adminLoginRequest struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type adminLoginResponse struct {
|
|
Token string `json:"token"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
}
|
|
|
|
// AdminLoginHandler handles POST /v1/auth/login.
|
|
func AdminLoginHandler(loginClient LoginClient) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
var req adminLoginRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeAdminError(w, http.StatusBadRequest, "invalid request body")
|
|
return
|
|
}
|
|
if req.Username == "" || req.Password == "" {
|
|
writeAdminError(w, http.StatusBadRequest, "username and password required")
|
|
return
|
|
}
|
|
|
|
token, expiresIn, err := loginClient.Login(req.Username, req.Password)
|
|
if err != nil {
|
|
writeAdminError(w, http.StatusUnauthorized, "authentication failed")
|
|
return
|
|
}
|
|
|
|
expiresAt := time.Now().UTC().Add(time.Duration(expiresIn) * time.Second).Format(time.RFC3339)
|
|
writeJSON(w, http.StatusOK, adminLoginResponse{
|
|
Token: token,
|
|
ExpiresAt: expiresAt,
|
|
})
|
|
}
|
|
}
|
|
|
|
// AdminLogoutHandler handles POST /v1/auth/logout.
|
|
func AdminLogoutHandler() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, _ *http.Request) {
|
|
// MCIAS token revocation is not currently supported.
|
|
// The client should discard the token.
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
}
|
|
|
|
// AdminHealthHandler handles GET /v1/health.
|
|
func AdminHealthHandler() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
|
|
}
|
|
}
|