feat(analytics): replace custom pipeline with Matomo
Removes the hand-rolled analytics endpoint and SQLite event table in favour of Matomo — a self-hosted, full-featured analytics platform. k8s: - Deploy MariaDB 11 + Bitnami Matomo 5 in the solitaire namespace - Route analytics.aleshym.co ingress to the Matomo service - Remove Datasette sidecar and its BasicAuth middleware/secret - Remove the analytics port from the solitaire-server Service Rust: - Replace AnalyticsClient (custom HTTP endpoint) with MatomoClient (Matomo HTTP Tracking API bulk endpoint); maps game events to Matomo categories - Add matomo_url + matomo_site_id fields to Settings (serde default → None/1) - Privacy toggle in Settings now activates when matomo_url is set (not tied to SyncBackend::SolitaireServer) - Remove POST /api/analytics route from solitaire_server Web: - Add Matomo JS tracking snippet to game.html (/play page) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
//! application against an in-memory SQLite database without starting a real
|
||||
//! TCP listener.
|
||||
|
||||
pub mod analytics;
|
||||
pub mod auth;
|
||||
pub mod challenge;
|
||||
pub mod error;
|
||||
@@ -190,23 +189,6 @@ fn build_router_inner(state: AppState, rate_limit: bool) -> Router {
|
||||
auth_routes
|
||||
};
|
||||
|
||||
// Analytics endpoint — public, but throttled at 5 batches/min per IP to
|
||||
// limit abuse. Rate limiting is skipped in tests (same pattern as auth).
|
||||
let analytics_route = Router::new().route("/api/analytics", post(analytics::ingest));
|
||||
let analytics_route = if rate_limit {
|
||||
let governor_conf = Arc::new(
|
||||
GovernorConfigBuilder::default()
|
||||
.key_extractor(SmartIpKeyExtractor)
|
||||
.per_second(12) // 1 token / 12 s = 5 / min steady-state
|
||||
.burst_size(5)
|
||||
.finish()
|
||||
.expect("invalid analytics governor config"),
|
||||
);
|
||||
analytics_route.layer(GovernorLayer::new(governor_conf))
|
||||
} else {
|
||||
analytics_route
|
||||
};
|
||||
|
||||
// Public endpoints (no auth, no rate limit beyond defaults).
|
||||
let public = Router::new()
|
||||
.route("/api/daily-challenge", get(challenge::daily_challenge))
|
||||
@@ -251,7 +233,6 @@ fn build_router_inner(state: AppState, rate_limit: bool) -> Router {
|
||||
Router::new()
|
||||
.merge(protected)
|
||||
.merge(auth_routes)
|
||||
.merge(analytics_route)
|
||||
.merge(public)
|
||||
.merge(web)
|
||||
// Reject request bodies larger than 1 MB.
|
||||
|
||||
Reference in New Issue
Block a user