feat: implementar webapp app_compartir con visor universal, auth y purga 24h para guest
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const slugChars = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
|
||||
var safeFilenameRegex = regexp.MustCompile(`[^a-zA-Z0-9._-]+`)
|
||||
|
||||
func GenerateSlug(length int) string {
|
||||
b := make([]byte, length)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "x" + strings.ToLower(slugChars[:length-1])
|
||||
}
|
||||
charsLen := byte(len(slugChars))
|
||||
for i := 0; i < length; i++ {
|
||||
b[i] = slugChars[b[i]%charsLen]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func SanitizeFilename(name string) string {
|
||||
base := filepath.Base(name)
|
||||
sanitized := safeFilenameRegex.ReplaceAllString(base, "_")
|
||||
if len(sanitized) > 100 {
|
||||
ext := filepath.Ext(sanitized)
|
||||
sanitized = sanitized[:100-len(ext)] + ext
|
||||
}
|
||||
if sanitized == "" || sanitized == "." {
|
||||
sanitized = "archivo.bin"
|
||||
}
|
||||
return sanitized
|
||||
}
|
||||
|
||||
func DetectMimeType(filename string, reader io.ReadSeeker) string {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
if m := mime.TypeByExtension(ext); m != "" {
|
||||
return m
|
||||
}
|
||||
|
||||
// Sniff first 512 bytes
|
||||
if reader != nil {
|
||||
buf := make([]byte, 512)
|
||||
n, err := reader.Read(buf)
|
||||
_, _ = reader.Seek(0, io.SeekStart)
|
||||
if err == nil && n > 0 {
|
||||
detected := http.DetectContentType(buf[:n])
|
||||
if detected != "application/octet-stream" {
|
||||
return detected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Known fallback extensions
|
||||
switch ext {
|
||||
case ".pdf":
|
||||
return "application/pdf"
|
||||
case ".mp4":
|
||||
return "video/mp4"
|
||||
case ".webm":
|
||||
return "video/webm"
|
||||
case ".mp3":
|
||||
return "audio/mpeg"
|
||||
case ".wav":
|
||||
return "audio/wav"
|
||||
case ".ogg":
|
||||
return "audio/ogg"
|
||||
case ".png":
|
||||
return "image/png"
|
||||
case ".jpg", ".jpeg":
|
||||
return "image/jpeg"
|
||||
case ".gif":
|
||||
return "image/gif"
|
||||
case ".webp":
|
||||
return "image/webp"
|
||||
case ".svg":
|
||||
return "image/svg+xml"
|
||||
case ".txt", ".log", ".env":
|
||||
return "text/plain; charset=utf-8"
|
||||
case ".json":
|
||||
return "application/json"
|
||||
case ".js":
|
||||
return "application/javascript"
|
||||
case ".go", ".py", ".rs", ".java", ".c", ".cpp", ".sh", ".sql", ".html", ".css", ".md":
|
||||
return "text/plain; charset=utf-8"
|
||||
default:
|
||||
return "application/octet-stream"
|
||||
}
|
||||
}
|
||||
|
||||
func SaveFile(uploadDir, slug, originalFilename string, src io.Reader) (string, int64, error) {
|
||||
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
safeName := SanitizeFilename(originalFilename)
|
||||
storedFilename := slug + "_" + safeName
|
||||
destPath := filepath.Join(uploadDir, storedFilename)
|
||||
|
||||
dest, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer dest.Close()
|
||||
|
||||
written, err := io.Copy(dest, src)
|
||||
if err != nil {
|
||||
_ = os.Remove(destPath)
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
return storedFilename, written, nil
|
||||
}
|
||||
Reference in New Issue
Block a user