149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
agentbackup "zlh-agent/internal/backup"
|
|
"zlh-agent/internal/state"
|
|
)
|
|
|
|
var loadBackupConfig = state.LoadConfig
|
|
var restoreBackup = agentbackup.Restore
|
|
|
|
func HandleGameBackups(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
handleGameBackupsList(w, r)
|
|
case http.MethodPost:
|
|
handleGameBackupCreate(w, r)
|
|
default:
|
|
writeJSONError(w, http.StatusMethodNotAllowed, "GET or POST only")
|
|
}
|
|
}
|
|
|
|
func HandleGameBackupByID(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodDelete {
|
|
writeJSONError(w, http.StatusMethodNotAllowed, "DELETE only")
|
|
return
|
|
}
|
|
endOp, ok := beginHandlerOperation(w, "backup_delete", true, "deleting backup")
|
|
if !ok {
|
|
return
|
|
}
|
|
defer endOp()
|
|
|
|
if _, ok := requireBackupConfig(w); !ok {
|
|
return
|
|
}
|
|
|
|
id := strings.TrimPrefix(r.URL.Path, "/game/backups/")
|
|
id = strings.TrimSpace(strings.Trim(id, "/"))
|
|
if id == "" || strings.Contains(id, "/") {
|
|
writeJSONError(w, http.StatusBadRequest, "backup id required")
|
|
return
|
|
}
|
|
|
|
if err := agentbackup.Delete(id); err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
writeJSONError(w, http.StatusNotFound, "backup not found")
|
|
return
|
|
}
|
|
writeJSONError(w, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"deleted": true, "id": id})
|
|
}
|
|
|
|
func handleGameBackupsList(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := requireBackupConfig(w); !ok {
|
|
return
|
|
}
|
|
backups, err := agentbackup.List()
|
|
if err != nil {
|
|
writeJSONError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"backups": backups})
|
|
}
|
|
|
|
func handleGameBackupCreate(w http.ResponseWriter, r *http.Request) {
|
|
endOp, ok := beginHandlerOperation(w, "backup_create", true, "creating backup")
|
|
if !ok {
|
|
return
|
|
}
|
|
defer endOp()
|
|
|
|
cfg, ok := requireBackupConfig(w)
|
|
if !ok {
|
|
return
|
|
}
|
|
manifest, err := agentbackup.Create(cfg)
|
|
if err != nil {
|
|
writeJSONError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, manifest)
|
|
}
|
|
|
|
func HandleGameBackupRestore(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
writeJSONError(w, http.StatusMethodNotAllowed, "POST only")
|
|
return
|
|
}
|
|
endOp, ok := beginHandlerOperation(w, "backup_restore", true, "restoring backup")
|
|
if !ok {
|
|
return
|
|
}
|
|
defer endOp()
|
|
|
|
cfg, ok := requireBackupConfig(w)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
id := strings.TrimSpace(r.URL.Query().Get("id"))
|
|
if id == "" {
|
|
var req struct {
|
|
ID string `json:"id"`
|
|
}
|
|
_ = json.NewDecoder(r.Body).Decode(&req)
|
|
id = strings.TrimSpace(req.ID)
|
|
}
|
|
if id == "" {
|
|
writeJSONError(w, http.StatusBadRequest, "backup id required")
|
|
return
|
|
}
|
|
|
|
result, err := restoreBackup(cfg, id)
|
|
if err != nil {
|
|
writeJSONError(w, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"restored": true,
|
|
"backup": result.Backup,
|
|
"checkpoint": result.Checkpoint,
|
|
})
|
|
}
|
|
|
|
func requireBackupConfig(w http.ResponseWriter) (*state.Config, bool) {
|
|
cfg, err := loadBackupConfig()
|
|
if err != nil {
|
|
writeJSONError(w, http.StatusBadRequest, "no config loaded")
|
|
return nil, false
|
|
}
|
|
if !strings.EqualFold(cfg.ContainerType, "game") {
|
|
writeJSONError(w, http.StatusBadRequest, "not a game container")
|
|
return nil, false
|
|
}
|
|
if !strings.EqualFold(cfg.Game, "minecraft") {
|
|
writeJSONError(w, http.StatusNotImplemented, "backups are only implemented for minecraft")
|
|
return nil, false
|
|
}
|
|
return cfg, true
|
|
}
|