# leandrosalvas/cs2_wstats architecture

> Architecture diagram of the GitHub repository leandrosalvas/cs2_wstats, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-10). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/leandrosalvas/cs2_wstats
- Repository: https://github.com/leandrosalvas/cs2_wstats

## Overview

CS2 WStats ingests game telemetry and match events, serves persistent statistics and account/admin functions, and streams delayed in-memory snapshots to a React dashboard with a live radar. The primary workflow runs from the CS2 plugin through webhook/GSI ingestion, then to persisted statistics or the RAM-only delay buffer, and finally to browser views. Edges rely on README contracts and SOURCE INDEX references; unsampled route internals are represented at subsystem level rather than assigned speculative calls.

Main entry points: [`app/server/src/lib/prisma.ts`](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/prisma.ts) (Prisma client), [`app/server/src/routes/gsi.routes.ts`](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/gsi.routes.ts) (GSI endpoint), [`app/web/src/api/client.ts`](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/api/client.ts) (API client), [`app/server/src/routes/webhooks.routes.ts`](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/webhooks.routes.ts) (Match webhooks)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_ingest["Game ingestion"]
  node_cs2["CS2 plugin<br/>[CS2WStats.cs]"]
  node_gsi["GSI endpoint<br/>[gsi.routes.ts]"]
  node_webhooks["Match webhooks<br/>[webhooks.routes.ts]"]
  node_normalize["GSI normalization<br/>[normalize.ts]"]
end

subgraph group_live["Live state"]
  node_live_state["Live state<br/>[liveState.ts]"]
  node_delay["Delay buffer"]
  node_ws["Live WebSocket<br/>[wsHub.ts]"]
  node_status["Server status<br/>[status.routes.ts]"]
end

subgraph group_stats["Statistics"]
  node_dashboard_api["Dashboard API"]
  node_players_api["Player APIs<br/>[players.routes.ts]"]
  node_ranking_api["Ranking API<br/>[ranking.routes.ts]"]
  node_matches_api["Match APIs<br/>[matches.routes.ts]"]
  node_player_logic["Player metrics<br/>[players.ts]"]
  node_db_client["Prisma client<br/>[prisma.ts]"]
  node_postgres[("PostgreSQL")]
end

subgraph group_identity["Identity and admin"]
  node_auth_routes["Authentication API<br/>[auth.routes.ts]"]
  node_auth_service["Session and OAuth<br/>[authService.ts]"]
  node_rbac["Role authorization<br/>[rbac.ts]"]
  node_rcon["RCON administration<br/>[rconService.ts]"]
  node_rcon_routes["RCON API<br/>[rcon.routes.ts]"]
end

subgraph group_web["Web experience"]
  node_app["React routes<br/>[App.tsx]"]
  node_client["API client<br/>[client.ts]"]
  node_pages["Stats pages"]
  node_live_page["Live radar page<br/>[LiveRadarPage.tsx]"]
  node_live_hook["Live socket hook<br/>[useLiveSocket.ts]"]
  node_renderer["Radar renderer<br/>[RadarRenderer.ts]"]
  node_map_registry["Map calibration<br/>[mapRegistry.ts]"]
  node_admin_pages["Admin pages<br/>[AdminRconPage.tsx]"]
end

node_browser(("Browser user"))

node_cs2 -->|"sends telemetry"| node_gsi
node_cs2 -->|"sends match events"| node_webhooks
node_gsi -->|"normalizes payload"| node_normalize
node_gsi -.->|"updates status"| node_live_state
node_gsi -->|"buffers snapshots"| node_delay
node_webhooks -->|"learns teams"| node_live_state
node_webhooks -->|"persists match stats"| node_db_client
node_delay -->|"broadcasts after delay"| node_ws
node_live_state -.->|"serves heartbeat status"| node_status
node_dashboard_api -->|"reads aggregates"| node_db_client
node_dashboard_api -->|"formats player metrics"| node_player_logic
node_players_api -->|"queries players"| node_db_client
node_players_api -->|"formats ranking data"| node_player_logic
node_ranking_api -->|"queries ranking"| node_db_client
node_ranking_api -->|"formats ranking"| node_player_logic
node_matches_api -->|"queries matches"| node_db_client
node_db_client -->|"reads and writes"| node_postgres
node_auth_routes -->|"signs sessions"| node_auth_service
node_auth_routes -->|"reads users"| node_db_client
node_rbac -->|"checks session roles"| node_auth_service
node_rcon_routes -->|"authorizes requests"| node_rbac
node_rcon_routes -->|"dispatches commands"| node_rcon
node_browser -->|"opens dashboard"| node_app
node_app -->|"routes views"| node_pages
node_app -->|"routes admin views"| node_admin_pages
node_pages -.->|"requests statistics"| node_client
node_live_page -->|"uses live updates"| node_live_hook
node_live_hook -->|"connects WebSocket"| node_client
node_client -->|"receives snapshots"| node_ws
node_live_page -->|"renders live map"| node_renderer
node_renderer -->|"uses map calibration"| node_map_registry
node_admin_pages -->|"calls admin APIs"| node_client

