package handlers import ( "errors" "fmt" "net/http" "os" "time" "warpbox.dev/backend/libs/helpers" "warpbox.dev/backend/libs/web" ) type downloadPageData struct { Box boxView Files []fileView ZipURL string DownloadCount int MaxDownloads int ExpiresLabel string } type boxView struct { ID string } type fileView struct { ID string Name string Size string ContentType string URL string } func (a *App) DownloadPage(w http.ResponseWriter, r *http.Request) { box, err := a.uploadService.GetBox(r.PathValue("boxID")) if err != nil { http.NotFound(w, r) return } if err := a.uploadService.CanDownload(box); err != nil { a.renderer.Render(w, http.StatusForbidden, "download.gohtml", web.PageData{ Title: "Download unavailable", Description: "This Warpbox link is no longer available.", Data: downloadPageData{ Box: boxView{ID: box.ID}, ExpiresLabel: err.Error(), }, }) return } files := make([]fileView, 0, len(box.Files)) for _, file := range box.Files { files = append(files, fileView{ ID: file.ID, Name: file.Name, Size: helpers.FormatBytes(file.Size), ContentType: file.ContentType, URL: fmt.Sprintf("/d/%s/f/%s", box.ID, file.ID), }) } a.renderer.Render(w, http.StatusOK, "download.gohtml", web.PageData{ Title: "Download files", Description: "Download files shared through Warpbox.", Data: downloadPageData{ Box: boxView{ID: box.ID}, Files: files, ZipURL: fmt.Sprintf("/d/%s/zip", box.ID), DownloadCount: box.DownloadCount, MaxDownloads: box.MaxDownloads, ExpiresLabel: box.ExpiresAt.Format("Jan 2, 2006 15:04 MST"), }, }) } func (a *App) DownloadFile(w http.ResponseWriter, r *http.Request) { box, err := a.uploadService.GetBox(r.PathValue("boxID")) if err != nil { http.NotFound(w, r) return } if err := a.uploadService.CanDownload(box); err != nil { http.Error(w, err.Error(), statusForDownloadError(err)) return } file, err := a.uploadService.FindFile(box, r.PathValue("fileID")) if err != nil { http.NotFound(w, r) return } path := a.uploadService.FilePath(box, file) source, err := os.Open(path) if err != nil { http.NotFound(w, r) return } defer source.Close() stat, err := source.Stat() if err != nil { http.NotFound(w, r) return } w.Header().Set("Content-Type", file.ContentType) w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", file.Name)) http.ServeContent(w, r, file.Name, stat.ModTime(), source) if err := a.uploadService.RecordDownload(box.ID); err != nil && !errors.Is(err, os.ErrNotExist) { a.logger.Warn("failed to record file download", "source", "download", "code", 4002, "box_id", box.ID, "error", err.Error()) } } func (a *App) DownloadZip(w http.ResponseWriter, r *http.Request) { box, err := a.uploadService.GetBox(r.PathValue("boxID")) if err != nil { http.NotFound(w, r) return } if err := a.uploadService.CanDownload(box); err != nil { http.Error(w, err.Error(), statusForDownloadError(err)) return } w.Header().Set("Content-Type", "application/zip") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", "warpbox-"+box.ID+".zip")) w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) if err := a.uploadService.WriteZip(w, box); err != nil { a.logger.Error("zip download failed", "source", "download", "code", 5002, "box_id", box.ID, "error", err.Error()) return } if err := a.uploadService.RecordDownload(box.ID); err != nil && !errors.Is(err, os.ErrNotExist) { a.logger.Warn("failed to record zip download", "source", "download", "code", 4003, "box_id", box.ID, "error", err.Error()) } }