57 lines
1.4 KiB
Go
Raw Normal View History

2025-09-16 00:05:16 -05:00
// backend/internal/naming.go
2025-09-08 00:48:47 -05:00
package internal
import (
"fmt"
"path/filepath"
"regexp"
"strings"
"time"
)
var (
descMax = 64
allowedDesc = regexp.MustCompile(`[^A-Za-z0-9 _-]`) // to be replaced by `_`
dateOnly = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`) // UI sends YYYY-MM-DD
2025-09-08 00:48:47 -05:00
finalNameValidate = regexp.MustCompile(`^\d{4}\.\d{2}\.\d{2}\.[A-Za-z0-9_-]{1,64}\.[A-Za-z0-9]{1,8}$`)
)
2026-04-11 00:02:59 -03:00
// SanitizeDesc normalizes a free-form description into a safe filename segment.
2025-09-08 00:48:47 -05:00
func SanitizeDesc(in string) string {
s := strings.TrimSpace(in)
if s == "" {
s = "upload"
}
2025-09-08 00:48:47 -05:00
s = allowedDesc.ReplaceAllString(s, "_")
s = strings.Join(strings.Fields(s), "_") // collapse whitespace
if len(s) > descMax {
s = s[:descMax]
}
2025-09-08 00:48:47 -05:00
return s
}
2026-04-11 00:02:59 -03:00
// ComposeFinalName builds the on-disk upload filename from the user date, description, and original name.
2025-09-08 00:48:47 -05:00
func ComposeFinalName(dateStr, desc, origFilename string) (string, error) {
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(origFilename), "."))
if ext == "" {
ext = "bin"
}
2025-09-08 00:48:47 -05:00
var t time.Time
var err error
if dateOnly.MatchString(dateStr) {
t, err = time.Parse("2006-01-02", dateStr)
} else {
t = time.Now()
}
if err != nil {
return "", fmt.Errorf("bad date")
}
2025-09-08 00:48:47 -05:00
final := fmt.Sprintf("%04d.%02d.%02d.%s.%s", t.Year(), int(t.Month()), t.Day(), SanitizeDesc(desc), ext)
if !finalNameValidate.MatchString(final) {
return "", fmt.Errorf("invalid final name")
}
2025-09-08 00:48:47 -05:00
return final, nil
}