click node_cs2 "https://github.com/leandrosalvas/cs2_wstats/blob/main/cs2-plugin/CS2WStats/CS2WStats.cs"
click node_gsi "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/gsi.routes.ts"
click node_webhooks "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/webhooks.routes.ts"
click node_normalize "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/gsi/normalize.ts"
click node_live_state "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/gsi/liveState.ts"
click node_delay "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/gsi/GSIBufferManager.ts"
click node_ws "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/wsHub.ts"
click node_status "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/status.routes.ts"
click node_dashboard_api "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/dashboard.routes.ts"
click node_players_api "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/players.routes.ts"
click node_ranking_api "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/ranking.routes.ts"
click node_matches_api "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/matches.routes.ts"
click node_player_logic "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/players.ts"
click node_db_client "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/prisma.ts"
click node_auth_routes "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/auth.routes.ts"
click node_auth_service "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/services/authService.ts"
click node_rbac "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/plugins/rbac.ts"
click node_rcon "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/services/rconService.ts"
click node_rcon_routes "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/rcon.routes.ts"
click node_app "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/App.tsx"
click node_client "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/api/client.ts"
click node_pages "https://github.com/leandrosalvas/cs2_wstats/tree/main/app/web/src/pages"
click node_live_page "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/pages/LiveRadarPage.tsx"
click node_live_hook "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/hooks/useLiveSocket.ts"
click node_renderer "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/radar/RadarRenderer.ts"
click node_map_registry "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/radar/mapRegistry.ts"
click node_admin_pages "https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/pages/AdminRconPage.tsx"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_cs2,node_gsi,node_webhooks,node_normalize,node_browser toneBlue
class node_live_state,node_delay,node_ws,node_status toneAmber
class node_dashboard_api,node_players_api,node_ranking_api,node_matches_api,node_player_logic,node_db_client,node_postgres toneMint
class node_auth_routes,node_auth_service,node_rbac,node_rcon,node_rcon_routes toneRose
class node_app,node_client,node_pages,node_live_page,node_live_hook,node_renderer,node_map_registry,node_admin_pages toneIndigo
```

## Components

### Game ingestion

- [CS2 plugin](https://github.com/leandrosalvas/cs2_wstats/blob/main/cs2-plugin/CS2WStats/CS2WStats.cs): `cs2-plugin/CS2WStats/CS2WStats.cs`
- [GSI endpoint](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/gsi.routes.ts): `app/server/src/routes/gsi.routes.ts`
- [Match webhooks](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/webhooks.routes.ts): `app/server/src/routes/webhooks.routes.ts`
- [GSI normalization](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/gsi/normalize.ts): `app/server/src/gsi/normalize.ts`

### Live state

- [Live state](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/gsi/liveState.ts): `app/server/src/gsi/liveState.ts`
- [Delay buffer](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/gsi/GSIBufferManager.ts): `app/server/src/gsi/GSIBufferManager.ts`
- [Live WebSocket](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/wsHub.ts): `app/server/src/lib/wsHub.ts`
- [Server status](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/status.routes.ts): `app/server/src/routes/status.routes.ts`

### Statistics

- [Dashboard API](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/dashboard.routes.ts): `app/server/src/routes/dashboard.routes.ts`
- [Player APIs](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/players.routes.ts): `app/server/src/routes/players.routes.ts`
- [Ranking API](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/ranking.routes.ts): `app/server/src/routes/ranking.routes.ts`
- [Match APIs](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/matches.routes.ts): `app/server/src/routes/matches.routes.ts`
- [Player metrics](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/players.ts): `app/server/src/lib/players.ts`
- [Prisma client](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/lib/prisma.ts): `app/server/src/lib/prisma.ts`
- **PostgreSQL**

### Identity and admin

- [Authentication API](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/auth.routes.ts): `app/server/src/routes/auth.routes.ts`
- [Session and OAuth](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/services/authService.ts): `app/server/src/services/authService.ts`
- [Role authorization](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/plugins/rbac.ts): `app/server/src/plugins/rbac.ts`
- [RCON administration](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/services/rconService.ts): `app/server/src/services/rconService.ts`
- [RCON API](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/server/src/routes/rcon.routes.ts): `app/server/src/routes/rcon.routes.ts`

### Web experience

- [React routes](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/App.tsx): `app/web/src/App.tsx`
- [API client](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/api/client.ts): `app/web/src/api/client.ts`
- [Stats pages](https://github.com/leandrosalvas/cs2_wstats/tree/main/app/web/src/pages): `app/web/src/pages`
- [Live radar page](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/pages/LiveRadarPage.tsx): `app/web/src/pages/LiveRadarPage.tsx`
- [Live socket hook](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/hooks/useLiveSocket.ts): `app/web/src/hooks/useLiveSocket.ts`
- [Radar renderer](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/radar/RadarRenderer.ts): `app/web/src/radar/RadarRenderer.ts`
- [Map calibration](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/radar/mapRegistry.ts): `app/web/src/radar/mapRegistry.ts`
- [Admin pages](https://github.com/leandrosalvas/cs2_wstats/blob/main/app/web/src/pages/AdminRconPage.tsx): `app/web/src/pages/AdminRconPage.tsx`

### Other components

- **Browser user**

## Connections

- CS2 plugin → GSI endpoint: sends telemetry
- CS2 plugin → Match webhooks: sends match events
- GSI endpoint → GSI normalization: normalizes payload (evidence: `app/server/src/index.ts`)
- GSI endpoint → Live state: updates status
- GSI endpoint → Delay buffer: buffers snapshots
- Match webhooks → Live state: learns teams (evidence: `app/server/src/routes/webhooks.routes.ts`)
- Match webhooks → Prisma client: persists match stats (evidence: `app/server/src/routes/webhooks.routes.ts`)
- Delay buffer → Live WebSocket: broadcasts after delay
- Live state → Server status: serves heartbeat status
- Dashboard API → Prisma client: reads aggregates (evidence: `app/server/src/routes/dashboard.routes.ts`)
- Dashboard API → Player metrics: formats player metrics (evidence: `app/server/src/routes/dashboard.routes.ts`)
- Player APIs → Prisma client: queries players (evidence: `app/server/src/routes/players.routes.ts`)
- Player APIs → Player metrics: formats ranking data (evidence: `app/server/src/routes/players.routes.ts`)
- Ranking API → Prisma client: queries ranking (evidence: `app/server/src/routes/ranking.routes.ts`)
- Ranking API → Player metrics: formats ranking (evidence: `app/server/src/routes/ranking.routes.ts`)
- Match APIs → Prisma client: queries matches (evidence: `app/server/src/routes/matches.routes.ts`)
- Prisma client → PostgreSQL: reads and writes
- Authentication API → Session and OAuth: signs sessions (evidence: `app/server/src/routes/auth.routes.ts`)
- Authentication API → Prisma client: reads users (evidence: `app/server/src/routes/auth.routes.ts`)
- Role authorization → Session and OAuth: checks session roles (evidence: `app/server/src/plugins/rbac.ts`)
- RCON API → Role authorization: authorizes requests (evidence: `app/server/src/routes/rcon.routes.ts`)
- RCON API → RCON administration: dispatches commands (evidence: `app/server/src/routes/rcon.routes.ts`)
- Browser user → React routes: opens dashboard (evidence: `app/web/src/App.tsx`)
- React routes → Stats pages: routes views (evidence: `app/web/src/App.tsx`)
- React routes → Admin pages: routes admin views (evidence: `app/web/src/App.tsx`)
- Stats pages → API client: requests statistics (evidence: `app/web/src/pages/AdminRconPage.tsx`)
- Live radar page → Live socket hook: uses live updates (evidence: `app/web/src/pages/LiveRadarPage.tsx`)
- Live socket hook → API client: connects WebSocket (evidence: `app/web/src/hooks/useLiveSocket.ts`)
- API client → Live WebSocket: receives snapshots
- Live radar page → Radar renderer: renders live map (evidence: `app/web/src/pages/LiveRadarPage.tsx`)
- Radar renderer → Map calibration: uses map calibration (evidence: `app/web/src/radar/RadarRenderer.ts`)
- Admin pages → API client: calls admin APIs (evidence: `app/web/src/pages/AdminRconPage.tsx`)
