51917bcbaf
Rate limiting: - Shop POST endpoints get per-IP throttles with distinct prefixes so login, register, password-email, and password-reset have separate buckets: login/register/pw-reset = 5/min, pw-email = 3/min - OcrInvoiceService gates per-tenant via RateLimiter (30/hour) so a runaway uploader can't burn Claude Vision spend Health monitor (poor-man's monitoring): - HealthCheckCommand probes DB (SELECT 1), cache write/read, public storage write/read, and most-recent backup age. On any failure, pushes a Telegram alert via HEALTH_ALERT_BOT_TOKEN/HEALTH_ALERT_CHAT_ID. Dedups identical failures within a 30-min window via cache. - Scheduled every 10 min. Pair with external uptime monitoring (UptimeRobot, Better Stack hitting /up) for total-outage coverage. - .env.example documents the two new env vars. VAPID secret hygiene: - credentials.md no longer stores the VAPID_PRIVATE_KEY; the source of truth is the Coolify env on the autocrm app. Doc points to where to read it (UI or API). Mitigates accidental git leak. Tests (4 new): - shop login throttles after 5 attempts (6th = 429); register throttle is independent of login (separate prefix); health command runs clean; dedup cache path exercised Full suite: 138 passed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.6 KiB
PHP
48 lines
1.6 KiB
PHP
<?php
|
|
|
|
use Illuminate\Console\Scheduling\Schedule;
|
|
use Illuminate\Foundation\Inspiring;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Schedule as ScheduleFacade;
|
|
|
|
Artisan::command('inspire', function () {
|
|
$this->comment(Inspiring::quote());
|
|
})->purpose('Display an inspiring quote');
|
|
|
|
// Daily tenant backups at 03:00 — auto-rotates after 14 days.
|
|
ScheduleFacade::command('backup:tenants --keep=14')
|
|
->dailyAt('03:00')
|
|
->withoutOverlapping()
|
|
->onOneServer();
|
|
|
|
// AI chat cleanup — keep tokens spend in check.
|
|
ScheduleFacade::command('queue:prune-batches --hours=48')->daily();
|
|
ScheduleFacade::command('queue:prune-failed --hours=72')->daily();
|
|
|
|
// Weekly supplier rating recomputation — Monday 04:00.
|
|
ScheduleFacade::command('suppliers:rate --days=90')
|
|
->weeklyOn(1, '04:00')
|
|
->withoutOverlapping()
|
|
->onOneServer();
|
|
|
|
// Daily service reminders at 09:00 (tenant-local time = UTC; adjust per-tenant later).
|
|
ScheduleFacade::command('reminders:send')
|
|
->dailyAt('09:00')
|
|
->withoutOverlapping()
|
|
->onOneServer();
|
|
|
|
// Weekly seasonal tire-swap reminders — Monday 09:30. Self-gates to the
|
|
// Feb 15-Mar 15 / Sep 15-Oct 15 windows; outside them it no-ops.
|
|
ScheduleFacade::command('tires:remind-seasonal')
|
|
->weeklyOn(1, '09:30')
|
|
->withoutOverlapping()
|
|
->onOneServer();
|
|
|
|
// Internal health probe every 10 min — pushes Telegram alerts via
|
|
// HEALTH_ALERT_BOT_TOKEN/CHAT_ID env when DB/cache/storage/backup fails.
|
|
// Pair with external uptime monitoring for total-outage coverage.
|
|
ScheduleFacade::command('health:check --silent')
|
|
->everyTenMinutes()
|
|
->withoutOverlapping()
|
|
->onOneServer();
|