Lite.fi tunnus

api.lite.fi · v1

Lite.fi-tunnus pelikehittäjille

Näin peli liittyy yhteiseen kirjautumiseen ja Top 100 -listoihin. Pelit pysyvät staattisina: ne eivät koskaan näe OIDC-tokeneita, vain krypta_sid-evästeen, jonka selain lähettää automaattisesti.

Pikaohje

  1. Kopioi krypta-id.js pelisi repoon (esim. js/krypta-id.js) — pelien CSP on script-src 'self'.
  2. Lisää CSP:hen connect-src 'self' https://api.lite.fi (ja img-src https://api.lite.fi, jos näytät avatarit).
  3. Kirjautumislinkki: https://api.lite.fi/login?return=https://mato.lite.fi/ — ei popupeja.
  4. Pelin lopuksi KryptaID.submitScore('matopeli', 'klassikko', 1234).

Kirjasto krypta-id.js

<script src="js/krypta-id.js"></script>

const me = await KryptaID.me();          // null = ei kirjautunut
if (!me) loginLink.href = KryptaID.loginUrl();   // palaa tälle sivulle
else nameEl.textContent = me.nickname;

const r = await KryptaID.submitScore('tetris', 'klassikko', 48200, { level: 12 });
// r = { saved: true, best: 48200, previousBest: 30100, rank: 7, pending: false, ... }

const top = await KryptaID.leaderboard('tetris', 'klassikko', 10);
// top.entries = [{ rank: 1, nickname: 'Kalmo', avatar: 'kallo', score: 99100, at: 1761550000000 }, ...]

await KryptaID.myRank('tetris', 'klassikko');    // { score, rank, pending } tai null
KryptaID.avatarUrl(me.avatar);                   // https://api.lite.fi/avatars/kallo.svg
await KryptaID.logout();

Virheissä lupaus hylätään Error-oliolla, jolla on status ja data.error (esim. 401 unauthorized, 403 nickname_required, 429 too_frequent, 422 score_out_of_range). Devissä voit asettaa ennen kirjastoa window.KRYPTA_ID_BASE = 'https://krypta-api.dev.empor.fi'.

API

KutsuKuvaus
GET /login?return=URLAloittaa Empor ID -kirjautumisen (myös tilin luonti). return hyväksytään vain sivustodomaineille (lite.fi,krypta.gg) ja niiden alidomaineille. Ensikertalainen valitsee nimimerkin ennen paluuta.
GET /logout?return=URLUloskirjautumissivu (tai POST /api/logout).
GET /api/me{ id, nickname, avatar, avatarUrl, needsNickname } tai 401. id on pysyvä julkinen tunniste (ei Empor ID:n sub).
POST /api/scoresRunko { game, board?, score, meta? }. Säilyttää paras-tuloksen per (pelaaja, peli, lista). 201 = uusi ennätys, 200 = ei parantunut. Vaatii nimimerkin. meta ≤ 1 kt JSON-olio.
GET /api/leaderboard?game=&board=&limit=100Julkinen, välimuisti 30 s. board puuttuu → pelin oletuslista.
GET /api/leaderboard/me?game=&board=Oma tulos ja sijoitus (kirjautunut).
GET /api/users/:nicknameJulkinen profiili: { nickname, avatar, joined, scores: [{ game, board, score, rank, at }] }.
GET /api/gamesPelit ja niiden listat, järjestys (desc/asc) ja muoto.
GET /api/boards?game=Pelin listat pelaajamäärineen (myös päivän listat).
GET /api/daily?date=&limit=3Kaikkien pelien päivän kärki, ks. Päivän listat.
/api/ideasIdeat ja äänestys, ks. Ideat.
/api/leaguesLiigat, ks. Liigat.
wss://api.lite.fi/partyBilemoodin huonevälitys, ks. Bilemoodi.
POST /api/internal/verify-sessionPalvelinten välinen, ks. Backend.

Listat (board): pienet kirjaimet, numerot ja -, max 40 merkkiä, pelikohtainen regex. Päivän haaste: daily-YYYY-MM-DD (Suomen aikaa) — vastaanotetaan vain tänään ja eilen.

Päivän listat

Ideat

GET /api/ideas?sort=votes|new&status=&limit=50&offset=0Julkinen. { ideas: [{ id, title, body, status, votes, author: { nickname, avatar } | null, createdAt, voted? }] }. voted vain kirjautuneelle. Hylätyt näkyvät vain status=hylatty-suodattimella.
POST /api/ideasKirjautunut + nimimerkki. { title (3–80), body? (≤ 1000) }. Kirosana- ja linkkisuodatin (sivuston omat domainit saa mainita), 3 ideaa / vrk / käyttäjä (429 idea_limit).
POST /api/ideas/:id/vote · DELETE …/voteYksi ääni per käyttäjä, peruttavissa. → { id, voted, votes }
DELETE /api/ideas/:idTekijä voi poistaa oman idean, kun tila on vielä uusi.

