Přeskočit na obsah

Napojení a token

Šablona se na patro napojuje jedním tokenem. Není v tom žádná registrace aplikace, žádný OAuth a žádné ID webu v adrese — token sám určuje, čí obsah dostanete.

Vydá ho vlastník webu v administraci patra (Administrace → API tokeny; editor tam nemá přístup). Dostane podobu pat_… a ukáže se jen jednou, při vydání — patro si z něj drží jen otisk a začátek, takže znovu ho vypsat nejde. Když se ztratí, vydá se nový a starý se zruší.

Ten začátek (pat_ a osm znaků) je v seznamu vidět a je tam kvůli jediné otázce: který řádek odpovídá klíči nasazenému na kterém webu. K tokenu se dá při vydání nastavit i platnost; bez ní nevyprší.

Token nese identitu webu (v žargonu patra „tenanta“). Odtud plyne to podstatné:

  • do dotazů se nikdy neposílá ID webu — server si ho odvodí z tokenu;
  • token z jednoho webu nemá jak sáhnout na obsah jiného, i kdyby se o to pokusil, protože se dotaz omezuje na serveru, ne v klientovi;
  • kdo má token, ten webu vidí do publikovaného obsahu.

Je to tajemství, ne veřejný klíč

Sekce “Je to tajemství, ne veřejný klíč”

Token patří k serverové části vaší šablony a nesmí se dostat do prohlížeče. Prakticky to znamená dvě věci:

  1. Ukládá se jako secret, ne jako proměnná v konfiguraci. Na Cloudflare wrangler secret put PATRO_TENANT_TOKEN, lokálně do .dev.vars.
  2. Nikdy se nepředává komponentě, která má client:* direktivu. Astro props ostrůvků serializuje do HTML stránky, takže by token skončil ve zdrojovém kódu, který si přečte kdokoli.

Když potřebuje něco odeslat prohlížeč (typicky formulář komentáře), nechodí do patra přímo — jde na endpoint vaší šablony a ten to s tokenem přepošle. Hotový prostředník na to v balíku je a popisuje ho kapitola o blocích.

Co token umí — celý rozsah, žádné zúžení

Sekce “Co token umí — celý rozsah, žádné zúžení”

Buďme přesní, protože je to bezpečnostní téma. Token nejde vydat užší. Není volba „jen pro čtení“: kdo ho má, umí kromě čtení i těch pár zápisů, které veřejné API má:

  • nahlásit nenalezené adresy a zásahy přesměrování (client.reportRedirects),
  • ohlásit, které oblasti widgetů šablona kreslí (client.declareWidgetAreas),
  • poslat komentář prostředníkem.

Všechny tři zapisují výhradně do dat toho webu, kterému token patří, a mají vlastní stropy (velikost dávky, počet oblastí, délky).

Praktický závěr: hranici drží příslušnost k webu, ne rozsah oprávnění. Token z jednoho webu na cizí obsah nedosáhne, ale uvnitř svého webu ho nic dál nezužuje — počítejte s tím při rozhodování, kam ho uložíte a komu ho dáte.

src/lib/patro.ts
import { env } from "cloudflare:workers"
import { createPatroClient, type PatroClient } from "@patro/render"
export function getPatroClient(): PatroClient {
return createPatroClient({
baseUrl: env.PUBLIC_API_BASE,
token: env.PATRO_TENANT_TOKEN,
})
}

Klient se staví při každém požadavku, ne jednou při startu. Na Cloudflare je env proxy, která se vyhodnocuje až za běhu požadavku — kdyby se hodnota přečetla při načtení modulu, byla by prázdná. Ze stejného důvodu se do funkcí, které klienta potřebují později (middleware, oblasti widgetů), předává továrna getPatroClient, ne hotový klient.

createPatroClient({
baseUrl: "https://patro.priklad.cz", // koncové lomítko se ořeže
token: "pat_…",
fetch: mujFetch, // volitelné — vlastní runtime nebo test
timeoutMs: 5_000, // volitelné — výchozí PATRO_REQUEST_TIMEOUT_MS
})

timeoutMs stojí za jednu větu navíc. Cloudflare žádný strop na podřízené dotazy nemá — pomalé (ne padající) patro by se propsalo přímo do doby, kterou návštěvník čeká na stránku, a nic by to nezastavilo. Výchozích pět sekund je kompromis: dost na studený start patra, málo na to, aby se kvůli výpadku zasekla stránka.

Jméno Druh K čemu
PUBLIC_API_BASE var Adresa patra, např. https://patro.priklad.cz
PATRO_TENANT_TOKEN secret Token webu

Pojmenování je jen konvence našeho pilotního webu — klient bere hodnoty parametrem, takže si je můžete číst odkudkoli.

Jak klient hlásí, že se něco nepovedlo

Sekce “Jak klient hlásí, že se něco nepovedlo”

Tohle je jediná konvence, kterou je potřeba znát dřív, než se napíše první stránka. Klient rozlišuje tři výsledky, ne dva:

Hodnota — povedlo se.

null — legitimní odpověď „tohle tu není“ nebo „beze změny“. Není to chyba a nemá se z toho dělat pětistovka:

  • client.getEntryBySlug vrátí null u slugu, který v daném jazyce publikovaný není → vaše 404;
  • client.getMenu vrátí null u menu, které web nemá → vykreslete stránku bez navigace;
  • metody s knownVersion (client.getRedirects, client.listWidgetAreas, client.getComments) vrátí null, když se od vámi držené podoby nic nezměnilo → nechte si, co máte.

Pozor na rozdíl mezi null a prázdným seznamem. Prázdné pole znamená „existuje to a je to prázdné“; null znamená „to konkrétní, na co ses ptal, tu není“.

Odmítnutý příslib — všechno ostatní: špatný token (401), výpadek patra, překročený strop. Klient nikdy nevrací tiché null místo chyby, takže se rozbité napojení nemůže projevit jako prázdná stránka.

Odmítnutí musí někdo chytit a rozhodnout, jestli se stránka ještě dá vykreslit. Balík na to má trojici, kterou stačí zapojit:

---
import { isOutage, loadContent, outageResponse } from "@patro/render"
import { getPatroClient } from "../lib/patro"
const client = getPatroClient()
const patroDown = (error: unknown): void => {
console.error("patro: obsah stránky — ", error)
}
// Volání schované do proměnné schválně: `astro check` výraz vracený
// z hlavičky stránky nekontroluje, takže tady se ověří, a v `return`
// pak zbývá `outage()`, kde se není čeho chytit.
const outage = (): Response => outageResponse()
const tenant = await loadContent(() => client.getTenant(), patroDown)
if (isOutage(tenant)) return outage()
---

outageResponse odpoví 503 s hlavičkou Retry-After a čitelnou stránkou, ne pětistovkou — pro vyhledávače je to „zkus to za chvíli“, ne „tahle stránka je rozbitá“.

Obal patří jen kolem volání patra. Chyba ve vlastním kódu se nemá vydávat za výpadek patra; ta patří na vaši chybovou stránku.

A platí to jen pro obsah. Navigace, oblasti widgetů a dopočet výpisových bloků se ošetřují tolerantně samy — nedostupné patro u nich znamená stránku bez té ozdoby, ne 503.