Plan: prave SEO rute
Hash rute → pravi URL-ovi + prerender, bez Astra (25.9.2026.)
# Plan: prave SEO rute umjesto hash ruta
2026-09-25. Plan refaktora web/. Glasanje je živo, ali je korisnik jedini glasač, pa refaktor ne čeka zatvaranje.
Status (25. 9. 2026. navečer): sve faze 1–5 su u produkciji. Faza 4 i 5 napravljene su zajedno: site poslužuje Worker
maksimirpreko ruta zone (DNS nepromijenjen),stadion-maksimir.domovina.aii*.pages.devdaju 301 namaksimir.domovina.ai. Kako se provjerava i vraća:2026-09-25-regresijske-provjere.md.
# Cilj
Svaka stranica dobiva pravi URL koji crawler vidi kao zasebnu stranicu, s vlastitim naslovom, opisom, canonicalom, OG karticom i već renderiranim sadržajem u HTML-u:
| Sada | Poslije |
|---|---|
/#/ |
/ |
/#/radovi, /#/radovi/6TVJ3MUHR |
/radovi, /radovi/6TVJ3MUHR |
/#/rezultati, /#/rezultati/1 |
/rezultati, /rezultati/1 |
/#/glasanje, /#/glasanje/g/<id> |
/glasanje, /glasanje/g/<id> (javna poveznica ostaje /g/<id>) |
/#/sot, /#/research-08-rezultati#sidro |
/sot, /research-08-rezultati#sidro |
Sve ostalo ostaje isto: izgled, sadržaj, ponašanje glasanja, prijava, ZK ključ u localStorage, podaci i backend.
# Odluka: bez Astra, ostaje Vite
- Najveća vrijednost (indeksiranje 88 radova i dokumenata, OG po stranici) dobiva se prerenderom u postojećem Vite buildu. Za to Astro ne treba.
- Glasanje (Certilia polling, Supabase, Semaphore ZK) je interaktivni kod koji se izvršava na klijentu. U Astru bi bio jedan veliki island, dakle isti kod u novoj ljusci.
- Prepisivanje oko 3000 linija u
.astrokomponente nosi rizik vizualnih i funkcionalnih regresija. Invarijanta „ništa drugo se ne mijenja” time bi bila teško provjerljiva. - Astro se ponovno razmatra samo ako sadržajni dio (radovi, research, docs) nastavi rasti i ručni
docs.tspostane teret.
# Invarijante (moraju vrijediti nakon svake faze)
#contentza svaku rutu daje isti DOM kao danas. Jedina razlika suhrefvrijednosti (#/x→/x).- Vizualno ista stranica na 390 px i 1280 px širine.
- Stare hash poveznice rade:
/#/radovi/Xna učitavanju prelazi na/radovi/X(replaceState, bez novog unosa u povijesti). Poslužitelj hash ne vidi, pa ovo mora raditi na klijentu. /g/<id>i dalje daje OG karticu i vodi na objavu glasa.- Glasanje: prijava, listić, predaja, povlačenje, javna i ZK objava, uvoz/izvoz ključa i potvrda rade bez promjene.
Ključ
maksimir-zk-identityostaje na istom originu i neće se izgubiti. - Sidra unutar dokumenata (
#naslov) rade, uključujući dolazak na stranicu izravno sa sidrom.
# Faze
# Faza 1: jedan izvor URL-ova (bez promjene ponašanja)
- Novi
web/src/routes.tss funkcijomhref(path)i popisom svih ruta (allRoutes(): naslovnica, dokumenti izdocs.ts,radovi+ 88 šifri,rezultati+ 5 nagrada,glasanje). - Svi
href="#/…"literali ulanding.ts,radoviView.ts,rezultatiView.ts,glasanjeView.ts,shareView.ts,main.tsi rewriter umarkdown.tsidu krozhref().location.hash = …uglasanjeView.tspostajenavigate(). - U ovoj fazi
href()i dalje vraća#/…, pa se ništa vidljivo ne mijenja. Tako je refaktor mehanički i lako se provjerava.
# Faza 2: History API ruter
href()vraća/….main.tsčitalocation.pathnameumjestolocation.hash. Klikovi na interne<a>presreću se jednim delegiranim listenerom (pushState+route()),popstatezamjenjujehashchange.- Sidra:
/slug#sidrokoristi pravi fragment. Rewriter umarkdown.tsviše ne treba oblik#/slug#sidro. - Stari hash (
#/…) se na bootu prevodi u put (invarijanta 3). Postojeći prijelaz/g/<id>→ hash postaje/g/<id>→/glasanje/g/<id>. document.titlei<meta name="description">postavljaju se po ruti, iz istog izvora koji koristi prerender.
# Faza 3: prerender u buildu
vite buildostaje. Dodaje se SSR build (vite build --ssr src/prerender.ts) i skriptascripts/prerender.mjskoja podhappy-dompokreće iste view funkcije (renderRad,renderAward,renderDoc…). Za svaku rutu izallRoutes()zapisujedist/<ruta>.htmls:- popunjenim
#nav,#crumbsi#content, <title>,description,<link rel="canonical">, OG/Twitter meta (slika: naslovnica rada ili nagrade, inačeog-glasanje.png),- JSON-LD
CreativeWorkza radove (neobavezno).
- popunjenim
- Mermaid se ne prerenderira; kao i sada, crta se na klijentu.
/glasanjese prerenderira samo kao ljuska s meta podacima (sadržaj ovisi o Supabaseu). - Klijent na bootu normalno renderira rutu u
#content. Kako je HTML isti, nema vidljivog treptaja. Hidracija nije potrebna. dist/sitemap.xml,dist/robots.txtidist/404.html(pravi 404 status umjesto SPA fallbacka za nepoznate puteve).- Datoteke kao
radovi/X.html(a neradovi/X/index.html), pa je canonical bez završne kose crte u skladu s Pagesovimauto-trailing-slash.
# Faza 4: Pages Function i domena
functions/g/[id].ts: cilj preusmjeravanja postaje/glasanje/g/<id>.- Canonical origin je
https://maksimir.domovina.ai.stadion-maksimir.domovina.aii*.pages.devdanas vraćaju 200 s istim sadržajem (duplikat za Google). Oni dobivaju 301 na canonical, preko Bulk Redirects ili provjere hosta u_middleware.ts.
# Faza 5 (neobavezno, zasebno): Pages → Workers static assets
wrangler.jsonc s assets.directory = "dist", html_handling = "auto-trailing-slash",
not_found_handling = "404-page" i malim Worker entrypointom koji preuzima /g/<id> i preusmjeravanje hostova.
Ne utječe na SEO. Radi se samo ako zatreba nešto što Pages ne nudi.
# Provjera
Za svaku fazu, prije deploya:
- DOM diff (Playwright): za svaku rutu iz
allRoutes()uzima se#content.innerHTMLs produkcije (stari hash URL) i s lokalnogvite preview(novi URL). Nakon normalizacijehref="#/x"→href="/x"moraju biti identični. - Screenshot diff na 390 px i 1280 px za naslovnicu,
radovi, jedan rad,rezultati/1, jedan dokument s Mermaidom iglasanje. - Bez JavaScripta:
curl /radovi/6TVJ3MUHRvraća HTML s nazivom rada, opisom i canonicalom. - Kompatibilnost:
/#/radovi/X,/#/sot#sidro,/g/<id>(crawler UA dobiva karticu, preglednik stiže na objavu), povratak unatrag nakon navigacije i izravan ulaz na duboku rutu. - Glasanje: postojeći E2E tok (vidi
docs/2026-09-25-glasanje-javnosti.md) na preview deployu. Ne dira se korisnikovlocalStorageu produkciji, prema pravilu o testiranju. npm run buildbez grešaka.tsc --noEmitdanas pada zbog 13 postojećih grešaka tipova umarkdown.ts(markdown-it bez anotacija). Popravljaju se u fazi 1, kako bitscod tada bio pouzdana provjera.
Deploy ide najprije na deploy:preview URL, provjera 1–5 ponovi se tamo, a tek onda slijedi npm run deploy.
# Povrat
Svaka faza je zaseban commit. Povrat je git revert + npm run deploy. Stare hash poveznice rade i u starom i u
novom kodu, pa povrat ne kvari podijeljene linkove, osim linkova novog oblika /radovi/X podijeljenih u međuvremenu.