Skip to content

Commit 8c79316

Browse files
committed
feat: backend changes for file upload
1 parent 6242e77 commit 8c79316

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

lib/httpapi/models.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,14 @@ type MessageResponse struct {
6262
Ok bool `json:"ok" doc:"Indicates whether the message was sent successfully. For messages of type 'user', success means detecting that the agent began executing the task described. For messages of type 'raw', success means the keystrokes were sent to the terminal."`
6363
}
6464
}
65+
66+
type UploadResponse struct {
67+
Body struct {
68+
Ok bool `json:"ok" doc:"Indicates whether the files were uploaded successfully."`
69+
}
70+
}
71+
72+
type UploadRequest struct {
73+
UploadPath string `form:"uploadPath" required:"true" doc:"location where all the files in the zip will be extracted to."`
74+
Files huma.FormFile `form:"file" contentType:"application/zip" required:"true" doc:"zip of all the files that needs to be uploaded"`
75+
}

lib/httpapi/server.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package httpapi
22

33
import (
4+
"archive/zip"
5+
"bytes"
46
"context"
57
"encoding/json"
68
"fmt"
9+
"io"
710
"log/slog"
811
"net/http"
912
"net/url"
13+
"os"
14+
"path/filepath"
1015
"slices"
1116
"strings"
1217
"sync"
@@ -305,6 +310,10 @@ func (s *Server) registerRoutes() {
305310
o.Description = "Send a message to the agent. For messages of type 'user', the agent's status must be 'stable' for the operation to complete successfully. Otherwise, this endpoint will return an error."
306311
})
307312

313+
huma.Post(s.api, "/upload", s.uploadFiles, func(o *huma.Operation) {
314+
o.Description = "Upload files to the specified upload path."
315+
})
316+
308317
// GET /events endpoint
309318
sse.Register(s.api, huma.Operation{
310319
OperationID: "subscribeEvents",
@@ -391,6 +400,72 @@ func (s *Server) createMessage(ctx context.Context, input *MessageRequest) (*Mes
391400
return resp, nil
392401
}
393402

403+
// uploadFiles handles POST /upload
404+
func (s *Server) uploadFiles(ctx context.Context, input *struct {
405+
RawBody huma.MultipartFormFiles[UploadRequest]
406+
}) (*UploadResponse, error) {
407+
formData := input.RawBody.Data()
408+
409+
file := formData.Files.File
410+
411+
buf, err := io.ReadAll(file)
412+
if err != nil {
413+
return nil, xerrors.Errorf("failed to upload files: %w", err)
414+
}
415+
416+
// Create a zip.Reader from the buffer
417+
zipReader, err := zip.NewReader(bytes.NewReader(buf), int64(len(buf)))
418+
if err != nil {
419+
return nil, xerrors.Errorf("failed to upload files: %w", err)
420+
}
421+
422+
for _, f := range zipReader.File {
423+
outPath := filepath.Join(formData.UploadPath, f.Name)
424+
425+
if f.FileInfo().IsDir() {
426+
err := os.MkdirAll(outPath, f.Mode())
427+
if err != nil {
428+
return nil, xerrors.Errorf("failed to upload files: %w", err)
429+
}
430+
continue
431+
}
432+
433+
err := os.MkdirAll(filepath.Dir(outPath), f.Mode())
434+
if err != nil {
435+
return nil, xerrors.Errorf("failed to upload files: %w", err)
436+
}
437+
438+
outFile, err := os.OpenFile(outPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
439+
if err != nil {
440+
return nil, xerrors.Errorf("failed to upload files: %w", err)
441+
}
442+
443+
rc, err := f.Open()
444+
if err != nil {
445+
err := outFile.Close()
446+
if err != nil {
447+
return nil, xerrors.Errorf("failed to upload files: %w", err)
448+
}
449+
return nil, xerrors.Errorf("failed to upload files: %w", err)
450+
}
451+
452+
_, err = io.Copy(outFile, rc)
453+
err = rc.Close()
454+
if err != nil {
455+
return nil, xerrors.Errorf("failed to upload files: %w", err)
456+
}
457+
458+
err = outFile.Close()
459+
if err != nil {
460+
return nil, xerrors.Errorf("failed to upload files: %w", err)
461+
}
462+
}
463+
464+
resp := &UploadResponse{}
465+
resp.Body.Ok = true
466+
return resp, nil
467+
}
468+
394469
// subscribeEvents is an SSE endpoint that sends events to the client
395470
func (s *Server) subscribeEvents(ctx context.Context, input *struct{}, send sse.Sender) {
396471
subscriberId, ch, stateEvents := s.emitter.Subscribe()

0 commit comments

Comments
 (0)