isConfigured()) { throw new RuntimeException('Database config missing.'); } $pdo = $app['database']->pdo(); $passwordResetService = new PasswordResetService( $pdo, new MailerService($app['config']['mail'] ?? []), $app['config']['password_reset'] ?? [] ); $rateLimiterService = new RateLimiterService( $pdo, $app['config']['rate_limits'] ?? [] ); $purgedPasswordResetTokens = $passwordResetService->purgeExpiredTokens(); $prunedRateLimits = $rateLimiterService->prune(); $pendingRfid = (int) $pdo->query( 'SELECT COUNT(*) FROM rfid_events WHERE status = "received"' )->fetchColumn(); $summary = [ 'timestamp' => gmdate(DATE_ATOM), 'cleanup' => [ 'password_reset_tokens_removed' => $purgedPasswordResetTokens, 'rate_limit_entries_removed' => $prunedRateLimits, ], 'pending_rfid_events' => $pendingRfid, 'notes' => [ 'Cron bereinigt abgelaufene Passwort-Reset-Tokens und veraltete Rate-Limit-Eintraege.', 'In diesem MVP dient Cron ausserdem als technischer Haken fuer spaetere Reminder, Exporte und RFID-Regeln.', ], ]; fwrite(STDOUT, json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL); } catch (\Throwable $throwable) { fwrite(STDERR, $throwable->getMessage() . PHP_EOL); exit(1); } finally { if (is_file($lockFile)) { unlink($lockFile); } }