monorepo/cloud/maplepress-backend/internal/scheduler/ip_cleanup.go

116 lines
3.2 KiB
Go

package scheduler
import (
"context"
"time"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/leaderelection"
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/ipcleanup"
)
// IPCleanupScheduler handles scheduled IP address cleanup for GDPR compliance
// CWE-359: IP addresses must be deleted after 90 days
type IPCleanupScheduler struct {
cron *cron.Cron
cleanupService *ipcleanup.CleanupService
leaderElection leaderelection.LeaderElection
logger *zap.Logger
enabled bool
schedulePattern string
}
// ProvideIPCleanupScheduler creates a new IPCleanupScheduler from config
func ProvideIPCleanupScheduler(
cfg *config.Config,
cleanupService *ipcleanup.CleanupService,
leaderElection leaderelection.LeaderElection,
logger *zap.Logger,
) *IPCleanupScheduler {
// IP cleanup enabled if configured (defaults to true for GDPR compliance)
enabled := cfg.Scheduler.IPCleanupEnabled
// Default: run daily at 2 AM
schedulePattern := cfg.Scheduler.IPCleanupSchedule
// Create cron with logger
cronLog := &cronLogger{logger: logger.Named("cron")}
c := cron.New(
cron.WithLogger(cronLog),
cron.WithChain(
cron.Recover(cronLog),
),
)
return &IPCleanupScheduler{
cron: c,
cleanupService: cleanupService,
leaderElection: leaderElection,
logger: logger.Named("ip-cleanup-scheduler"),
enabled: enabled,
schedulePattern: schedulePattern,
}
}
// Start starts the IP cleanup scheduler
func (s *IPCleanupScheduler) Start() error {
if !s.enabled {
s.logger.Info("IP cleanup scheduler is disabled")
return nil
}
s.logger.Info("starting IP cleanup scheduler for GDPR compliance",
zap.String("schedule", s.schedulePattern),
zap.String("retention_period", "90 days"))
// Schedule the IP cleanup job
_, err := s.cron.AddFunc(s.schedulePattern, s.cleanupIPs)
if err != nil {
s.logger.Error("failed to schedule IP cleanup job", zap.Error(err))
return err
}
// Start the cron scheduler
s.cron.Start()
s.logger.Info("IP cleanup scheduler started successfully")
return nil
}
// Stop stops the IP cleanup scheduler
func (s *IPCleanupScheduler) Stop() {
if !s.enabled {
return
}
s.logger.Info("stopping IP cleanup scheduler")
ctx := s.cron.Stop()
<-ctx.Done()
s.logger.Info("IP cleanup scheduler stopped")
}
// cleanupIPs is the cron job function that cleans up expired IP addresses
func (s *IPCleanupScheduler) cleanupIPs() {
// Only execute if this instance is the leader
if !s.leaderElection.IsLeader() {
s.logger.Debug("skipping IP cleanup - not the leader instance",
zap.String("instance_id", s.leaderElection.GetInstanceID()))
return
}
s.logger.Info("executing scheduled IP cleanup for GDPR compliance as leader instance",
zap.String("instance_id", s.leaderElection.GetInstanceID()))
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
err := s.cleanupService.CleanupExpiredIPs(ctx)
if err != nil {
s.logger.Error("IP cleanup failed", zap.Error(err))
return
}
s.logger.Info("IP cleanup completed successfully")
}