Files
up_go/api/HandleUpload.go
wt 7f0e5b7b10
Build / Build (push) Successful in 10s
up some deprecated fixes
2026-03-19 14:54:33 +07:00

206 lines
5.1 KiB
Go

package api
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"sync"
"syscall"
"up/utils"
)
type UploadState struct {
mu sync.RWMutex
Type string
Received int64
Progress int
Status string
}
var uploadState UploadState
// POST /api/upload
func HandleUpload(w http.ResponseWriter, r *http.Request) {
uboot_max_size_int, err := strconv.Atoi(os.Getenv("UBOOT_MAX_SIZE"))
if err != nil {
fmt.Println("Error during conversion:", err)
return
}
max_file_size_int, err := strconv.Atoi(os.Getenv("MAX_FILE_SIZE"))
if err != nil {
fmt.Println("error during conversion: ", err)
return
}
UBOOT_MAX_SIZE := uboot_max_size_int * 1024 * 1024
MAX_FILE_SIZE := max_file_size_int * 1024 * 1024
MOUNT_POINT := os.Getenv("MOUNT_POINT")
r.Body = http.MaxBytesReader(w, r.Body, int64(MAX_FILE_SIZE)+1024)
if err := r.ParseMultipartForm(32 << 20); err != nil { // 32 MB в памяти
http.Error(w, `{"error":"Failed to parse multipart form"}`, http.StatusBadRequest)
return
}
defer r.MultipartForm.RemoveAll()
typeVals := r.MultipartForm.Value["type"]
if len(typeVals) == 0 {
http.Error(w, `{"error":"Missing type field"}`, http.StatusBadRequest)
return
}
typ := typeVals[0]
files := r.MultipartForm.File["file"]
if len(files) == 0 {
http.Error(w, `{"error":"Missing file field"}`, http.StatusBadRequest)
return
}
fileHeader := files[0]
maxSize := int64(MAX_FILE_SIZE)
if typ == "uboot_bin" {
maxSize = int64(UBOOT_MAX_SIZE)
}
if fileHeader.Size > maxSize {
http.Error(w, `{"error":"File too large"}`, http.StatusBadRequest)
return
}
srcFile, err := fileHeader.Open()
if err != nil {
http.Error(w, `{"error":"Failed to open uploaded file"}`, http.StatusInternalServerError)
return
}
defer srcFile.Close()
if utils.MountDevice() < 0 {
http.Error(w, `{"error":"Failed to mount device"}`, http.StatusInternalServerError)
return
}
filename := utils.GetTargetFilename(typ)
if filename == "" {
http.Error(w, `{"error":"Invalid upload type"}`, http.StatusBadRequest)
return
}
destPath := filepath.Join(MOUNT_POINT, filename)
uploadState.mu.Lock()
uploadState.Type = typ
uploadState.Received = 0
uploadState.Progress = 0
uploadState.Status = "Uploading..."
uploadState.mu.Unlock()
fmt.Printf("Upload: %s (%d bytes) -> %s\n", typ, fileHeader.Size, destPath)
timestamp := r.URL.Query().Get("timestamp")
if timestamp != "" {
decoded, err := url.QueryUnescape(timestamp)
if err == nil {
timestamp = decoded
}
}
if typ == "uboot_bin" {
data, err := io.ReadAll(srcFile)
if err != nil {
uploadState.mu.Lock()
uploadState.Status = "Failed"
uploadState.mu.Unlock()
http.Error(w, `{"error":"Failed to read U-Boot data"}`, http.StatusInternalServerError)
return
}
if err := utils.WriteUbootToDevices(data, destPath); err != nil {
uploadState.mu.Lock()
uploadState.Status = "Failed"
uploadState.mu.Unlock()
fmt.Println("error: ", err)
http.Error(w, `{"error":"Failed to write U-Boot"}`, http.StatusInternalServerError)
return
}
uploadState.mu.Lock()
uploadState.Received = int64(len(data))
uploadState.Progress = 100
uploadState.Status = "Complete"
uploadState.mu.Unlock()
} else {
destFile, err := os.Create(destPath)
if err != nil {
uploadState.mu.Lock()
uploadState.Status = "Failed"
uploadState.mu.Unlock()
http.Error(w, `{"error":"Failed to create file"}`, http.StatusInternalServerError)
return
}
defer destFile.Close()
buf := make([]byte, 65536) // 64 KB chunks
var written int64
for {
n, err := srcFile.Read(buf)
if n > 0 {
if _, err := destFile.Write(buf[:n]); err != nil {
uploadState.mu.Lock()
uploadState.Status = "Failed"
uploadState.mu.Unlock()
http.Error(w, `{"error":"Failed to write file"}`, http.StatusInternalServerError)
return
}
written += int64(n)
uploadState.mu.Lock()
uploadState.Received = written
uploadState.Progress = int(written * 100 / fileHeader.Size)
uploadState.mu.Unlock()
}
if err == io.EOF {
break
}
if err != nil {
uploadState.mu.Lock()
uploadState.Status = "Failed"
uploadState.mu.Unlock()
http.Error(w, `{"error":"Failed to read uploaded file"}`, http.StatusInternalServerError)
return
}
}
destFile.Sync()
syscall.Sync()
uploadState.mu.Lock()
uploadState.Progress = 100
uploadState.Status = "Complete"
uploadState.mu.Unlock()
}
if timestamp != "" {
versionPath := filepath.Join(MOUNT_POINT, filename+".version")
vf, err := os.Create(versionPath)
if err == nil {
var readableDate string
if len(timestamp) >= 19 {
readableDate = timestamp[:10] + " " + timestamp[11:19] // "YYYY-MM-DD HH:MM:SS"
} else {
readableDate = timestamp
}
vf.WriteString(readableDate + "\n")
vf.Close()
fmt.Printf("Written version file: %s (%s)\n", versionPath, readableDate)
}
}
fmt.Printf("Upload complete: %s\n", destPath)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "success",
"type": typ,
"filename": filename,
"size": fileHeader.Size,
"progress": 100,
})
}