A Node.js application for setting LiveHeats round durations from the athlete count in each heat.
The browser renders the interface and calls same-origin /api routes. OAuth
authentication, access tokens, GraphQL event queries, calculations, validation,
and mutations are handled by the Node server.
- Node.js 20 or newer
- LiveHeats API client credentials with the required permissions
With Docker Desktop or Docker Engine and the Compose plugin installed:
docker compose up -dOpen http://localhost:3000.
Useful commands:
docker compose logs -f
docker compose downTo expose the application on a different host port:
$env:PORT=8080
docker compose up -dThen open http://localhost:8080.
The Compose file defaults the public origin to
https://heatmath.abw.com.br. To deploy on a different domain, override it:
$env:APP_ORIGIN="https://heatmath.abw.com.br"
docker compose up -dAPP_ORIGIN may contain a comma-separated list when more than one public origin
is required. The server also recognizes X-Forwarded-Host and
X-Forwarded-Proto from a trusted reverse proxy.
npm install
npm startOpen http://localhost:3000.
For automatic server restarts while developing:
npm run devSet a different port with the PORT environment variable.
POST /api/connectauthenticates with LiveHeats and starts an HTTP-only server session.GET /api/organisations/:shortName/eventsloads organization details and returns events with scheduled/published events first, then all other statuses; each group is sorted by descending date.GET /api/events/:eventIdloads the event, divisions, heats, and competitor counts.POST /api/events/:eventId/apply-durationsreloads the event, independently validates the requested minutes-per-athlete calculation, applies every eligible round update sequentially, and returns the refreshed event.
- OAuth uses
https://liveheats.com/oauth/token. - GraphQL uses
https://liveheats.com/api/graphql. - The requested scopes are
public events/director organisations/manage. - LiveHeats exposes duration changes through
updateRound. When athlete counts differ within a division/round, ABW HeatMath calculates each heat's time, averages those times, rounds to the nearest whole minute, and applies that shared duration to every heat in the round. Empty heats still block the operation because they usually represent an incomplete draw. - Event selection uses
organisationByShortName; the organization short name defaults tocbeawand can be changed in the interface.
- Client credentials and access tokens are held only in server memory.
- The browser receives an opaque, HTTP-only, SameSite session cookie.
- Sessions expire after two hours and disappear whenever the server restarts.
- State-changing routes reject cross-origin browser requests.
- API responses never include credentials or access tokens.
- In production, run the app behind HTTPS and set
NODE_ENV=production. - Session cookies automatically use the
Secureflag when the request arrives over HTTPS. The server trusts one reverse-proxy hop, so an HTTPS proxy should forwardX-Forwarded-Proto.
This in-memory session store is appropriate for a small, single-instance operations tool. For multiple Node instances, replace it with a shared encrypted session store.