2.2-stable fixes

This commit is contained in:
Alexander Renz 2024-12-25 18:24:05 +01:00
parent 9f8b57d7cc
commit be0f5b2bd0
3 changed files with 127 additions and 56 deletions

View File

@ -10,6 +10,11 @@ import (
"encoding/hex"
"flag"
"fmt"
// "image" // Unused import removed
_ "image/gif" // Ensure GIF support
_ "image/jpeg" // Ensure JPEG support
_ "image/png" // Ensure PNG support
"io"
"mime"
"net"
@ -32,6 +37,7 @@ import (
"github.com/patrickmn/go-cache"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/robfig/cron/v3"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"
@ -123,9 +129,10 @@ type DeduplicationConfig struct {
}
type ThumbnailsConfig struct {
Enabled bool `mapstructure:"enabled"`
Directory string `mapstructure:"directory"`
Size string `mapstructure:"size"`
Enabled bool `mapstructure:"enabled"`
Directory string `mapstructure:"directory"`
Size string `mapstructure:"size"`
ThumbnailIntervalScan string `mapstructure:"thumbnailintervalscan"`
}
type ISOConfig struct {
@ -225,17 +232,17 @@ type FileMetadata struct {
}
var (
conf Config
versionString string = "v2.2-stable"
log = logrus.New()
uploadQueue chan UploadTask
networkEvents chan NetworkEvent
fileInfoCache *cache.Cache
conf Config
versionString string = "v2.2"
log = logrus.New()
uploadQueue chan UploadTask
networkEvents chan NetworkEvent
fileInfoCache *cache.Cache
fileMetadataCache *cache.Cache
clamClient *clamd.Clamd
redisClient *redis.Client
redisConnected bool
mu sync.RWMutex
clamClient *clamd.Clamd
redisClient *redis.Client
redisConnected bool
mu sync.RWMutex
uploadDuration prometheus.Histogram
uploadErrorsTotal prometheus.Counter
@ -251,8 +258,8 @@ var (
uploadSizeBytes prometheus.Histogram
downloadSizeBytes prometheus.Histogram
scanQueue chan ScanTask
ScanWorkers = 5
scanQueue chan ScanTask
ScanWorkers = 5
)
const (
@ -323,6 +330,26 @@ func main() {
}
log.Info("Configuration loaded successfully.")
// Log configuration settings
log.Infof("Server ListenPort: %s", conf.Server.ListenPort)
log.Infof("Server UnixSocket: %v", conf.Server.UnixSocket)
log.Infof("Server StoragePath: %s", conf.Server.StoragePath)
log.Infof("Server LogLevel: %s", conf.Server.LogLevel)
log.Infof("Server LogFile: %s", conf.Server.LogFile)
log.Infof("Server MetricsEnabled: %v", conf.Server.MetricsEnabled)
log.Infof("Server MetricsPort: %s", conf.Server.MetricsPort)
log.Infof("Server FileTTL: %s", conf.Server.FileTTL)
log.Infof("Server MinFreeBytes: %s", conf.Server.MinFreeBytes)
log.Infof("Server AutoAdjustWorkers: %v", conf.Server.AutoAdjustWorkers)
log.Infof("Server NetworkEvents: %v", conf.Server.NetworkEvents)
log.Infof("Server TempPath: %s", conf.Server.TempPath)
log.Infof("Server LoggingJSON: %v", conf.Server.LoggingJSON)
log.Infof("Server PIDFilePath: %s", conf.Server.PIDFilePath)
log.Infof("Server CleanUponExit: %v", conf.Server.CleanUponExit)
log.Infof("Server PreCaching: %v", conf.Server.PreCaching)
log.Infof("Server FileTTLEnabled: %v", conf.Server.FileTTLEnabled)
log.Infof("Server DeduplicationEnabled: %v", conf.Server.DeduplicationEnabled)
err = writePIDFile(conf.Server.PIDFilePath) // Write PID file after config is loaded
if err != nil {
log.Fatalf("Error writing PID file: %v", err)
@ -357,6 +384,13 @@ func main() {
}
log.WithField("directory", conf.Server.StoragePath).Info("Store directory is ready")
// Ensure thumbnail directory exists if thumbnails are enabled
if conf.Thumbnails.Enabled {
if err := os.MkdirAll(conf.Thumbnails.Directory, os.ModePerm); err != nil {
log.Fatalf("Failed to create thumbnail directory: %v", err)
}
}
err = checkFreeSpaceWithRetry(conf.Server.StoragePath, 3, 5*time.Second)
if err != nil {
log.Fatalf("Insufficient free space: %v", err)
@ -451,6 +485,9 @@ func main() {
go monitorWorkerPerformance(ctx, &conf.Server, &conf.Workers, &conf.ClamAV)
}
// Schedule periodic thumbnail generation
scheduleThumbnailGeneration()
log.Infof("Starting HMAC file server %s...", versionString)
if conf.Server.UnixSocket {
if err := os.RemoveAll(conf.Server.ListenPort); err != nil {
@ -612,6 +649,8 @@ func setDefaults() {
viper.SetDefault("iso.size", "1GB")
viper.SetDefault("iso.mountpoint", "/mnt/iso")
viper.SetDefault("iso.charset", "utf-8")
viper.SetDefault("thumbnails.thumbnailintervalscan", "24h")
}
func validateConfig(conf *Config) error {
@ -757,7 +796,7 @@ func checkStoragePath(path string) error {
func setupLogging() {
level, err := logrus.ParseLevel(conf.Server.LogLevel)
if (err != nil) {
if err != nil {
log.Fatalf("Invalid log level: %s", conf.Server.LogLevel)
}
log.SetLevel(level)
@ -1040,7 +1079,7 @@ func processUpload(task UploadTask) error {
// Gajim and Dino do not require a callback or acknowledgement beyond HTTP success.
callbackURL := r.Header.Get("Callback-URL")
if (callbackURL != "") {
if callbackURL != "" {
log.Warnf("Callback-URL provided (%s) but not needed. Ignoring.", callbackURL)
// We do not block or wait, just ignore.
}
@ -1068,16 +1107,6 @@ func processUpload(task UploadTask) error {
log.Infof("ISO container handled successfully for file: %s", absFilename)
}
if conf.Thumbnails.Enabled {
err = generateThumbnail(absFilename, conf.Thumbnails.Directory, conf.Thumbnails.Size)
if err != nil {
log.Errorf("Failed to generate thumbnail for %s: %v", absFilename, err)
uploadErrorsTotal.Inc()
return err
}
log.Infof("Thumbnail generated for %s", absFilename)
}
if redisClient != nil {
errSet := redisClient.Set(context.Background(), hashVal, absFilename, 0).Err()
if errSet != nil {
@ -1446,15 +1475,6 @@ func handleUpload(w http.ResponseWriter, r *http.Request, absFilename, fileStore
}
}
// Generate thumbnail if enabled
if conf.Thumbnails.Enabled {
thumbnailPath := filepath.Join(conf.Thumbnails.Directory, filepath.Base(absFilename))
err := generateThumbnail(absFilename, thumbnailPath, conf.Thumbnails.Size)
if err != nil {
log.Errorf("Failed to generate thumbnail for %s: %v", absFilename, err)
}
}
logMessages = append(logMessages, fmt.Sprintf("Processing completed successfully for %s", absFilename))
uploadsTotal.Inc()
@ -1791,7 +1811,7 @@ func setupGracefulShutdown(server *http.Server, cancel context.CancelFunc) {
}
func initRedis() {
if (!conf.Redis.RedisEnabled) {
if !conf.Redis.RedisEnabled {
log.Info("Redis is disabled in configuration.")
return
}
@ -2323,15 +2343,13 @@ func precacheStoragePath(dir string) error {
}
func generateThumbnail(originalPath, thumbnailDir, size string) error {
// Implement thumbnail generation logic here
// For example, using an image processing library like "github.com/disintegration/imaging"
img, err := imaging.Open(originalPath)
if err != nil {
return err
// Check if thumbnail generation is enabled
if !conf.Thumbnails.Enabled {
log.Println("Thumbnail generation is disabled.")
return nil
}
// Parse size (e.g., "200x200")
// Parse the size (e.g., "200x200")
dimensions := strings.Split(size, "x")
if len(dimensions) != 2 {
return fmt.Errorf("invalid thumbnail size format: %s", size)
@ -2347,29 +2365,40 @@ func generateThumbnail(originalPath, thumbnailDir, size string) error {
return fmt.Errorf("invalid thumbnail height: %s", dimensions[1])
}
thumb := imaging.Thumbnail(img, width, height, imaging.Lanczos) // Example size
baseName := filepath.Base(originalPath)
thumbName := strings.TrimSuffix(baseName, filepath.Ext(baseName)) + "_thumb" + filepath.Ext(baseName)
thumbPath := filepath.Join(thumbnailDir, thumbName)
err = os.MkdirAll(thumbnailDir, os.ModePerm)
if err != nil {
return err
// Ensure the thumbnail directory exists
if err := os.MkdirAll(thumbnailDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create thumbnail directory: %v", err)
}
err = imaging.Save(thumb, thumbPath)
// Open the original image
img, err := imaging.Open(originalPath)
if err != nil {
return err
log.Printf("Error opening image %s: %v", originalPath, err)
return fmt.Errorf("thumbnail creation skipped for %s: %v", originalPath, err)
}
// Resize the image using Lanczos filter
thumbnail := imaging.Resize(img, width, height, imaging.Lanczos)
// Define the thumbnail file path
filename := filepath.Base(originalPath)
thumbnailPath := filepath.Join(thumbnailDir, filename)
// Save the thumbnail
err = imaging.Save(thumbnail, thumbnailPath)
if err != nil {
log.Printf("Error saving thumbnail for %s: %v", originalPath, err)
return fmt.Errorf("thumbnail creation skipped for %s: %v", originalPath, err)
}
log.Printf("Thumbnail created at %s", thumbnailPath)
return nil
}
func handleFileCleanup(conf *Config) {
if conf.Server.FileTTLEnabled {
ttlDuration, err := parseTTL(conf.Server.FileTTL)
if (err != nil) {
if err != nil {
log.Fatalf("Invalid TTL configuration: %v", err)
}
log.Printf("File TTL is enabled. Files older than %v will be deleted.", ttlDuration)
@ -2421,3 +2450,42 @@ func deleteOldFiles(conf *Config, ttl time.Duration) {
log.Printf("Error during file cleanup: %v", err)
}
}
func scheduleThumbnailGeneration() {
if !conf.Thumbnails.Enabled {
log.Println("Thumbnail generation is disabled.")
return
}
c := cron.New()
_, err := c.AddFunc("@every "+conf.Thumbnails.ThumbnailIntervalScan, func() {
log.Println("Starting scheduled thumbnail generation.")
err := filepath.Walk(conf.Server.StoragePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Printf("Error accessing path %s: %v", path, err)
return nil
}
if !info.IsDir() && isExtensionAllowed(path) {
thumbPath := filepath.Join(conf.Thumbnails.Directory, filepath.Base(path))
if _, err := os.Stat(thumbPath); os.IsNotExist(err) {
err := generateThumbnail(path, conf.Thumbnails.Directory, conf.Thumbnails.Size)
if err != nil {
log.Printf("Failed to generate thumbnail for %s: %v", path, err)
} else {
log.Printf("Thumbnail generated for %s", path)
}
}
}
return nil
})
if err != nil {
log.Printf("Error during thumbnail generation: %v", err)
}
log.Println("Thumbnail generation complete.")
})
if err != nil {
log.Fatalf("Failed to schedule thumbnail generation: %v", err)
}
c.Start()
log.Println("Thumbnail generation scheduler started.")
}

1
go.mod
View File

@ -53,6 +53,7 @@ require (
github.com/prometheus/common v0.61.0
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/tview v0.0.0-20241103174730-c76f7879f592
github.com/robfig/cron/v3 v3.0.1
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect

2
go.sum
View File

@ -79,6 +79,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=