package handlers import ( "encoding/json" "log" "net/http" "strings" "spore-registry/internal/models" "spore-registry/internal/service" ) // FirmwareHandler handles HTTP requests for firmware operations type FirmwareHandler struct { service *service.FirmwareService } // NewFirmwareHandler creates a new firmware handler func NewFirmwareHandler(service *service.FirmwareService) *FirmwareHandler { return &FirmwareHandler{service: service} } // UploadFirmware handles POST /firmware endpoint func (h *FirmwareHandler) UploadFirmware(w http.ResponseWriter, r *http.Request) { log.Printf("POST /firmware - %s", r.RemoteAddr) if r.Method != http.MethodPost { log.Printf("Invalid method %s for /firmware", r.Method) http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Parse multipart form err := r.ParseMultipartForm(32 << 20) // 32MB max if err != nil { log.Printf("Failed to parse multipart form: %v", err) http.Error(w, "Failed to parse form: "+err.Error(), http.StatusBadRequest) return } // Get metadata from form metadataJSON := r.FormValue("metadata") if metadataJSON == "" { log.Printf("Missing metadata field in firmware upload") http.Error(w, "Missing metadata field", http.StatusBadRequest) return } var metadata models.FirmwareMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { log.Printf("Invalid metadata JSON: %v", err) http.Error(w, "Invalid metadata JSON: "+err.Error(), http.StatusBadRequest) return } // Get firmware file file, _, err := r.FormFile("firmware") if err != nil { log.Printf("Missing firmware file in upload: %v", err) http.Error(w, "Missing firmware file: "+err.Error(), http.StatusBadRequest) return } defer file.Close() log.Printf("Uploading firmware: %s/%s", metadata.Name, metadata.Version) // Upload firmware using service response, err := h.service.UploadFirmware(metadata, file) if err != nil { log.Printf("Failed to upload firmware: %v", err) http.Error(w, "Failed to upload firmware: "+err.Error(), http.StatusInternalServerError) return } log.Printf("Successfully uploaded firmware: %s/%s (size: %d bytes)", metadata.Name, metadata.Version, response.Size) // Return success response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(response) } // ListFirmware handles GET /firmware endpoint with optional query parameters func (h *FirmwareHandler) ListFirmware(w http.ResponseWriter, r *http.Request) { log.Printf("GET /firmware - %s (name=%s, version=%s)", r.RemoteAddr, r.URL.Query().Get("name"), r.URL.Query().Get("version")) if r.Method != http.MethodGet { log.Printf("Invalid method %s for /firmware", r.Method) http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Parse query parameters name := r.URL.Query().Get("name") version := r.URL.Query().Get("version") // Get firmware records using service grouped, err := h.service.ListFirmware(name, version) if err != nil { log.Printf("Failed to retrieve firmware list: %v", err) http.Error(w, "Failed to retrieve firmware list: "+err.Error(), http.StatusInternalServerError) return } log.Printf("Returning %d firmware groups", len(grouped)) // Return grouped firmware list w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(grouped) } // UpdateFirmwareMetadata handles PUT /firmware/{name}/{version} endpoint for metadata-only updates func (h *FirmwareHandler) UpdateFirmwareMetadata(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPut { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Extract path parameters path := strings.TrimPrefix(r.URL.Path, "/firmware/") parts := strings.Split(path, "/") if len(parts) != 2 { log.Printf("Invalid firmware path for update: %s", r.URL.Path) http.Error(w, "Invalid firmware path", http.StatusBadRequest) return } name := parts[0] version := parts[1] log.Printf("PUT /firmware/%s/%s - %s", name, version, r.RemoteAddr) // Parse JSON body var metadata models.FirmwareMetadata if err := json.NewDecoder(r.Body).Decode(&metadata); err != nil { log.Printf("Invalid JSON in metadata update: %v", err) http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest) return } // Update metadata using service response, err := h.service.UpdateFirmwareMetadata(name, version, metadata) if err != nil { if strings.Contains(err.Error(), "not found") { log.Printf("Firmware not found for update: %s/%s", name, version) http.Error(w, "Firmware not found", http.StatusNotFound) } else { log.Printf("Failed to update firmware metadata: %v", err) http.Error(w, "Failed to update firmware metadata: "+err.Error(), http.StatusInternalServerError) } return } log.Printf("Successfully updated firmware metadata: %s/%s", name, version) // Return success response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(response) } // DownloadFirmware handles GET /firmware/{name}/{version} endpoint func (h *FirmwareHandler) DownloadFirmware(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Extract path parameters path := strings.TrimPrefix(r.URL.Path, "/firmware/") parts := strings.Split(path, "/") if len(parts) != 2 { log.Printf("Invalid firmware path: %s", r.URL.Path) http.Error(w, "Invalid firmware path", http.StatusBadRequest) return } name := parts[0] version := parts[1] log.Printf("GET /firmware/%s/%s - %s", name, version, r.RemoteAddr) // Get firmware file path using service filePath, err := h.service.GetFirmwarePath(name, version) if err != nil { if strings.Contains(err.Error(), "not found") { log.Printf("Firmware not found: %s/%s", name, version) http.Error(w, "Firmware not found", http.StatusNotFound) } else { log.Printf("Failed to get firmware path: %v", err) http.Error(w, "Failed to get firmware: "+err.Error(), http.StatusInternalServerError) } return } // Serve the file log.Printf("Serving firmware file: %s", filePath) http.ServeFile(w, r, filePath) } // DeleteFirmware handles DELETE /firmware/{name}/{version} endpoint func (h *FirmwareHandler) DeleteFirmware(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Extract path parameters path := strings.TrimPrefix(r.URL.Path, "/firmware/") parts := strings.Split(path, "/") if len(parts) != 2 { log.Printf("Invalid firmware path for delete: %s", r.URL.Path) http.Error(w, "Invalid firmware path", http.StatusBadRequest) return } name := parts[0] version := parts[1] log.Printf("DELETE /firmware/%s/%s - %s", name, version, r.RemoteAddr) // Delete firmware using service if err := h.service.DeleteFirmware(name, version); err != nil { if strings.Contains(err.Error(), "not found") { log.Printf("Firmware not found for deletion: %s/%s", name, version) http.Error(w, "Firmware not found", http.StatusNotFound) } else { log.Printf("Failed to delete firmware: %v", err) http.Error(w, "Failed to delete firmware: "+err.Error(), http.StatusInternalServerError) } return } log.Printf("Successfully deleted firmware: %s/%s", name, version) // Return success response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, "message": "Firmware deleted successfully", "name": name, "version": version, }) } // HealthCheck provides a simple health check endpoint func (h *FirmwareHandler) HealthCheck(w http.ResponseWriter, r *http.Request) { response := models.HealthResponse{Status: "healthy"} w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) }