Appearance
Architecture
RankHub is a full-stack application deployed on Cloudflare's edge network. This page describes the high-level architecture and data flow.
System Overview
+------------------+
| Cloudflare CDN |
+--------+---------+
|
+--------------+--------------+
| |
+--------v--------+ +----------v-----------+
| SolidStart App | | Durable Objects |
| (Cloudflare |<------->| - LeaderboardDO |
| Pages/SSR) | WS | - RateLimitDO |
+--------+---------+ | - TokenTrackingDO |
| +----------+-----------+
| |
+--------v---------+ +----------v-----------+
| PostgreSQL | | Cloudflare Storage |
| (Supabase) |<------->| (DO state + alarms)|
+------------------+ +----------------------+Components
Web App (apps/web/)
SolidStart application with server-side rendering, file-based routing, and TailwindCSS v4.
- Handles HTTP requests, serves HTML, and provides the REST API via Hono.
- Runs on Cloudflare Pages with SSR enabled.
- File-based routing:
/category/[slug],/leaderboard/[slug],/vote,/submit, etc. - API routes under
apps/web/src/routes/api/.
Durable Object Worker (apps/do-worker/)
Cloudflare Durable Objects worker that provides stateful, per-subcategory leaderboard management.
- LeaderboardDO -- One instance per subcategory. Maintains an in-memory
Map<string, ItemRating>for sub-100ms vote processing. Processes votes using the Glicko-2 engine, syncs to PostgreSQL every 30 seconds or every 100 votes via write-back caching. Supports WebSocket connections for real-time leaderboard updates. - RateLimitDO -- Distributed rate limiting with sliding window counters. One instance per rate limit scope (e.g., "vote", "api"). Auto-cleans expired entries every 5 minutes.
- TokenTrackingDO -- Tracks token usage for authenticated users.
Rating Engine (packages/engine/)
Pure TypeScript implementation of the Glicko-2 algorithm using integer storage format.
glicko.ts-- Core rating computation: volatility update (Illinois algorithm), rating/RD update.matchup.ts-- Matchup selection algorithm that considers rating uncertainty and recent matchup history to provide informative pairings.- Uses TypeBox schemas for runtime validation of all data structures.
Database Layer (packages/db/)
Drizzle ORM layer over PostgreSQL (hosted on Supabase).
- Schema definitions with TypeScript inference.
- Migration system: Drizzle auto-migrations plus manual SQL migrations for complex changes.
- Manual migrations live in
packages/db/migrations/manual_*.sqlwith idempotency tracking.
Data Flow: Vote Processing
- Client requests a matchup via
GET /api/v1/matchup?subcategory=<slug>. - Server fetches items from Supabase, selects a matchup using the engine's
selectMatchup(). - Client submits a vote to
POST /api/v1/votewith a Turnstile token. - Vote routes to the
LeaderboardDOinstance for that subcategory. - DO processes the vote with Glicko-2 via
processVote(), updating in-memory state. - DO syncs to Supabase on a 30-second alarm or after 100 accumulated votes.
- WebSocket-connected clients receive real-time rating updates.
Deployment
- Web app: Cloudflare Pages with SSR (SolidStart preset).
- DO worker: Cloudflare Workers with Durable Objects binding.
- Database: Supabase managed PostgreSQL with transaction pooler for edge connections.
- CI/CD: Forgejo Actions (
.forgejo/workflows/) -- runs migrations, builds, and deploys on push.