command('mailings:process-scheduled')->everyMinute(); // Seal the ballot ledger of polls that have closed $schedule->command('polls:seal')->everyFiveMinutes(); // Clean up offline users every minute $schedule->command('presence:cleanup-offline --minutes=5')->everyMinute(); // Clean up old presence data weekly (safety net for real-time cleanup) $schedule->command('presence:cleanup')->weekly(); // Anonymize old IP addresses weekly for GDPR compliance $schedule->command('ip:cleanup') ->weekly() ->mondays() ->at('03:00') ->withoutOverlapping() ->appendOutputTo(storage_path('logs/ip-cleanup.log')); // Daily Scout reindex (runs at 4 AM) $scoutModels = ['User', 'Organization', 'Bank', 'Post', 'Call', 'Tag', 'Transaction']; foreach ($scoutModels as $model) { $schedule->command("scout:import \"App\\Models\\{$model}\"") ->dailyAt('04:00') ->withoutOverlapping() ->before(function () use ($model) { file_put_contents( storage_path('logs/scout-reindex.log'), '[' . now()->toDateTimeString() . '] Starting reindex: ' . $model . PHP_EOL, FILE_APPEND ); }) ->appendOutputTo(storage_path('logs/scout-reindex.log')); } // Process bounce emails every hour (requires IMAP configuration) $schedule->command('mailings:process-bounces --delete') ->hourly() ->withoutOverlapping() ->appendOutputTo(storage_path('logs/bounce-processing.log')) ->when(fn() => ENV('BOUNCE_PROCESSING_ENABLED', false)); // config('app.bounce_processing_enabled', false)); // Clean up old soft bounce records weekly (keep hard bounces, requires IMAP configuration) $cleanupConfig = timebank_config('mailing.bounce_thresholds.automatic_cleanup'); $cleanupDays = $cleanupConfig['cleanup_days'] ?? 90; $cleanupTime = $cleanupConfig['time'] ?? '03:00'; $dayOfWeek = $cleanupConfig['day_of_week'] ?? 1; // Monday $schedule->command("mailings:manage-bounces cleanup --days={$cleanupDays}") ->weekly() ->when(function () use ($dayOfWeek) { return now()->dayOfWeek === $dayOfWeek && config('app.bounce_processing_enabled', false); }) ->at($cleanupTime); // Send expiry warning and expired notification emails for calls $schedule->command('calls:process-expiry') ->daily() ->at('08:00') ->withoutOverlapping() ->appendOutputTo(storage_path('logs/call-expiry.log')); // Mark profiles as inactive when they haven't logged in for configured days $markInactiveTime = timebank_config('profile_inactive.scheduler.mark_inactive_time', '01:30'); $schedule->command('profiles:mark-inactive') ->daily() ->at($markInactiveTime) ->withoutOverlapping() ->appendOutputTo(storage_path('logs/mark-inactive-profiles.log')); // Record which profiles are incomplete, so the state gains a history // the statistics can chart (the rule itself is computed live). $markIncompleteTime = timebank_config('profile_incomplete.scheduler.mark_incomplete_time', '01:45'); $schedule->command('profiles:mark-incomplete') ->daily() ->at($markIncompleteTime) ->withoutOverlapping() ->appendOutputTo(storage_path('logs/mark-incomplete-profiles.log')); // Process inactive profiles daily (send warnings and delete profiles) $processInactiveTime = timebank_config('profile_inactive.scheduler.process_inactive_time', '02:00'); $schedule->command('profiles:process-inactive') ->daily() ->at($processInactiveTime) ->withoutOverlapping() ->appendOutputTo(storage_path('logs/inactive-profiles.log')); // Permanently delete (anonymize) profiles that exceeded grace period after deletion $permanentDeleteTime = timebank_config('profile_inactive.scheduler.permanent_delete_time', '02:30'); $schedule->command('profiles:permanently-delete-expired') ->daily() ->at($permanentDeleteTime) ->withoutOverlapping() ->appendOutputTo(storage_path('logs/permanent-deletions.log')); // Trim inactive profile log files monthly (keep last 30 days) $schedule->command('profiles:trim-logs --days=30') ->monthly() ->at('03:30') ->withoutOverlapping(); // Daily TLS certificate expiry monitor (two-tier NOTICE/URGENT email) if (timebank_config('cert_monitor.enabled', true)) { $certCheck = $schedule->command('cert:check') ->daily() ->at(timebank_config('cert_monitor.run_at', '08:00')) ->withoutOverlapping() ->appendOutputTo(storage_path('logs/cert-monitor.log')); // Dead-man's-switch: ping healthchecks.io on each completed run so a // missed run (box down, scheduler broken) is itself alerted on. $healthcheckUrl = timebank_config('cert_monitor.healthcheck_url'); if ($healthcheckUrl) { $certCheck->thenPing($healthcheckUrl); } } // Health check every 5 minutes, reported to healthchecks.io: database, // Redis, Elasticsearch, the public site and disk space. A failure // alerts at once; missing pings (server down) after the grace period. // Background, so its timeouts never hold up the other scheduled tasks. // No withoutOverlapping(): that needs a cache lock, i.e. Redis, which // is one of the things being checked. if (timebank_config('health_monitor.enabled', true)) { $schedule->command('health:check') ->everyFiveMinutes() ->runInBackground(); } // Delete expired WireChat disappearing messages $wirechatSchedule = timebank_config('wirechat.disappearing_messages.cleanup_schedule', 'everyFiveMinutes'); $wirechatEnabled = timebank_config('wirechat.disappearing_messages.enabled', true); if ($wirechatEnabled) { $schedule->command('wirechat:delete-expired') ->$wirechatSchedule() ->withoutOverlapping() ->appendOutputTo(storage_path('logs/wirechat-cleanup.log')); } } /** * Register the commands for the application. */ protected function commands(): void { $this->load(__DIR__ . '/Commands'); require base_path('routes/console.php'); } }