237 lines
6.5 KiB
Go
237 lines
6.5 KiB
Go
|
|
package server
|
||
|
|
|
||
|
|
import (
|
||
|
|
"io"
|
||
|
|
"net/http"
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
|
||
|
|
"warpbox/lib/boxstore"
|
||
|
|
"warpbox/lib/helpers"
|
||
|
|
"warpbox/lib/models"
|
||
|
|
)
|
||
|
|
|
||
|
|
func (app *App) handleCreateBox(ctx *gin.Context) {
|
||
|
|
if !app.requireAPI(ctx) || !app.requireGuestUploads(ctx) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
app.limitRequestBody(ctx)
|
||
|
|
|
||
|
|
boxID, err := boxstore.NewBoxID()
|
||
|
|
if err != nil {
|
||
|
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create upload box"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := os.MkdirAll(boxstore.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
|
||
|
|
}
|
||
|
|
if err := app.validateCreateBoxRequest(&request); err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
files, err := boxstore.CreateManifest(boxID, request)
|
||
|
|
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 (app *App) handleManifestFileUpload(ctx *gin.Context) {
|
||
|
|
if !app.requireAPI(ctx) || !app.requireGuestUploads(ctx) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
app.limitRequestBody(ctx)
|
||
|
|
|
||
|
|
boxID := ctx.Param("id")
|
||
|
|
fileID := ctx.Param("file_id")
|
||
|
|
if !boxstore.ValidBoxID(boxID) {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid box id"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
file, err := ctx.FormFile("file")
|
||
|
|
if err != nil {
|
||
|
|
boxstore.MarkFileStatus(boxID, fileID, models.FileStatusFailed)
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "No file received"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := app.validateManifestFileUpload(boxID, fileID, file.Size); err != nil {
|
||
|
|
boxstore.MarkFileStatus(boxID, fileID, models.FileStatusFailed)
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
savedFile, err := boxstore.SaveManifestUpload(boxID, fileID, file)
|
||
|
|
if err != nil {
|
||
|
|
boxstore.MarkFileStatus(boxID, fileID, models.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 (app *App) handleFileStatusUpdate(ctx *gin.Context) {
|
||
|
|
if !app.requireAPI(ctx) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
app.limitRequestBody(ctx)
|
||
|
|
|
||
|
|
boxID := ctx.Param("id")
|
||
|
|
fileID := ctx.Param("file_id")
|
||
|
|
if !boxstore.ValidBoxID(boxID) || !helpers.ValidLowerHexID(fileID, 16) {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var request models.UpdateFileStatusRequest
|
||
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid status payload"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if request.Status == models.FileStatusReady {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Uploads must complete through the upload endpoint"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := app.rejectExpiredManifestBox(boxID); err != nil {
|
||
|
|
ctx.JSON(http.StatusGone, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
file, err := boxstore.MarkFileStatus(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 (app *App) handleDirectBoxUpload(ctx *gin.Context) {
|
||
|
|
if !app.requireAPI(ctx) || !app.requireGuestUploads(ctx) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
app.limitRequestBody(ctx)
|
||
|
|
|
||
|
|
boxID := ctx.Param("id")
|
||
|
|
if !boxstore.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
|
||
|
|
}
|
||
|
|
if err := app.validateIncomingFile(boxID, file.Size); err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
savedFile, err := boxstore.SaveUpload(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 (app *App) handleLegacyUpload(ctx *gin.Context) {
|
||
|
|
if !app.requireAPI(ctx) || !app.requireGuestUploads(ctx) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
app.limitRequestBody(ctx)
|
||
|
|
|
||
|
|
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
|
||
|
|
}
|
||
|
|
totalSize := int64(0)
|
||
|
|
for _, file := range files {
|
||
|
|
if err := app.validateFileSize(file.Size); err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
totalSize += file.Size
|
||
|
|
}
|
||
|
|
if err := app.validateBoxSize(totalSize); err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
boxID, err := boxstore.NewBoxID()
|
||
|
|
if err != nil {
|
||
|
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create upload box"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := os.MkdirAll(boxstore.BoxPath(boxID), 0755); err != nil {
|
||
|
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Could not prepare upload box"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
retentionKey := strings.TrimSpace(ctx.PostForm("retention_key"))
|
||
|
|
if retentionKey == "" {
|
||
|
|
retentionKey = strings.TrimSpace(ctx.PostForm("retention"))
|
||
|
|
}
|
||
|
|
allowZip := true
|
||
|
|
if strings.EqualFold(strings.TrimSpace(ctx.PostForm("allow_zip")), "false") {
|
||
|
|
allowZip = false
|
||
|
|
}
|
||
|
|
request := models.CreateBoxRequest{
|
||
|
|
RetentionKey: retentionKey,
|
||
|
|
Password: ctx.PostForm("password"),
|
||
|
|
AllowZip: &allowZip,
|
||
|
|
Files: make([]models.CreateBoxFileRequest, 0, len(files)),
|
||
|
|
}
|
||
|
|
for _, file := range files {
|
||
|
|
request.Files = append(request.Files, models.CreateBoxFileRequest{Name: file.Filename, Size: file.Size})
|
||
|
|
}
|
||
|
|
if err := app.validateCreateBoxRequest(&request); err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
manifestFiles, err := boxstore.CreateManifest(boxID, request)
|
||
|
|
if err != nil {
|
||
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
savedFiles := make([]models.BoxFile, 0, len(files))
|
||
|
|
for index, file := range files {
|
||
|
|
savedFile, err := boxstore.SaveManifestUpload(boxID, manifestFiles[index].ID, file)
|
||
|
|
if err != nil {
|
||
|
|
_, _ = boxstore.MarkFileStatus(boxID, manifestFiles[index].ID, models.FileStatusFailed)
|
||
|
|
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})
|
||
|
|
}
|