Spaces:
Paused
Paused
Download server.js from Shaurya2020/paddock-api: direct link, hf CLI and curl.
- Browser
- Download file 4.42 kB
-
https://huggingface.co/spaces/Shaurya2020/paddock-api/resolve/main/server.js
- Command line
-
hf download hf://spaces/Shaurya2020/paddock-api/server.js
-
curl -L -o server.js https://huggingface.co/spaces/Shaurya2020/paddock-api/resolve/main/server.js
4.42 kB
| const express = require('express'); | |
| const cors = require('cors'); | |
| const { db, authenticate, ApiError, scope } = require('./src/core.js'); | |
| const app = express(); | |
| app.use(cors()); | |
| app.use(express.json()); | |
| /* ── public ──────────────────────────────────────────────────────────────── */ | |
| app.get('/', (_req, res) => res.json({ | |
| service: 'paddock-api', | |
| docs: 'https://huggingface.co/spaces/Shaurya2020/paddock-api', | |
| console: 'https://Shaurya2020-paddock-console.hf.space', | |
| endpoints: { | |
| health: 'GET /api/health', | |
| login: 'POST /api/auth/login {email, password}', | |
| me: 'GET /api/me', | |
| race_events: 'GET|POST /api/race-events · GET|PUT|DELETE /api/race-events/:id · PATCH /api/race-events/:id/status', | |
| odds: 'GET|POST /api/race-events/:id/odds · GET /api/race-events/:id/odds/history', | |
| betting: 'POST|GET /api/bets · GET /api/bets/:id · PATCH /api/bets/:id/cancel', | |
| settlement: 'GET /api/settlements · GET /api/settlements/:id · POST /api/settlements/:id/{result,settle,void}', | |
| content: 'GET|POST /api/teams · PUT|DELETE /api/teams/:id · same for /api/jockeys' | |
| }, | |
| demo_logins: [ | |
| { email: 'ops@paddock.io', password: 'super', role: 1, sees: 'every operator' }, | |
| { email: 'admin@meridian.in', password: 'meridian', role: 2, sees: 'own rows only' }, | |
| { email: 'admin@kingsgate.in', password: 'kingsgate', role: 2, sees: 'own rows only' } | |
| ] | |
| })); | |
| app.get('/api/health', (_req, res) => { | |
| const t = db.prepare( | |
| `SELECT count(*) n FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name <> '_migrations'` | |
| ).get().n; | |
| res.json({ ok: true, tables: t, uptime_s: Math.round(process.uptime()) }); | |
| }); | |
| /** Demo sign-in. Returns the token the other endpoints expect. */ | |
| app.post('/api/auth/login', (req, res) => { | |
| const crypto = require('node:crypto'); | |
| const { email, password } = req.body || {}; | |
| const hash = crypto.createHash('sha256').update((password || '') + '::paddock').digest('hex'); | |
| const a = db.prepare('SELECT * FROM admins WHERE email = ? AND password_hash = ?').get(email, hash); | |
| if (!a) return res.status(401).json({ error: { message: 'Those credentials do not match an admin.' } }); | |
| res.json({ data: { token: `admin:${a.id}`, admin: { id: a.id, name: a.name, role: a.role, label: a.label } } }); | |
| }); | |
| /* ── everything below requires a token ───────────────────────────────────── */ | |
| app.use('/api', authenticate); | |
| app.get('/api/me', (req, res) => { | |
| const counts = {}; | |
| for (const t of ['race_events', 'bets', 'teams', 'jockeys']) { | |
| const s = scope(req.admin, 'x'); | |
| counts[t] = { | |
| visible: db.prepare(`SELECT count(*) n FROM ${t} x WHERE ${s.sql}`).get(...s.params).n, | |
| total: db.prepare(`SELECT count(*) n FROM ${t}`).get().n | |
| }; | |
| } | |
| res.json({ data: { admin: req.admin, scope: req.admin.role === 1 ? 'all operators' : 'own rows only', counts } }); | |
| }); | |
| const raceEvents = require('./src/raceEventsApi.js'); | |
| const odds = require('./src/oddsApi.js'); | |
| const betting = require('./src/bettingApi.js'); | |
| const settlements = require('./src/settlementsApi.js'); | |
| const content = require('./src/contentApi.js'); | |
| app.use('/api/race-events/:id/odds', odds); // mounted first so it wins over /race-events/:id | |
| app.use('/api/race-events', raceEvents); | |
| app.use('/api/bets', betting); | |
| app.use('/api/settlements', settlements); | |
| app.use('/api', content); // /api/teams and /api/jockeys | |
| /* ── errors ──────────────────────────────────────────────────────────────── */ | |
| app.use((_req, res) => res.status(404).json({ error: { message: 'No such endpoint.' } })); | |
| app.use((err, _req, res, _next) => { | |
| const status = err instanceof ApiError ? err.status : 500; | |
| if (status === 500) console.error(err); | |
| res.status(status).json({ error: { message: err.message, ...(err.detail ? { detail: err.detail } : {}) } }); | |
| }); | |
| const PORT = process.env.PORT || 3000; | |
| if (require.main === module) | |
| app.listen(PORT, '0.0.0.0', () => console.log(`paddock-api listening on ${PORT}`)); | |
| module.exports = app; | |