package vm import ( "context" "encoding/json" "net/http" "time" "proxui/backend/internal/auth" ) type Repository interface { ListProjectVMs(ctx context.Context, profileID string, projectID string) ([]VM, bool, error) GetVM(ctx context.Context, profileID string, vmID string) (VM, bool, error) GetProjectInfo(ctx context.Context, profileID string, projectID string) (ProjectInfo, bool, error) CheckQuota(ctx context.Context, projectID string) (QuotaInfo, error) ReserveNextVMID(ctx context.Context, clusterID string) (int, error) InsertVM(ctx context.Context, vm InsertVMRecord) (VM, error) } type Handler struct { repository Repository power PowerDependencies provision ProvisionDependencies } type Option func(*Handler) func WithPower(dependencies PowerDependencies) Option { return func(h *Handler) { h.power = dependencies } } func WithProvision(dependencies ProvisionDependencies) Option { return func(h *Handler) { h.provision = dependencies } } func NewHandler(repository Repository, opts ...Option) Handler { handler := Handler{repository: repository} for _, opt := range opts { opt(&handler) } return handler } func (h Handler) ListProjectVMs(w http.ResponseWriter, r *http.Request) { principal, ok := auth.PrincipalFromRequest(r) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } projectID := r.PathValue("projectID") if projectID == "" { writeError(w, http.StatusBadRequest, "project_id_required") return } vms, found, err := h.repository.ListProjectVMs(r.Context(), principal.Subject, projectID) if err != nil { writeError(w, http.StatusInternalServerError, "vm_list_failed") return } if !found { writeError(w, http.StatusNotFound, "project_not_found") return } writeJSON(w, http.StatusOK, map[string][]VM{"data": vms}) } func (h Handler) GetVM(w http.ResponseWriter, r *http.Request) { principal, ok := auth.PrincipalFromRequest(r) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } vmID := r.PathValue("vmID") if vmID == "" { writeError(w, http.StatusBadRequest, "vm_id_required") return } vm, found, err := h.repository.GetVM(r.Context(), principal.Subject, vmID) if err != nil { writeError(w, http.StatusInternalServerError, "vm_get_failed") return } if !found { writeError(w, http.StatusNotFound, "vm_not_found") return } writeJSON(w, http.StatusOK, vm) } type VM struct { ID string `json:"id"` ProjectID string `json:"project_id"` TenantID string `json:"tenant_id"` ClusterID string `json:"cluster_id"` ProxmoxVMID int `json:"proxmox_vmid"` Node string `json:"node"` Name string `json:"name"` Status string `json:"status"` VCPU int `json:"vcpu"` RAMMB int `json:"ram_mb"` DiskGB int `json:"disk_gb"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` ProjectName string `json:"project_name"` MembershipRole string `json:"membership_role"` } 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}) }