// 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 }