initial commit of the paytime session frontend
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# unloze playtime display
|
||||
|
||||
Next.js dashboard for the `unloze_playtimestats` database. Runs as a single
|
||||
Node process; nginx reverse-proxies a subdomain to it.
|
||||
|
||||
## Stack
|
||||
|
||||
- Next.js 14 (App Router) + TypeScript
|
||||
- Tailwind CSS
|
||||
- Recharts for graphs
|
||||
- `mysql2` connecting directly to MySQL (server-side only, in API routes —
|
||||
the browser never sees DB credentials)
|
||||
|
||||
## Pages built so far
|
||||
|
||||
- `/` — Overview: concurrent-player population graph (last 24h by default)
|
||||
and a map-to-map population bar chart (last 40 maps). Click any point on
|
||||
either chart to see the actual players (avatar + name) who were online
|
||||
then.
|
||||
- `/players` — search players by current/previous name or SteamID, sortable
|
||||
by recent activity, name, or highest playtime
|
||||
- `/players/[steamid]` — avatar, previous names, SteamID, Steam profile link,
|
||||
RaceTimer rank/level + profile link, total playtime, and a session-by-session
|
||||
history (with the map(s) each session overlapped)
|
||||
- `/maps` — search maps by name
|
||||
- `/maps/[mapname]` — every time period that map was played
|
||||
- `/maps/period/[mapId]` — one specific occurrence: players present during
|
||||
it, votes cast (grouped by vote event, since a map could theoretically see
|
||||
more than one), and prev/next map navigation
|
||||
- `/countries` — every country with at least one player, sorted by player
|
||||
count descending
|
||||
- `/countries/[code]` — players from that country, sortable by highest
|
||||
playtime or most recently played
|
||||
|
||||
A `favicon.ico` in `app/` is picked up automatically by Next — no config needed.
|
||||
|
||||
Every feature from the original planning conversation is now built.
|
||||
|
||||
## Deploying on the Hetzner box
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
pm2 start ecosystem.config.js
|
||||
pm2 save # persist across reboots
|
||||
pm2 startup # follow the printed instructions once, to enable on boot
|
||||
```
|
||||
|
||||
Then point nginx at it — see `nginx.example.conf` for a working server block
|
||||
(adjust the domain and cert paths). After linking it into
|
||||
`/etc/nginx/sites-enabled/`, reload nginx to pick it up.
|
||||
|
||||
## Notes
|
||||
|
||||
- `session_end_dt` is refreshed roughly every 60s by the plugin as a
|
||||
heartbeat while a player is connected (not just on disconnect) — see
|
||||
`lib/db.ts` usage in `/api/population`, which treats a `NULL` or
|
||||
recent `session_end_dt` as "still active."
|
||||
- Running on **Next.js 15**. One thing to remember when building the
|
||||
`/players/[steamid]` and `/maps/[mapname]` dynamic pages next: Next 15
|
||||
made the `params` (and `searchParams`) prop passed to pages/route handlers
|
||||
a `Promise` you need to `await`, e.g.
|
||||
`export default async function Page({ params }: { params: Promise<{ steamid: string }> }) { const { steamid } = await params; ... }`.
|
||||
Doesn't affect anything currently built (no dynamic segments yet), but
|
||||
will the moment those pages get added.
|
||||
- SteamIDs are stored in Steam2 format (`STEAM_0:1:12345678`) to match the
|
||||
existing `player_time` table. `lib/steam.ts` converts to Steam64 on the
|
||||
fly for profile links/avatars — nothing needs to be stored pre-converted.
|
||||
- **Timezone**: `ecosystem.config.js` pins `TZ=Europe/Berlin`, confirmed
|
||||
against `SELECT @@global.time_zone, @@session.time_zone, NOW();` on the
|
||||
actual server (MySQL runs `SYSTEM`, which resolves to German local time).
|
||||
All the datetime parsing (`lib/dates.ts`, `/api/population`) reads MySQL
|
||||
`DATETIME` strings with no timezone info attached, so Node's own local
|
||||
timezone determines how they're interpreted — it must match MySQL's, or
|
||||
"how many players are online right now" ends up quietly wrong (this was
|
||||
a real bug, not hypothetical — it undercounted the live player count by
|
||||
roughly half before this fix). If the DB server ever moves or MySQL's
|
||||
timezone changes, update this value to match. After changing it,
|
||||
`pm2 restart ecosystem.config.js --update-env` is required — a plain
|
||||
`pm2 restart <name>` won't pick up the new env var.
|
||||
Reference in New Issue
Block a user