Tilat: uusi, tyon-alla, julkaistu, hylatty — ylläpito vaihtaa (myös piilotus/poisto).

Liigat

GET /api/leaguesOmat liigat { leagues: [{ id, name, code, isOwner, members }] }
POST /api/leagues{ name (3–40) } → perustaja on omistaja ja jäsen. Max 20 liigaa / käyttäjä.
POST /api/leagues/join{ code } (8 merkkiä, kirjainkoolla ei väliä). Max 500 jäsentä. 10 väärää koodia / 10 min → 429.
GET /api/leagues/:idVain jäsenille (muille 404): nimi, koodi, jäsenet.
GET /api/leagues/:id/leaderboard?game=&board=&week=YYYY-WwwJäsenten viikon paras (ISO-viikko Suomen aikaa, oletus kuluva). Jokainen POST /api/scores päivittää viikon parhaan, vaikka kaikkien aikojen paras ei parantuisi.
POST /api/leagues/:id/leavePoistu. Omistajan lähtiessä omistajuus siirtyy vanhimmalle jäsenelle; viimeisen lähtiessä liiga poistuu.
PATCH /api/leagues/:id · POST …/code · DELETE … · DELETE …/members/:nicknameVain omistaja: nimeä, uusi koodi (vanha lakkaa toimimasta), poista liiga, poista jäsen.

Bilemoodi (TV + puhelimet ohjaimina)

WebSocket wss://api.lite.fi/party. Palvelin vain välittää viestejä, mitään ei tallenneta eikä kirjautumista tarvita. Selaimen Origin pitää olla sallittu (ks. CORS). Kaikki viestit ovat JSON-tekstikehyksiä.

TV (host)

const ws = new WebSocket('wss://api.lite.fi/party?role=host&game=sirkus'
    + '&controller=' + encodeURIComponent('https://sirkus.lite.fi/ohjain'));
// ← { t: 'room', code: 'KXRT', key, game, joinUrl: 'https://sirkus.lite.fi/ohjain?room=KXRT',
//     qr: 'https://api.lite.fi/api/party/qr/KXRT.svg', maxPlayers: 8, players: [] }
// ← { t: 'join', id: 'p1', name: 'Matti', rejoin? }   ← { t: 'leave', id, reason }
// ← { t: 'msg', from: 'p1', d: { …ohjaimen data… } }
ws.send(JSON.stringify({ t: 'send', to: 'p1' /* tai 'all' */, d: { vibrate: 40 } }));
ws.send(JSON.stringify({ t: 'kick', id: 'p1' }));
ws.send(JSON.stringify({ t: 'close' }));             // sulkee huoneen kaikilta
// Yhteys katkesi? Palaa samaan huoneeseen: ?role=host&code=KXRT&key=<key>

controller = pelin ohjainsivu (sallitulla domainilla); joinUrl ja QR-koodi osoittavat sinne ?room=-parametrilla. QR on SVG (<img src="…qr…">, lisää img-src https://api.lite.fi CSP:hen).

Puhelin (player)

const ws = new WebSocket('wss://api.lite.fi/party?role=player&code=KXRT&name=Matti');
// ← { t: 'welcome', id: 'p1', key, name, game, hostConnected }   (tallenna id + key sessionStorageen)
ws.send(JSON.stringify({ x: 0.42, btn: 'A' }));      // mikä tahansa JSON ≤ 256 tavua → TV:lle
// ← { t: 'msg', from: 'host', d }   ← { t: 'host-away' } / { t: 'host-back' }   ← { t: 'error', error: 'too_large' }
// Palaa samaksi pelaajaksi: ?role=player&code=KXRT&id=p1&key=<key>

CORS & CSRF

Huijaussuoja — rehellisesti

Selaimesta lähetetty tulos on aina väärennettävissä. Palvelin tekee sen minkä voi: pelikohtaiset ylä- ja alarajat (yli → 422), lähetystiheyden rajat, ja epäilyttävän suuret (tai miinaharavassa pienet) tulokset liputetaan: ne piilotetaan listoilta, kunnes ylläpito hyväksyy ne. Pelaaja näkee oman tuloksensa merkinnällä "tarkistettavana". Rajat ovat tiedostossa games.config.json — uusi peli tarvitsee sinne rivin.

Pelin oma backend (esim. roguelike)

POST https://api.lite.fi/api/internal/verify-session
X-Krypta-Service-Key: <avain .env.krypta-api:n SERVICE_KEYS-listalta>
Content-Type: application/json

{ "sid": "<krypta_sid-evästeen arvo>" }

→ { "valid": true, "user": { "id": "k3x9…", "nickname": "Kalmo", "avatar": "kallo" } }
→ { "valid": false }

Backend lukee krypta_sid-evästeen omasta pyynnöstään (eväste on koko .lite.fi:lle) ja varmistaa sen tällä. Tallenna linkiksi user.id. Avain annetaan ylläpidolta, eikä sitä koskaan laiteta selaimeen.

Pelit