refactor(server): use boxstore helpers and file status consts
- Move box ID validation, file listing/pathing, manifest creation, and uploads to `boxstore` - Use shared helpers for safe filenames and polling interval env parsing - Add file status constants to `models` to avoid duplicated magic strings across handlersrefactor(server): use boxstore helpers and file status consts - Move box ID validation, file listing/pathing, manifest creation, and uploads to `boxstore` - Use shared helpers for safe filenames and polling interval env parsing - Add file status constants to `models` to avoid duplicated magic strings across handlers
This commit is contained in:
20
lib/helpers/env.go
Normal file
20
lib/helpers/env.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func EnvInt(name string, fallback int, minimum int) int {
|
||||
rawValue := os.Getenv(name)
|
||||
if rawValue == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(rawValue)
|
||||
if err != nil || value < minimum {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
20
lib/helpers/format.go
Normal file
20
lib/helpers/format.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package helpers
|
||||
|
||||
import "fmt"
|
||||
|
||||
func FormatBytes(bytes int64) string {
|
||||
units := []string{"B", "KB", "MB", "GB"}
|
||||
size := float64(bytes)
|
||||
unitIndex := 0
|
||||
|
||||
for size >= 1024 && unitIndex < len(units)-1 {
|
||||
size /= 1024
|
||||
unitIndex++
|
||||
}
|
||||
|
||||
if unitIndex == 0 {
|
||||
return fmt.Sprintf("%d %s", bytes, units[unitIndex])
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.1f %s", size, units[unitIndex])
|
||||
}
|
||||
30
lib/helpers/ids.go
Normal file
30
lib/helpers/ids.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RandomHexID(byteCount int) (string, error) {
|
||||
bytes := make([]byte, byteCount)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
func ValidLowerHexID(value string, length int) bool {
|
||||
if len(value) != length {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, character := range value {
|
||||
if !strings.ContainsRune("0123456789abcdef", character) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
30
lib/helpers/mime.go
Normal file
30
lib/helpers/mime.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func MimeTypeForFile(path string, filename string) string {
|
||||
if mimeType := mime.TypeByExtension(strings.ToLower(filepath.Ext(filename))); mimeType != "" {
|
||||
return mimeType
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "application/octet-stream"
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := make([]byte, 512)
|
||||
bytesRead, err := file.Read(buffer)
|
||||
if err != nil && err != io.EOF {
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
return http.DetectContentType(buffer[:bytesRead])
|
||||
}
|
||||
47
lib/helpers/paths.go
Normal file
47
lib/helpers/paths.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SafeFilename(name string) (string, bool) {
|
||||
filename := filepath.Base(name)
|
||||
filename = strings.TrimSpace(filename)
|
||||
return filename, filename != "" && filename != "." && filename != string(filepath.Separator)
|
||||
}
|
||||
|
||||
func SafeChildPath(parent string, filename string) (string, bool) {
|
||||
path := filepath.Join(parent, filename)
|
||||
return path, strings.HasPrefix(path, parent+string(filepath.Separator))
|
||||
}
|
||||
|
||||
func UniqueFilename(directory string, filename string) string {
|
||||
if _, err := os.Stat(filepath.Join(directory, filename)); os.IsNotExist(err) {
|
||||
return filename
|
||||
}
|
||||
|
||||
extension := filepath.Ext(filename)
|
||||
base := strings.TrimSuffix(filename, extension)
|
||||
for count := 2; ; count++ {
|
||||
candidate := base + "-" + strconv.Itoa(count) + extension
|
||||
if _, err := os.Stat(filepath.Join(directory, candidate)); os.IsNotExist(err) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UniqueNameInBatch(filename string, usedNames map[string]int) string {
|
||||
count := usedNames[filename]
|
||||
usedNames[filename] = count + 1
|
||||
|
||||
if count == 0 {
|
||||
return filename
|
||||
}
|
||||
|
||||
extension := filepath.Ext(filename)
|
||||
base := strings.TrimSuffix(filename, extension)
|
||||
return base + "-" + strconv.Itoa(count+1) + extension
|
||||
}
|
||||
Reference in New Issue
Block a user