Files
gohoarder/pkg/scanner/rescanner.go
T
2026-01-02 04:02:02 +00:00

140 lines
3.2 KiB
Go

package scanner
import (
"context"
"time"
"github.com/lukaszraczylo/gohoarder/pkg/metadata"
"github.com/rs/zerolog/log"
)
// RescanWorker handles periodic re-scanning of cached packages
type RescanWorker struct {
manager *Manager
metadataStore metadata.MetadataStore
interval time.Duration
stopCh chan struct{}
}
// NewRescanWorker creates a new rescan worker
func NewRescanWorker(manager *Manager, metadataStore metadata.MetadataStore, interval time.Duration) *RescanWorker {
return &RescanWorker{
manager: manager,
metadataStore: metadataStore,
interval: interval,
stopCh: make(chan struct{}),
}
}
// Start begins the periodic re-scanning process
func (w *RescanWorker) Start(ctx context.Context) {
if !w.manager.enabled || w.interval == 0 {
log.Info().Msg("Rescan worker disabled")
return
}
log.Info().
Dur("interval", w.interval).
Msg("Starting package rescan worker")
ticker := time.NewTicker(w.interval)
defer ticker.Stop()
// Run initial scan immediately
w.rescanPackages(ctx)
for {
select {
case <-ticker.C:
w.rescanPackages(ctx)
case <-w.stopCh:
log.Info().Msg("Rescan worker stopped")
return
case <-ctx.Done():
log.Info().Msg("Rescan worker stopped (context cancelled)")
return
}
}
}
// Stop stops the rescan worker
func (w *RescanWorker) Stop() {
close(w.stopCh)
}
// rescanPackages re-scans packages that need updating
func (w *RescanWorker) rescanPackages(ctx context.Context) {
log.Info().Msg("Starting package rescan cycle")
// Get all packages
packages, err := w.metadataStore.ListPackages(ctx, &metadata.ListOptions{})
if err != nil {
log.Error().Err(err).Msg("Failed to list packages for rescan")
return
}
scanned := 0
skipped := 0
failed := 0
for _, pkg := range packages {
// Check if package needs rescanning
needsRescan, err := w.needsRescan(ctx, pkg)
if err != nil {
log.Error().
Err(err).
Str("package", pkg.Name).
Str("version", pkg.Version).
Msg("Failed to check rescan status")
failed++
continue
}
if !needsRescan {
skipped++
continue
}
// Rescan the package
// Note: We need the file path - we'll need to reconstruct it or get it from storage
// For now, we'll just log and skip actual rescanning
log.Info().
Str("registry", pkg.Registry).
Str("package", pkg.Name).
Str("version", pkg.Version).
Msg("Package needs rescanning")
// TODO: Implement actual rescanning by:
// 1. Retrieving package file from storage
// 2. Scanning it
// This would require access to storage backend
scanned++
}
log.Info().
Int("total", len(packages)).
Int("scanned", scanned).
Int("skipped", skipped).
Int("failed", failed).
Msg("Rescan cycle completed")
}
// needsRescan checks if a package needs to be rescanned
func (w *RescanWorker) needsRescan(ctx context.Context, pkg *metadata.Package) (bool, error) {
// Get latest scan result
scanResult, err := w.metadataStore.GetScanResult(ctx, pkg.Registry, pkg.Name, pkg.Version)
if err != nil {
// No scan result - needs scanning
return true, nil
}
// Check if scan is older than rescan interval
timeSinceLastScan := time.Since(scanResult.ScannedAt)
if timeSinceLastScan >= w.interval {
return true, nil
}
return false, nil
}