137 lines
3.0 KiB
Go
137 lines
3.0 KiB
Go
// 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
|
|
}
|