From e8706f529f16b7048ba0784e8ad9270efc74984a Mon Sep 17 00:00:00 2001 From: Sumanth Date: Wed, 11 Feb 2026 23:43:31 +0530 Subject: [PATCH 1/4] feat(model): Create a new struct for GetDropMetadataResponse --- internal/api/{handler_upload.go => handlers_upload.go} | 0 internal/api/models.go | 8 ++++++++ 2 files changed, 8 insertions(+) rename internal/api/{handler_upload.go => handlers_upload.go} (100%) diff --git a/internal/api/handler_upload.go b/internal/api/handlers_upload.go similarity index 100% rename from internal/api/handler_upload.go rename to internal/api/handlers_upload.go diff --git a/internal/api/models.go b/internal/api/models.go index e4389cc..35713ab 100644 --- a/internal/api/models.go +++ b/internal/api/models.go @@ -28,3 +28,11 @@ type ChunkUploadResponse struct { // 2. Server responds with CreateDropResponse (drop ID and expiration time) // 3. CLI uploads file chunks to /api/v1/drops/{drop_id}/chunks (POST) with ChunkUploadResponse confirming each chunk +// GetDropMetadataResponse is what the server sends back when the CLI requests metadata about a drop +type GetDropMetadataResponse struct { + FileName string `json:"file_name"` + FileSize int64 `json:"file_size"` + EncryptionSalt string `json:"encryption_salt"` + ChunkCount int `json:"chunk_count"` +} + From bc9d3974a9814cd086271ca48d8cf382b579d90f Mon Sep 17 00:00:00 2001 From: Sumanth Date: Thu, 12 Feb 2026 23:37:30 +0530 Subject: [PATCH 2/4] feat(handler): Add drop metadata and chunk download handlers --- internal/api/handlers.download.go | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 internal/api/handlers.download.go diff --git a/internal/api/handlers.download.go b/internal/api/handlers.download.go new file mode 100644 index 0000000..f02e057 --- /dev/null +++ b/internal/api/handlers.download.go @@ -0,0 +1,73 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/go-chi/chi/v5" +) + +// handleGetDropMetadata returns info about the file (name, size, salt) +// The CLI needs this *before* it starts downloading to set up decryption. +func (s *Server) handleGetDropMetadata() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + dropID := chi.URLParam(r, "id") + + var resp GetDropMetadataResponse + var expiresAt time.Time + + // 1. Fetch metadata from Postgres + query := ` + SELECT file_name, file_size, encryption_salt, expires_at + FROM drops WHERE id = $1` + + err := s.DB.QueryRow(query, dropID).Scan( + &resp.FileName, &resp.FileSize, &resp.EncryptionSalt, &expiresAt, + ) + + if err != nil { + http.Error(w, "Drop not found", http.StatusNotFound) + return + } + + // 2. Check Expiry + if time.Now().After(expiresAt) { + http.Error(w, "Drop has expired", http.StatusGone) + return + } + + // 3. Get chunk count (to know how many pieces to expect) + err = s.DB.QueryRow("SELECT COUNT(*) FROM chunks WHERE drop_id = $1", dropID).Scan(&resp.ChunkCount) + if err != nil { + http.Error(w, "Database error", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(resp) + } +} + +// handleDownloadChunk retrieves a specific piece of binary data +func (s *Server) handleDownloadChunk() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + dropID := chi.URLParam(r, "id") + chunkIndex := chi.URLParam(r, "chunkIndex") // We'll add this param to router + + // 1. Construct S3 Key + key := fmt.Sprintf("drops/%s/%s", dropID, chunkIndex) + + // 2. Fetch from S3 + data, err := s.Store.DownloadChunk(key) + if err != nil { + http.Error(w, "Chunk not found or storage error", http.StatusNotFound) + return + } + + // 3. Stream binary back + w.Header().Set("Content-Type", "application/octet-stream") + w.Write(data) + } +} \ No newline at end of file From 847e73f46974524b8d38981971be9f9b2144d029 Mon Sep 17 00:00:00 2001 From: Sumanth Date: Thu, 12 Feb 2026 23:38:37 +0530 Subject: [PATCH 3/4] feat(router): register new download routes --- internal/api/router.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/api/router.go b/internal/api/router.go index 0770d3e..90c0a60 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -52,5 +52,9 @@ func (s *Server) routes() { // Upload Endpoints r.Post("/drop", s.handleCreateDrop()) r.Post("/drop/{id}/chunk", s.handleUploadChunk()) + + // Download Endpoints + r.Get("/drop/{id}", s.handleGetDropMetadata()) + r.Get("/drop/{id}/chunk/{chunkIndex}", s.handleDownloadChunk()) }) } \ No newline at end of file From 5d0b57b2e2b49a42b7399d05bd39f39566400c60 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:30:48 +0000 Subject: [PATCH 4/4] Initial plan