--- name: upload_handler kind: pipeline lang: go domain: infra version: "1.0.0" purity: impure signature: "func UploadHandler(cfg StorageConfig) http.HandlerFunc" description: "HTTP handler completo para multipart upload. Compone UploadParse + FileValidateType + FileSaveDisk segun StorageConfig. Responde JSON con los UploadedFile guardados o un HTTPError estructurado en caso de fallo." tags: [http, upload, multipart, handler, pipeline, infra, pendiente-usar] uses_functions: [upload_parse_go_infra, file_validate_type_go_infra, file_save_disk_go_infra, http_json_response_go_infra, http_error_response_go_infra] uses_types: [StorageConfig_go_infra, UploadedFile_go_infra, HTTPError_go_infra] returns: [] returns_optional: false error_type: "error_go_core" imports: [net/http] params: - name: cfg desc: "StorageConfig con BaseDir, MaxFileSize y AllowedTypes" output: "http.HandlerFunc lista para montar como ruta. Responde 200 con {\"files\":[UploadedFile,...]}, 400 parse_error, 415 invalid_type o 500 save_error" tested: true tests: ["acepta upload con multiple imagenes y responde JSON", "rechaza tipo no permitido con 415", "rechaza body que excede MaxFileSize con 400"] test_file_path: "functions/infra/upload_handler_test.go" file_path: "functions/infra/upload_handler.go" --- ## Ejemplo ```go cfg := StorageConfig{ BaseDir: "./uploads", MaxFileSize: 10 << 20, AllowedTypes: []string{"image/png", "image/jpeg", "application/pdf"}, } mux := HTTPRouter([]Route{ {Method: "POST", Path: "/api/upload", Handler: UploadHandler(cfg)}, }) http.ListenAndServe(":8080", mux) ``` ## Notas Pipeline de 5 funciones — compone parse + validate + save + json/error responses. El campo `ContentType` del `UploadedFile` retornado se sobreescribe con el MIME REAL detectado por magic bytes (ignorando el Content-Type del request), no con el inferido por extension. Si un archivo del batch falla, el handler responde error y NO continua. Las funciones que ya se guardaron en disco quedan ahi (no hay rollback transaccional).