Tilannekuva 17.8.2026 · päivä ajettiin 15.8.

Sääksi 10+1 Laps — arkkitehtuuri

Kaksi kaaviota: mitä tapahtuu pyynnön aikana, ja miten koodi päätyy ajoon. Edellinen versio tästä dokumentista oli neljä päivää ennen tapahtumaa; tämä kuvaa sen mikä on nyt tuotannossa — live-päivän koneisto ja kaikki mitä sen jälkeen rakennettiin: arkisto, replay, kuvavirta, kuskien omat nauhoitukset, timelapse ja 2027-äänestys. Kaikki pyörii Cloudflaren ilmaistasolla.

Ajonaikainen rakenne

Koko järjestelmä on staattinen sivusto ja yksi Worker. Kiinnostavat kohdat ovat ne, mitä selain ei voi tehdä itse — ja ne, mitä Worker ei ehdi tehdä, koska ilmaistaso antaa pyynnölle noin 10 ms CPU-aikaa ja noin 50 alipyyntöä.

SELAIMET JA PUHELIN CLOUDFLARE · SAAKSI.BIKE ULKOISET PALVELUT Kävijän selain Kuskin selain Kuskin puhelin Järjestäjän selain Pages Worker · saaksi-live KV · LIVE R2 · arkisto OSM + unpkg Strava Beacon etusivu, /replay, /aita, /2027, /pakkauslista kuvat ja oma GPS-jälki, GPX parsitaan selaimessa Beacon, tapahtumapäivänä /admin: hyväksynnät, hätäkatkaisin staattiset sivut, replay-data, timelapse, og-kortit /live.json · /photos · /recordings /2027/vote · /admin + cron: arkistointi 15 min välein, tapahtumapäivänä 2 min beacon-id, kuskit GPX + raakadata, kuvat, nauhoitukset, äänet — sisältö customMetadatassa kartta ja kirjasto kuskien sijainnit 15.8. klo 05–22 HTML, JS, video lomakkeet + Turnstile, valmiiksi laskettu jälki avainlinkki → keksi laatat ja Leaflet suoraan selaimeen nopea tila pysyvä sisältö Beacon päällä koko päivän vain Worker voi lukea: ei CORS-otsakkeita, 10 s reunacache
Sama Worker, kaksi elämää. Tapahtumapäivänä se välitti Stravan sijainnit kartalle ja cron kirjoitti jokaisen kuskin jäljen R2:een kahden minuutin välein — siksi päivä on olemassa, vaikka Strava unohti sen heti tallennuksen jälkeen. Tapahtuman jälkeen sama Worker ottaa vastaan kuvia, GPS-jälkiä ja 2027-ääniä. Mikään niistä ei näy julkisesti ennen hyväksyntää, ja kaikki kolme tallentavat sisältönsä R2-objektin customMetadata-kenttiin: kahdensadan äänen tulostaulu on yksi list()-kutsu, ei kahtasataa hakua — alipyyntöraja teki siitä arkkitehtuuripäätöksen.

Raskas työ ei ole Workerissa

Kuskin GPX parsitaan selaimessa — kilometrit, nousut ja kierrokset lasketaan siellä ja Worker vain tarkistaa, että luvut täsmäävät piirretyn viivan kanssa. Replay-data, timelapse ja musiikki lasketaan kokonaan etukäteen ja julkaistaan staattisina tiedostoina.

Avoimet lomakkeet, hyväksyntä portilla

Kuka tahansa voi lähettää kuvia tai oman jälkensä ilman tiliä. Turnstile torjuu botit (jokaisella lomakkeella oma action), ja sisältö tulee näkyviin vasta kun merkkiobjekti kirjoitetaan R2:een. Kuka tahansa saa piilottaa kuvan; vain ylläpito palauttaa.

Hätäkatkaisin

Yksi kytkin sulkee kaikki kirjoituspolut ja piilottaa sisällön poistamatta mitään. Kuskien sijainnit jatkuivat silti, koska ne tulivat Stravasta eikä niitä voinut lähettää tänne. Vapautus on yksi painallus.

Julkaisu ja jälkikäsittely

Kaksi wrangler-komentoa samasta repositoriosta, ja niiden rinnalla Python-putki, joka jalostaa arkiston sivuiksi.

