storing analytics data

This commit is contained in:
2026-02-27 09:46:51 +01:00
parent 15b7b77d20
commit f0cca76eb3
57 changed files with 3478 additions and 466 deletions

View File

@@ -2,6 +2,25 @@
use Illuminate\Support\Facades\Route;
// ── Per-artwork signal tracking (public) ────────────────────────────────────
// GET /api/art/{id}/similar → up to 12 similar artworks (Meilisearch)
// POST /api/art/{id}/view → record a view (session-deduped, 5 per 10 min)
// POST /api/art/{id}/download → record a download, returns file URL (10/min)
Route::middleware(['web', 'throttle:60,1'])
->get('art/{id}/similar', \App\Http\Controllers\Api\SimilarArtworksController::class)
->whereNumber('id')
->name('api.art.similar');
Route::middleware(['web', 'throttle:5,10'])
->post('art/{id}/view', \App\Http\Controllers\Api\ArtworkViewController::class)
->whereNumber('id')
->name('api.art.view');
Route::middleware(['web', 'throttle:10,1'])
->post('art/{id}/download', \App\Http\Controllers\Api\ArtworkDownloadController::class)
->whereNumber('id')
->name('api.art.download');
/**
* API v1 routes for Artworks module
*

View File

@@ -2,6 +2,7 @@
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;
use App\Uploads\Services\CleanupService;
Artisan::command('inspire', function () {
@@ -14,3 +15,51 @@ Artisan::command('uploads:cleanup {--limit=100 : Maximum drafts to clean in one
$this->info("Uploads cleanup deleted {$deleted} draft(s).");
})->purpose('Delete stale draft uploads and temporary files');
// ── Scheduled tasks ────────────────────────────────────────────────────────────
// Recalculate trending scores every 30 minutes (staggered: 24h first, then 7d)
Schedule::command('skinbase:recalculate-trending --period=24h')
->everyThirtyMinutes()
->name('trending-24h')
->withoutOverlapping();
Schedule::command('skinbase:recalculate-trending --period=7d --skip-index')
->everyThirtyMinutes()
->name('trending-7d')
->runInBackground()
->withoutOverlapping();
// Reset windowed view/download counters so trending uses recent-activity data.
// Downloads are recomputed from the artwork_downloads log (accurate).
// Views are zeroed (no per-view event log) and re-accumulate from midnight.
Schedule::command('skinbase:reset-windowed-stats --period=24h')
->dailyAt('03:30')
->name('reset-windowed-stats-24h')
->withoutOverlapping();
Schedule::command('skinbase:reset-windowed-stats --period=7d')
->weeklyOn(1, '03:30') // Monday 03:30
->name('reset-windowed-stats-7d')
->withoutOverlapping();
// Daily maintenance
Schedule::command('uploads:cleanup')->dailyAt('03:00');
Schedule::command('analytics:aggregate-similar-artworks')->dailyAt('03:10');
Schedule::command('analytics:aggregate-feed')->dailyAt('03:20');
// Drain Redis artwork-stat delta queue so MySQL counters stay fresh.
// Run every 5 minutes with overlap protection.
Schedule::command('skinbase:flush-redis-stats')
->everyFiveMinutes()
->name('flush-redis-stats')
->withoutOverlapping();
// Prune artwork_view_events rows older than 90 days.
// Runs Sunday at 04:00, after all other weekly maintenance.
Schedule::command('skinbase:prune-view-events --days=90')
->weekly()
->sundays()
->at('04:00')
->name('prune-view-events')
->withoutOverlapping();

View File

@@ -378,3 +378,7 @@ Route::middleware(['auth', 'ensure.onboarding.complete'])->prefix('messages')->n
Route::get('/', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'index'])->name('index');
Route::get('/{id}', [\App\Http\Controllers\Messaging\MessagesPageController::class, 'show'])->whereNumber('id')->name('show');
});
// ── Community Activity Feed ───────────────────────────────────────────────────
Route::get('/community/activity', [\App\Http\Controllers\Web\CommunityActivityController::class, 'index'])
->name('community.activity');