193 lines
5.1 KiB
Go
193 lines
5.1 KiB
Go
package template
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type Repository interface {
|
|
List(ctx context.Context) ([]Template, error)
|
|
Get(ctx context.Context, id string) (Template, bool, error)
|
|
Upsert(ctx context.Context, t Template) (Template, error)
|
|
Delete(ctx context.Context, id string) (bool, error)
|
|
}
|
|
|
|
type Handler struct {
|
|
repository Repository
|
|
}
|
|
|
|
func NewHandler(repository Repository) Handler {
|
|
return Handler{repository: repository}
|
|
}
|
|
|
|
func (h Handler) ListTemplates(w http.ResponseWriter, r *http.Request) {
|
|
templates, err := h.repository.List(r.Context())
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "templates_list_failed")
|
|
return
|
|
}
|
|
if templates == nil {
|
|
templates = []Template{}
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string][]Template{"data": templates})
|
|
}
|
|
|
|
func (h Handler) CreateTemplate(w http.ResponseWriter, r *http.Request) {
|
|
var request createTemplateRequest
|
|
if !decodeJSON(w, r, &request) {
|
|
return
|
|
}
|
|
|
|
request.Name = strings.TrimSpace(request.Name)
|
|
if request.Name == "" {
|
|
writeError(w, http.StatusBadRequest, "name_required")
|
|
return
|
|
}
|
|
if request.ClusterID == "" {
|
|
writeError(w, http.StatusBadRequest, "cluster_id_required")
|
|
return
|
|
}
|
|
if request.ProxmoxTemplateVMID <= 0 {
|
|
writeError(w, http.StatusBadRequest, "proxmox_template_vmid_required")
|
|
return
|
|
}
|
|
|
|
saved, err := h.repository.Upsert(r.Context(), Template{
|
|
ClusterID: request.ClusterID,
|
|
Name: request.Name,
|
|
Description: request.Description,
|
|
ProxmoxTemplateVMID: request.ProxmoxTemplateVMID,
|
|
ProxmoxNode: request.ProxmoxNode,
|
|
})
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, "template_upsert_failed")
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusCreated, saved)
|
|
}
|
|
|
|
func (h Handler) UpdateTemplate(w http.ResponseWriter, r *http.Request) {
|
|
templateID := r.PathValue("templateID")
|
|
if templateID == "" {
|
|
writeError(w, http.StatusBadRequest, "template_id_required")
|
|
return
|
|
}
|
|
|
|
existing, found, err := h.repository.Get(r.Context(), templateID)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "template_get_failed")
|
|
return
|
|
}
|
|
if !found {
|
|
writeError(w, http.StatusNotFound, "template_not_found")
|
|
return
|
|
}
|
|
|
|
var request updateTemplateRequest
|
|
if !decodeJSON(w, r, &request) {
|
|
return
|
|
}
|
|
|
|
if request.ClusterID == nil {
|
|
request.ClusterID = &existing.ClusterID
|
|
}
|
|
if request.Name == nil {
|
|
request.Name = &existing.Name
|
|
} else {
|
|
trimmed := strings.TrimSpace(*request.Name)
|
|
request.Name = &trimmed
|
|
if *request.Name == "" {
|
|
writeError(w, http.StatusBadRequest, "name_required")
|
|
return
|
|
}
|
|
}
|
|
if request.Description == nil {
|
|
request.Description = &existing.Description
|
|
}
|
|
if request.ProxmoxTemplateVMID == nil {
|
|
request.ProxmoxTemplateVMID = &existing.ProxmoxTemplateVMID
|
|
} else if *request.ProxmoxTemplateVMID <= 0 {
|
|
writeError(w, http.StatusBadRequest, "proxmox_template_vmid_required")
|
|
return
|
|
}
|
|
if request.ProxmoxNode == nil {
|
|
request.ProxmoxNode = &existing.ProxmoxNode
|
|
}
|
|
|
|
saved, err := h.repository.Upsert(r.Context(), Template{
|
|
ID: existing.ID,
|
|
ClusterID: *request.ClusterID,
|
|
Name: *request.Name,
|
|
Description: *request.Description,
|
|
ProxmoxTemplateVMID: *request.ProxmoxTemplateVMID,
|
|
ProxmoxNode: *request.ProxmoxNode,
|
|
})
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, "template_upsert_failed")
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, saved)
|
|
}
|
|
|
|
func (h Handler) DeleteTemplate(w http.ResponseWriter, r *http.Request) {
|
|
templateID := r.PathValue("templateID")
|
|
if templateID == "" {
|
|
writeError(w, http.StatusBadRequest, "template_id_required")
|
|
return
|
|
}
|
|
|
|
deleted, err := h.repository.Delete(r.Context(), templateID)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "template_delete_failed")
|
|
return
|
|
}
|
|
if !deleted {
|
|
writeError(w, http.StatusNotFound, "template_not_found")
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
type createTemplateRequest struct {
|
|
ClusterID string `json:"cluster_id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
ProxmoxTemplateVMID int `json:"proxmox_template_vmid"`
|
|
ProxmoxNode string `json:"proxmox_node"`
|
|
}
|
|
|
|
type updateTemplateRequest struct {
|
|
ClusterID *string `json:"cluster_id"`
|
|
Name *string `json:"name"`
|
|
Description *string `json:"description"`
|
|
ProxmoxTemplateVMID *int `json:"proxmox_template_vmid"`
|
|
ProxmoxNode *string `json:"proxmox_node"`
|
|
}
|
|
|
|
func decodeJSON(w http.ResponseWriter, r *http.Request, target any) bool {
|
|
defer r.Body.Close()
|
|
decoder := json.NewDecoder(r.Body)
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(target); err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid_json")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, status int, body any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(body)
|
|
}
|
|
|
|
func writeError(w http.ResponseWriter, status int, message string) {
|
|
writeJSON(w, status, map[string]string{"error": message})
|
|
}
|