pegasus/backend/uploads.go

137 lines
3.0 KiB
Go
Raw Permalink Normal View History

2026-04-11 00:02:59 -03:00
// backend/uploads.go
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/tus/tusd/pkg/handler"
"scm.bstein.dev/bstein/Pegasus/backend/internal"
)
var finalNameRe = regexp.MustCompile(`^\d{4}\.\d{2}\.\d{2}\.[A-Za-z0-9_-]{1,64}(?:\.[A-Za-z0-9_-]{1,64})?\.[A-Za-z0-9]{1,8}$`)
var renameFile = os.Rename
func sanitizeDescriptor(s string) string {
s = strings.TrimSpace(s)
s = strings.ReplaceAll(s, " ", "_")
if s == "" {
return "upload"
}
var b []rune
lastUnderscore := false
for _, r := range s {
ok := (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-'
if !ok {
r = '_'
}
if r == '_' {
if lastUnderscore {
continue
}
lastUnderscore = true
} else {
lastUnderscore = false
}
b = append(b, r)
if len(b) >= 64 {
break
}
}
return string(b)
}
func stemOf(orig string) string {
base := strings.TrimSuffix(orig, filepath.Ext(orig))
s := sanitizeDescriptor(strings.ReplaceAll(base, ".", "_"))
return s
}
func composeFinalName(date, desc, orig string) string {
var y, m, d string
if len(date) >= 10 && date[4] == '-' && date[7] == '-' {
y, m, d = date[:4], date[5:7], date[8:10]
} else {
now := time.Now()
y = fmt.Sprintf("%04d", now.Year())
m = fmt.Sprintf("%02d", int(now.Month()))
d = fmt.Sprintf("%02d", now.Day())
}
clean := sanitizeDescriptor(desc)
stem := stemOf(orig)
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(orig), "."))
if ext == "" {
ext = "bin"
}
return fmt.Sprintf("%s.%s.%s.%s.%s.%s", y, m, d, clean, stem, ext)
}
func moveFromTus(ev handler.HookEvent, dst string) error {
candidates := []string{
filepath.Join(tusDir, ev.Upload.ID),
filepath.Join(tusDir, ev.Upload.ID+".bin"),
filepath.Join(tusDir, "data", ev.Upload.ID),
filepath.Join(tusDir, "data", ev.Upload.ID+".bin"),
filepath.Join(tusDir, "uploads", ev.Upload.ID),
filepath.Join(tusDir, "uploads", ev.Upload.ID+".bin"),
}
var src string
for _, p := range candidates {
if fi, err := os.Stat(p); err == nil && fi.Mode().IsRegular() {
src = p
break
}
}
if src == "" {
entries, _ := os.ReadDir(tusDir)
for _, e := range entries {
if !e.IsDir() && strings.HasPrefix(e.Name(), ev.Upload.ID) {
src = filepath.Join(tusDir, e.Name())
break
}
}
}
if src == "" {
return fmt.Errorf("tus data file not found for id %s", ev.Upload.ID)
}
if err := os.MkdirAll(filepath.Dir(dst), 0o2775); err != nil {
return err
}
if internal.DryRun {
internal.Logf("[DRY] mv %s -> %s", src, dst)
return nil
}
if err := renameFile(src, dst); err != nil {
in, err2 := os.Open(src)
if err2 != nil {
return err2
}
defer in.Close()
out, err3 := os.Create(dst)
if err3 != nil {
return err3
}
if _, err := io.Copy(out, in); err != nil {
_ = out.Close()
return err
}
_ = out.Close()
_ = os.Remove(src)
}
_ = os.Remove(filepath.Join(tusDir, ev.Upload.ID+".info"))
_ = os.Remove(filepath.Join(tusDir, ev.Upload.ID+".json"))
return nil
}