init
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package adminapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/iceBear67/simplepages/api"
|
||||
"github.com/iceBear67/simplepages/internal/httpx"
|
||||
"github.com/iceBear67/simplepages/internal/version"
|
||||
)
|
||||
|
||||
// whoami handles GET /api/v1/whoami. Any valid key may call it; a CI job uses
|
||||
// it to check that the token it was handed is the one it expected.
|
||||
func (s *Server) whoami(w http.ResponseWriter, r *http.Request) error {
|
||||
ident, err := s.identity(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := api.WhoAmI{
|
||||
KeyID: ident.KeyID,
|
||||
Scope: string(ident.Scope),
|
||||
Name: ident.Name,
|
||||
ExpiresAt: copyTime(ident.ExpiresAt),
|
||||
}
|
||||
if ident.ProjectID != nil {
|
||||
// The identity carries the row id, not the name; the name is what a
|
||||
// human wants to see. A missing row here would mean the project was
|
||||
// deleted between authentication and now, in which case reporting an
|
||||
// empty name is more honest than failing the request.
|
||||
if p, err := s.DB.ProjectByID(r.Context(), *ident.ProjectID); err == nil {
|
||||
out.Project = p.Name
|
||||
}
|
||||
}
|
||||
noStore(w)
|
||||
httpx.WriteJSON(w, http.StatusOK, out)
|
||||
return nil
|
||||
}
|
||||
|
||||
// systemInfo handles GET /api/v1/system/info, admin only.
|
||||
func (s *Server) systemInfo(w http.ResponseWriter, r *http.Request) error {
|
||||
counts, err := s.DB.Counts(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schema, err := s.DB.SchemaVersion(r.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
linkMode := s.LinkMode
|
||||
if linkMode == "" {
|
||||
linkMode = "unknown"
|
||||
}
|
||||
uptime := int64(0)
|
||||
if !s.Started.IsZero() {
|
||||
uptime = int64(time.Since(s.Started).Seconds())
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, api.SystemInfo{
|
||||
Version: version.Short(),
|
||||
UptimeS: uptime,
|
||||
Projects: counts.Projects,
|
||||
Deployments: counts.Deployments,
|
||||
Blobs: counts.Blobs,
|
||||
CASBytes: counts.CASBytes,
|
||||
LinkMode: linkMode,
|
||||
SchemaVer: schema,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// gc handles POST /api/v1/gc, admin only.
|
||||
//
|
||||
// A collection pass also runs on a timer; this exists so an operator who needs
|
||||
// the disk back does not have to wait for it, and so that a dry run can answer
|
||||
// "what would you delete" before anything is deleted.
|
||||
func (s *Server) gc(w http.ResponseWriter, r *http.Request) error {
|
||||
var req api.GCRequest
|
||||
if r.ContentLength != 0 {
|
||||
if err := httpx.DecodeJSON(w, r, s.maxJSON(), &req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
stats, err := s.Deploy.Collect(r.Context(), req.DryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, stats)
|
||||
return nil
|
||||
}
|
||||
|
||||
// fsck handles POST /api/v1/fsck, admin only.
|
||||
//
|
||||
// Refcounts are maintained by triggers, so on a healthy server this always
|
||||
// reports zero drift. It exists for the cases outside normal operation — a
|
||||
// database restored from a backup, a schema touched by hand — because the
|
||||
// collector trusts those counters, and a count that reads low is the one way
|
||||
// this design can lose data.
|
||||
func (s *Server) fsck(w http.ResponseWriter, r *http.Request) error {
|
||||
var req api.FsckRequest
|
||||
if r.ContentLength != 0 {
|
||||
if err := httpx.DecodeJSON(w, r, s.maxJSON(), &req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
rep, err := s.DB.Fsck(r.Context(), req.Repair)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := api.FsckReport{
|
||||
Blobs: rep.Blobs,
|
||||
DriftCount: rep.DriftCount,
|
||||
Repaired: rep.Repaired,
|
||||
Drift: make([]api.BlobDrift, 0, len(rep.Drift)),
|
||||
}
|
||||
for _, d := range rep.Drift {
|
||||
out.Drift = append(out.Drift, api.BlobDrift{
|
||||
Digest: d.Digest.String(), Stored: d.Stored, Actual: d.Actual,
|
||||
})
|
||||
}
|
||||
if rep.DriftCount > 0 {
|
||||
s.Log.WarnContext(r.Context(), "blob reference counts disagree with the manifests",
|
||||
"blobs", rep.Blobs, "drift", rep.DriftCount, "repaired", rep.Repaired)
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, out)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user