refactor: extract models/routes and env-based server config
- Move API request/response structs into new lib/models package - Centralize Gin route registration in lib/routing to simplify wiring - Add lib/server config helper to allow WARPBOX_BOX_POLL_INTERVAL_MS override - Improves modularity and makes polling behavior configurable per environmentrefactor: extract models/routes and env-based server config - Move API request/response structs into new lib/models package - Centralize Gin route registration in lib/routing to simplify wiring - Add lib/server config helper to allow WARPBOX_BOX_POLL_INTERVAL_MS override - Improves modularity and makes polling behavior configurable per environment
This commit is contained in:
243
lib/server/handlers.go
Normal file
243
lib/server/handlers.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"warpbox/lib/models"
|
||||
)
|
||||
|
||||
func handleIndex(ctx *gin.Context) {
|
||||
ctx.HTML(http.StatusOK, "index.html", gin.H{})
|
||||
}
|
||||
|
||||
func handleShowBox(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
if !validBoxID(boxID) {
|
||||
ctx.String(http.StatusBadRequest, "Invalid box id")
|
||||
return
|
||||
}
|
||||
|
||||
files, err := listBoxFiles(boxID)
|
||||
if err != nil {
|
||||
ctx.String(http.StatusNotFound, "Box not found")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, "box.html", gin.H{
|
||||
"BoxID": boxID,
|
||||
"Files": files,
|
||||
"FileCount": len(files),
|
||||
"DownloadAll": "/box/" + boxID + "/download",
|
||||
"PollMS": boxPollingIntervalMS(),
|
||||
})
|
||||
}
|
||||
|
||||
func handleBoxStatus(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
if !validBoxID(boxID) {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid box id"})
|
||||
return
|
||||
}
|
||||
|
||||
files, err := listBoxFiles(boxID)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusNotFound, gin.H{"error": "Box not found"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"box_id": boxID, "files": files})
|
||||
}
|
||||
|
||||
func handleDownloadBox(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
if !validBoxID(boxID) {
|
||||
ctx.String(http.StatusBadRequest, "Invalid box id")
|
||||
return
|
||||
}
|
||||
|
||||
files, err := listBoxFiles(boxID)
|
||||
if err != nil {
|
||||
ctx.String(http.StatusNotFound, "Box not found")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Header("Content-Type", "application/zip")
|
||||
ctx.Header("Content-Disposition", fmt.Sprintf(`attachment; filename="warpbox-%s.zip"`, boxID))
|
||||
|
||||
zipWriter := zip.NewWriter(ctx.Writer)
|
||||
defer zipWriter.Close()
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsComplete {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := addFileToZip(zipWriter, boxID, file.Name); err != nil {
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleDownloadFile(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
filename, ok := safeFilename(ctx.Param("filename"))
|
||||
if !validBoxID(boxID) || !ok {
|
||||
ctx.String(http.StatusBadRequest, "Invalid file")
|
||||
return
|
||||
}
|
||||
|
||||
path, ok := safeBoxFilePath(boxID, filename)
|
||||
if !ok {
|
||||
ctx.String(http.StatusBadRequest, "Invalid file")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
ctx.String(http.StatusNotFound, "File not found")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.FileAttachment(path, filename)
|
||||
}
|
||||
|
||||
func handleCreateBox(ctx *gin.Context) {
|
||||
boxID, err := newBoxID()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create upload box"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(boxPath(boxID), 0755); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not prepare upload box"})
|
||||
return
|
||||
}
|
||||
|
||||
var request models.CreateBoxRequest
|
||||
if err := ctx.ShouldBindJSON(&request); err != nil && err != io.EOF {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid box payload"})
|
||||
return
|
||||
}
|
||||
|
||||
files, err := createBoxManifest(boxID, request.Files)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"box_id": boxID, "box_url": "/box/" + boxID, "files": files})
|
||||
}
|
||||
|
||||
func handleManifestFileUpload(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
fileID := ctx.Param("file_id")
|
||||
if !validBoxID(boxID) {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid box id"})
|
||||
return
|
||||
}
|
||||
|
||||
file, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
markManifestFileStatus(boxID, fileID, fileStatusFailed)
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "No file received"})
|
||||
return
|
||||
}
|
||||
|
||||
savedFile, err := saveManifestUploadedFile(ctx, boxID, fileID, file)
|
||||
if err != nil {
|
||||
markManifestFileStatus(boxID, fileID, fileStatusFailed)
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"box_id": boxID, "box_url": "/box/" + boxID, "file": savedFile})
|
||||
}
|
||||
|
||||
func handleFileStatusUpdate(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
fileID := ctx.Param("file_id")
|
||||
if !validBoxID(boxID) {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid box id"})
|
||||
return
|
||||
}
|
||||
|
||||
var request models.UpdateFileStatusRequest
|
||||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid status payload"})
|
||||
return
|
||||
}
|
||||
|
||||
file, err := markManifestFileStatus(boxID, fileID, request.Status)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"file": file})
|
||||
}
|
||||
|
||||
func handleDirectBoxUpload(ctx *gin.Context) {
|
||||
boxID := ctx.Param("id")
|
||||
if !validBoxID(boxID) {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid box id"})
|
||||
return
|
||||
}
|
||||
|
||||
file, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "No file received"})
|
||||
return
|
||||
}
|
||||
|
||||
savedFile, err := saveUploadedFile(ctx, boxID, file)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"box_id": boxID, "box_url": "/box/" + boxID, "file": savedFile})
|
||||
}
|
||||
|
||||
func handleLegacyUpload(ctx *gin.Context) {
|
||||
form, err := ctx.MultipartForm()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "No files received"})
|
||||
return
|
||||
}
|
||||
|
||||
files := form.File["files"]
|
||||
if len(files) == 0 {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": "No files received"})
|
||||
return
|
||||
}
|
||||
|
||||
boxID, err := newBoxID()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create upload box"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(boxPath(boxID), 0755); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not prepare upload box"})
|
||||
return
|
||||
}
|
||||
|
||||
savedFiles := make([]gin.H, 0, len(files))
|
||||
for _, file := range files {
|
||||
savedFile, err := saveUploadedFile(ctx, boxID, file)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
savedFiles = append(savedFiles, savedFile)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"box_id": boxID, "box_url": "/box/" + boxID, "files": savedFiles})
|
||||
}
|
||||
Reference in New Issue
Block a user