KEHITYSKONE CLOUDFLARE sportstracker/ pipeline/ · Python GitHub Pages · saaksi.bike Worker · saaksi-live KV R2 site/ — sivut, replay-data, video live-worker/ — Worker-koodi pipeline/ — jälkikäsittely functions/ — 301 vanhalta hostilta arkiston GPX + kuvat sisään → data.json, timelapse + musiikki, og-kortit, rekonstruoidut kellot lähdekoodi, ei julkaisua saaksi-10-laps · oma domain, vanha pages.dev-host 301:nä Web Analytics injektoidaan täällä vars: HUB, EVENT_DATE, sivuavain secrets: ADMIN_KEY, SESSION_SECRET, TURNSTILE_SECRET, INGEST_KEY crons: */15 aina, */2 tapahtumapäivä git push wrangler pages deploy site --branch main wrangler deploy tulokset site/-hakemistoon
Putki sulkee ympyrän. Cron arkistoi päivän R2:een; pipeline/ lukee arkiston ja kuvat, rakentaa replay-datan, rekonstruoi aikaleimattomien jälkien kellot vierellä ajaneen jäljestä, leipoo timelapsen musiikkeineen ja piirtää jokaiselle jaettavalle osoitteelle oman kortin — ja tulokset julkaistaan takaisin samalle staattiselle sivustolle. GitHubissa on lähdekoodi, mutta julkaisu tapahtuu yhä yhdeltä koneelta.
OsoiteMitä se onData
saaksi.bike Tapahtumasivu: kuvavirta, oma jälki -lomake, aita-video Worker: /photos, /recordings
/live Live-kartta — tapahtumapäivän näkymä live.json, 10 s reunacache
/replay Koko päivä uudelleen: 5 kuskia, 39 kuvaa, jaettavat hetkilinkit staattinen data.json, kuvat livenä
/aita Timelapse: sama portti joka kierroksella, kello ja km kuvassa staattinen mp4, ääni syntetisoitu
/2027 Avoin äänestys ensi vuoden päivästä — piilotettu, linkittämätön yksi R2-objekti per ääni
/pakkauslista Katselmoitu pakkauslista — mikä toimi 203 km:n päivänä staattinen, rastit selaimessa
…workers.dev/admin Hyväksynnät, aikaleimakorjaukset, hätäkatkaisin avainlinkki → 7 vrk keksi

Jokaisella jaettavalla osoitteella oma kortti

Fragmentti (#kuvat) ei voi saada omaa esikatselua, koska WhatsApp näkee vain sivun — siksi jaettavat asiat ovat oikeita polkuja. Kortit piirretään sivujen omasta datasta, joten ne eivät voi valehdella.

Rekonstruoitu kello sanotaan ääneen

Sports Trackerin vienti on reitti ilman aikaleimoja. Kahden kuskin kello on luettu vierellä ajaneen jäljestä — lähin piste, aikaisin samanarvoinen voittaa, mahdottomat nopeudet levitetään matkalla. Merkitty dataan ja kerrottu sivulla.

Raakadata jalostetun rinnalla

Arkisto tallentaa aina Stravan koskemattoman vastauksen GPX:n viereen, ja kuskin lähettämä alkuperäinen tiedosto säilyy missä tahansa muodossa se tuli. Rekonstruktio on arvostelma, joka pitää voida tehdä uudestaan.

Mikä muuttui 11.8. jälkeen

Edellisen tilannekuvan aukkolista, rivi riviltä.

Ansat jotka on kohdattu

Jokainen maksoi aikaa kerran. Ne eivät näy kaavioissa mutta kuuluvat kuvaan.

~50 alipyyntöä per pyyntö

Äänestyksen tulostaulu objektirunkoina olisi ylittänyt rajan heti. Sisältö customMetadata-kenttiin ja yksi listaus — sama temppu kuvavirrassa ja nauhoituslistassa.

R2-metadata kulkee HTTP-otsakkeissa

Ääkköset ja rivinvaihdot kaatavat kirjoituksen, ellei kaikkea prosenttikoodaa. Ja metadataa ei voi päivittää ilman että objektin runko kirjoitetaan uudelleen — aikaleiman korjaus on siirto uuteen avaimeen, koska aika on avaimen etuliite.

--branch main, repon juuresta

Ilman haaraa julkaisu menee preview-osoitteeseen eikä mikään kerro virheestä; site/-hakemistosta ajettuna wrangler etsii polkua site/site. Reunat päivittyvät eri tahtiin vielä minuutin pari julkaisun jälkeen.

Pysähtynyt GPS ja „lähin piste"

Kun referenssikuski seisoo, sata pistettä on samassa paikassa ja „lähin" on arpapeli — väärä valinta siirsi kelloa 39 minuuttia ja puristi kymmenen kilometriä kolmeen minuuttiin. Aikaisin samanarvoinen voittaa: saapuminen on hetki jolloin paikalle ensin tultiin.

Neljän tunnin oletuscache

Pages tarjoaa staattista sisältöä neljä tuntia, mikä tapahtuma-aamuna tarkoittaisi eilistä JavaScriptiä. _headers pakottaa HTML:n ja skriptien revalidoinnin joka latauksella; 304 maksaa kierroksen, ei latausta.

CSS-spesifisyys söi napit

.button.primary voitti lomakekohtaiset säännöt ja kaksi lähetysnappia oli kermanvärisiä kermanvärisellä pohjalla. Korjaus oli pinnan mukainen scope, ei !important.