The site now ingests the sidecar's live WebSocket feed and persists it to its own MariaDB, and re-broadcasts curated events to browsers over SSE. - schema: shard_events (append-only notable-kind log, sha1 dedupe_key + INSERT IGNORE for idempotent reconnect backfill), shard_online (current players, upsert/refresh/remove), shard_economy (gold-supply series), shard_houses (per-house decay stage + derived is_idoc). - model/shardEvents + model/shardState: the .db.js/.model.js split; writes take camelCase event data, reads are shaped; online upsert uses COALESCE so a partial char.vitals refresh never blanks login fields. - utils/shardIngest: single dispatcher routing each kind to state writes and/or the event log, then the broadcaster. High-frequency kinds (char.vitals, economy.supply) update state only. A changed server.hello bootId clears the stale online roster. Deps are injected for unit testing. - utils/uoLinkSocket: the server's first outbound WS client (ws dep). Verifies the ws.hello protocol, backfills via /history + /economy on every (re)connect (dedupe handles overlap), reconnects with capped backoff, and mirrors connection state into uo_link_config. Self-guards: only connects when the integration is enabled with a token. - utils/shardBroadcast: SSE fan-out with public (safe kinds only) and admin (all) channels, keepalive pings, per-client cleanup. - server.js: start the ingest socket on boot (no-op until configured) and stop it + close SSE streams on graceful shutdown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011qPmpmVH1xGCiZoz9m9vW3
32 lines
1.0 KiB
JavaScript
32 lines
1.0 KiB
JavaScript
const { query } = require('../../utils/db')
|
|
|
|
// INSERT IGNORE on the UNIQUE dedupe_key — a re-ingested event (WS-reconnect
|
|
// backfill overlap) is silently skipped rather than duplicated. Returns true if
|
|
// a new row was actually inserted.
|
|
async function insertIgnore({ kind, t, bootId, payload, dedupeKey }) {
|
|
const res = await query(
|
|
`INSERT IGNORE INTO shard_events (kind, t, boot_id, payload, dedupe_key)
|
|
VALUES (?, ?, ?, ?, ?)`,
|
|
[kind, t, bootId || null, JSON.stringify(payload), dedupeKey],
|
|
)
|
|
return res.affectedRows > 0
|
|
}
|
|
|
|
// Recent events, newest first. Optional kind filter; limit is clamped by the model.
|
|
async function list({ kind, limit }) {
|
|
if (kind) {
|
|
return query(
|
|
`SELECT id, kind, t, boot_id, payload, created_at
|
|
FROM shard_events WHERE kind = ? ORDER BY t DESC LIMIT ?`,
|
|
[kind, limit],
|
|
)
|
|
}
|
|
return query(
|
|
`SELECT id, kind, t, boot_id, payload, created_at
|
|
FROM shard_events ORDER BY t DESC LIMIT ?`,
|
|
[limit],
|
|
)
|
|
}
|
|
|
|
module.exports = { insertIgnore, list }
|