Code in a site
A site is files by default. A site can also run code: an Astro action, an API route, or a page rendered on demand. Pages stay files, and a page view runs no code.
The plan must allow code. Until it does, a publish with code is refused.
Turn it on
Add the adapter to astro.config.mjs. It is already installed.
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
adapter: cloudflare(),
// ...
});
Do not use Astro.session. It needs a binding the site does not get.
What the code gets
In env |
Is |
|---|---|
DB |
the site's store, when the site has migrations |
CLAVE_API, CLAVE_SITE_KEY |
the runtime API; a draft gets a key of its own |
CLAVE_ENV |
live or draft |
| your own names | the site's secrets; the draft has values of its own |
A request from the code goes out only to hosts on the site's
egress list (egress in clave.site.json), and to
the runtime API. Each call has limits:
50 ms of CPU time and 50 requests.
clave.site.json
A file named clave.site.json at the root of the repository holds the
site's settings. Three of its keys say what the code
asks of the platform: events, schedules and private. egress is the
list of hosts the code may call.
{
"events": ["payment.settled", "payment.refunded"],
"schedules": [{ "name": "reminders", "cron": "0 9 * * *" }],
"private": ["/staff/", { "path": "/admin/", "roles": ["owner"], "labels": ["admin"] }]
}
A fault in it stops the publish, and the message says which part.
Events
The platform sends POST /clave/events to the live site for each event it
asked for. The body is:
{ "id": "evt_...", "kind": "payment.settled", "created_at": "...", "data": { "payment_id": "pay_...", "reference": "order_123", "amount_minor": 2500, "currency": "eur", "late": false } }
The events today:
| Kind | data |
|---|---|
payment.settled |
payment_id, reference, currency, amount_minor, late |
payment.refunded |
payment_id, reference, currency, amount_minor, refunded_minor |
reference is the site's own, and is empty when the site gave none.
late is true when the money arrived after the hold ran out: the site's
code decides what that means. refunded_minor is the total given back so
far, not the amount of this one refund. It equals amount_minor when all
of it went back.
Answer with a 2xx only when the event is handled. A 2xx is final: an event answered with one is never sent again, even when the code ignored it. Any other answer, a redirect included, or no answer in 30 seconds, is sent again. The wait starts at 30 seconds and doubles to at most one hour, for 24 hours. An event can arrive twice, so keep the ids you handled in the store.
Write the page at src/pages/clave/events.ts. Only the platform can call
it: a request to /clave/ without the platform's signature gets a 404.
The platform checks the signature and takes the header off, so the page
never sees it and has no need to check it.
Schedules
The platform sends POST /clave/schedules/{name} when the cron
expression is due. The draft keeps its own timers, from the
clave.site.json it was built with, and they go to the draft's address. Five fields, in UTC. The shortest interval is 15
minutes. A cron in wrangler.jsonc does nothing.
Private paths
A path in private is for the site's staff, who sign in with Clave. A
draft guards its own private paths the same way, on its own address. The
site's code then gets a Clave-Identity on every request. See
Staff.
A path is a string, or { "path": ..., "roles": [...], "labels": [...] }.
A string opens for anyone with a role on the site. With roles or
labels, a person needs one of those roles (owner, collaborator,
staff) or one of those labels, from the staff list. When two paths
match, such as /admin/ and /admin/money/, the person must pass both.
At most 5 paths, and 5 labels for each.
Logs
Logs answers with what the code logged and threw in the last hour.
Restore
Restoring an old version brings back code and not data. The store stays as it is, so code must not assume the schema it had yesterday.