Public source validation / validate (push) Failing after 3m8s
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package backupapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/itworx/pulse/internal/auth"
|
|
"github.com/itworx/pulse/internal/backup"
|
|
)
|
|
|
|
type Handler struct {
|
|
Manager *backup.Manager
|
|
Audit func(context.Context, string, string) error
|
|
// OnCreated invalidates derived status caches after the archive and its
|
|
// checksum have both been written successfully.
|
|
OnCreated func()
|
|
}
|
|
|
|
type publicResult struct {
|
|
BackupID string `json:"backupId"`
|
|
SHA256 string `json:"sha256"`
|
|
Bytes int64 `json:"bytes"`
|
|
Rows int64 `json:"rows"`
|
|
Created time.Time `json:"createdAt"`
|
|
}
|
|
|
|
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
if h.Manager == nil {
|
|
writeError(w, http.StatusServiceUnavailable, "BACKUP_UNAVAILABLE", "Backup is not configured")
|
|
return
|
|
}
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
if r.URL.Path != "/api/v1/system/backups" {
|
|
writeError(w, http.StatusNotFound, "NOT_FOUND", "Not found")
|
|
return
|
|
}
|
|
result, err := h.Manager.List(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusServiceUnavailable, "BACKUP_UNAVAILABLE", "Backups are not available")
|
|
return
|
|
}
|
|
public := make([]publicResult, 0, len(result))
|
|
for _, item := range result {
|
|
public = append(public, toPublic(item))
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"backups": public})
|
|
case http.MethodPost:
|
|
if r.URL.Path != "/api/v1/system/backups" {
|
|
writeError(w, http.StatusNotFound, "NOT_FOUND", "Not found")
|
|
return
|
|
}
|
|
result, err := h.Manager.Create(r.Context())
|
|
principal, _ := auth.PrincipalFromContext(r.Context())
|
|
if err != nil {
|
|
if h.Audit != nil {
|
|
_ = h.Audit(r.Context(), principal.Subject, "failure")
|
|
}
|
|
status := http.StatusInternalServerError
|
|
code := "BACKUP_FAILED"
|
|
if errors.Is(err, backup.ErrNotConfigured) {
|
|
status = http.StatusServiceUnavailable
|
|
code = "BACKUP_UNAVAILABLE"
|
|
}
|
|
writeError(w, status, code, "Backup could not be created")
|
|
return
|
|
}
|
|
if h.Audit != nil {
|
|
_ = h.Audit(r.Context(), principal.Subject, "success")
|
|
}
|
|
if h.OnCreated != nil {
|
|
h.OnCreated()
|
|
}
|
|
writeJSON(w, http.StatusCreated, toPublic(result))
|
|
default:
|
|
writeError(w, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", "Method not allowed")
|
|
}
|
|
}
|
|
|
|
func toPublic(result backup.Result) publicResult {
|
|
return publicResult{BackupID: result.BackupID, SHA256: result.SHA256, Bytes: result.Bytes, Rows: result.Rows, Created: result.Created}
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, status int, value any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Cache-Control", "private, no-store")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(value)
|
|
}
|
|
|
|
func writeError(w http.ResponseWriter, status int, code, detail string) {
|
|
writeJSON(w, status, map[string]string{"code": code, "detail": detail})
|
|
}
|
|
|
|
var _ http.Handler = Handler{}
|