Files
2026-06-12 14:25:34 +02:00

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})
}