Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
116
cloud/maplepress-backend/internal/scheduler/ip_cleanup.go
Normal file
116
cloud/maplepress-backend/internal/scheduler/ip_cleanup.go
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
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")
|
||||
}
|
||||
129
cloud/maplepress-backend/internal/scheduler/quota_reset.go
Normal file
129
cloud/maplepress-backend/internal/scheduler/quota_reset.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
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"
|
||||
siteusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/site"
|
||||
)
|
||||
|
||||
// QuotaResetScheduler handles scheduled usage resets for billing cycles
|
||||
type QuotaResetScheduler struct {
|
||||
cron *cron.Cron
|
||||
resetUsageUC *siteusecase.ResetMonthlyUsageUseCase
|
||||
leaderElection leaderelection.LeaderElection
|
||||
logger *zap.Logger
|
||||
enabled bool
|
||||
schedulePattern string
|
||||
}
|
||||
|
||||
// cronLogger is a simple adapter for cron to use zap logger
|
||||
type cronLogger struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (l *cronLogger) Info(msg string, keysAndValues ...interface{}) {
|
||||
l.logger.Sugar().Infow(msg, keysAndValues...)
|
||||
}
|
||||
|
||||
func (l *cronLogger) Error(err error, msg string, keysAndValues ...interface{}) {
|
||||
l.logger.Sugar().Errorw(msg, append(keysAndValues, "error", err)...)
|
||||
}
|
||||
|
||||
// ProvideQuotaResetScheduler creates a new QuotaResetScheduler from config
|
||||
func ProvideQuotaResetScheduler(
|
||||
cfg *config.Config,
|
||||
resetUsageUC *siteusecase.ResetMonthlyUsageUseCase,
|
||||
leaderElection leaderelection.LeaderElection,
|
||||
logger *zap.Logger,
|
||||
) *QuotaResetScheduler {
|
||||
enabled := cfg.Scheduler.QuotaResetEnabled
|
||||
schedulePattern := cfg.Scheduler.QuotaResetSchedule
|
||||
|
||||
// Create cron with logger
|
||||
cronLog := &cronLogger{logger: logger.Named("cron")}
|
||||
c := cron.New(
|
||||
cron.WithLogger(cronLog),
|
||||
cron.WithChain(
|
||||
cron.Recover(cronLog),
|
||||
),
|
||||
)
|
||||
|
||||
return &QuotaResetScheduler{
|
||||
cron: c,
|
||||
resetUsageUC: resetUsageUC,
|
||||
leaderElection: leaderElection,
|
||||
logger: logger.Named("usage-reset-scheduler"),
|
||||
enabled: enabled,
|
||||
schedulePattern: schedulePattern,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the quota reset scheduler
|
||||
func (s *QuotaResetScheduler) Start() error {
|
||||
if !s.enabled {
|
||||
s.logger.Info("quota reset scheduler is disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
s.logger.Info("starting quota reset scheduler",
|
||||
zap.String("schedule", s.schedulePattern))
|
||||
|
||||
// Schedule the quota reset job
|
||||
_, err := s.cron.AddFunc(s.schedulePattern, s.resetQuotas)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to schedule quota reset job", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the cron scheduler
|
||||
s.cron.Start()
|
||||
|
||||
s.logger.Info("quota reset scheduler started successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the quota reset scheduler
|
||||
func (s *QuotaResetScheduler) Stop() {
|
||||
if !s.enabled {
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("stopping quota reset scheduler")
|
||||
ctx := s.cron.Stop()
|
||||
<-ctx.Done()
|
||||
s.logger.Info("quota reset scheduler stopped")
|
||||
}
|
||||
|
||||
// resetQuotas is the cron job function that resets monthly usage counters
|
||||
func (s *QuotaResetScheduler) resetQuotas() {
|
||||
// Only execute if this instance is the leader
|
||||
if !s.leaderElection.IsLeader() {
|
||||
s.logger.Debug("skipping quota reset - not the leader instance",
|
||||
zap.String("instance_id", s.leaderElection.GetInstanceID()))
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("executing scheduled usage reset as leader instance",
|
||||
zap.String("instance_id", s.leaderElection.GetInstanceID()))
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
output, err := s.resetUsageUC.Execute(ctx)
|
||||
if err != nil {
|
||||
s.logger.Error("usage reset failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("usage reset completed",
|
||||
zap.Int("processed_sites", output.ProcessedSites),
|
||||
zap.Int("reset_count", output.ResetCount),
|
||||
zap.Int("failed_count", output.FailedCount),
|
||||
zap.Time("processed_at", output.ProcessedAt))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue