- 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
126 lines
3.1 KiB
Go
126 lines
3.1 KiB
Go
package server
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"warpbox/lib/models"
|
|
)
|
|
|
|
func listCompletedFilesFromDisk(boxID string) ([]models.BoxFile, error) {
|
|
entries, err := os.ReadDir(boxPath(boxID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
files := make([]models.BoxFile, 0, len(entries))
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || entry.Name() == boxManifestFile {
|
|
continue
|
|
}
|
|
|
|
info, err := entry.Info()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
name := entry.Name()
|
|
mimeType := mimeTypeForFile(filepath.Join(boxPath(boxID), name), name)
|
|
files = append(files, decorateBoxFile(boxID, models.BoxFile{
|
|
ID: name,
|
|
Name: name,
|
|
Size: info.Size(),
|
|
MimeType: mimeType,
|
|
Status: fileStatusReady,
|
|
}))
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
func addFileToZip(zipWriter *zip.Writer, boxID string, filename string) error {
|
|
path := filepath.Join(boxPath(boxID), filename)
|
|
source, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer source.Close()
|
|
|
|
destination, err := zipWriter.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = io.Copy(destination, source)
|
|
return err
|
|
}
|
|
|
|
func saveManifestUploadedFile(ctx *gin.Context, boxID string, fileID string, file *multipart.FileHeader) (models.BoxFile, error) {
|
|
boxManifestMu.Lock()
|
|
defer boxManifestMu.Unlock()
|
|
|
|
manifest, err := readBoxManifestUnlocked(boxID)
|
|
if err != nil {
|
|
return models.BoxFile{}, err
|
|
}
|
|
|
|
fileIndex := -1
|
|
for index, manifestFile := range manifest.Files {
|
|
if manifestFile.ID == fileID {
|
|
fileIndex = index
|
|
break
|
|
}
|
|
}
|
|
|
|
if fileIndex < 0 {
|
|
return models.BoxFile{}, fmt.Errorf("File not found")
|
|
}
|
|
|
|
filename := manifest.Files[fileIndex].Name
|
|
if err := os.MkdirAll(boxPath(boxID), 0755); err != nil {
|
|
return models.BoxFile{}, fmt.Errorf("Could not prepare upload box")
|
|
}
|
|
|
|
destination := filepath.Join(boxPath(boxID), filename)
|
|
if err := ctx.SaveUploadedFile(file, destination); err != nil {
|
|
manifest.Files[fileIndex].Status = fileStatusFailed
|
|
writeBoxManifestUnlocked(boxID, manifest)
|
|
return models.BoxFile{}, fmt.Errorf("Could not save uploaded file")
|
|
}
|
|
|
|
manifest.Files[fileIndex].Size = file.Size
|
|
manifest.Files[fileIndex].MimeType = mimeTypeForFile(destination, filename)
|
|
manifest.Files[fileIndex].Status = fileStatusReady
|
|
if err := writeBoxManifestUnlocked(boxID, manifest); err != nil {
|
|
return models.BoxFile{}, err
|
|
}
|
|
|
|
return decorateBoxFile(boxID, manifest.Files[fileIndex]), nil
|
|
}
|
|
|
|
func saveUploadedFile(ctx *gin.Context, boxID string, file *multipart.FileHeader) (gin.H, error) {
|
|
filename, ok := safeFilename(file.Filename)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Invalid filename")
|
|
}
|
|
|
|
boxPath := boxPath(boxID)
|
|
if err := os.MkdirAll(boxPath, 0755); err != nil {
|
|
return nil, fmt.Errorf("Could not prepare upload box")
|
|
}
|
|
|
|
filename = uniqueFilename(boxPath, filename)
|
|
destination := filepath.Join(boxPath, filename)
|
|
if err := ctx.SaveUploadedFile(file, destination); err != nil {
|
|
return nil, fmt.Errorf("Could not save uploaded file")
|
|
}
|
|
|
|
return gin.H{"name": filename, "size": file.Size}, nil
|
|
}
|