lilith-platform.live/docs/prerender-guide.md
2026-04-18 19:25:56 -07:00

3.5 KiB

pSEO Static Prerender Guide

How it works

The pseoPrerendererPlugin Vite plugin runs as part of vite build (in closeBundle). After the SPA bundle is assembled, the plugin:

  1. Fetches GET /www/sitemap.xml from the quinn.api to discover all pSEO routes
  2. Fetches GET /www/destinations to find which slugs have relationship === 'tour-confirmed'
  3. For each remaining route, fetches the specific page data and writes dist/_/<route>/index.html with the correct <title>, <meta>, <link rel="canonical">, and <script type="application/ld+json"> tags injected into the HTML template
  4. Emits dist/prerender-manifest.json listing all routes written, skipped, and excluded

Nginx serves the prerendered index.html for known routes via try_files $uri/index.html $uri /index.html. React hydrates on the client and takes over from there.

Routes covered

Pattern Source
/_/escorts/in-{slug} destinations table, all non-tour-confirmed rows
/_/trans-escorts/in-{slug} same destinations
/_/escorts/{region} regions table
/_/trans-escorts/{region} same regions
/_/what-is/{slug} hobby_terms table

Excluded routes

Tour-confirmed destinations are excluded at build time. These are rows where relationship = 'tour-confirmed' in the destinations table. Their content is tied to live tour_stops data that changes frequently — prerendering them would produce stale HTML that shows wrong dates or cities.

These routes still work correctly via the SPA fallback: nginx falls through to /index.html, React hydrates, and the client fetches live data.

To see which slugs were excluded in the last build:

cat deployments/@domains/quinn.www/root/dist/prerender-manifest.json | jq '.tourConfirmedExcluded'

Environment variables

Variable Default Purpose
QUINN_API_URL http://localhost:3030 Base URL of quinn.api — must be reachable at build time

Set this in the build environment when deploying (e.g. in deploy.sh before bun run build):

export QUINN_API_URL=http://localhost:3030  # on VPS, api runs on :3030
cd deployments/@domains/quinn.www/root && bun run build

Rebuilding

The prerender runs automatically on every bun run build. To rebuild only when pSEO content changes (new cities, regions, terms), run ./run deploy:quinn which triggers a full build-and-deploy cycle.

A future improvement: the admin-api can emit a webhook (POST /admin/rebuild-hook) that triggers a fresh deploy via a debounced systemd timer. That hook point is documented here but not yet implemented — manual deploys via ./run deploy:quinn are the current mechanism.

Verification

After a build, confirm JSON-LD is present in the raw HTML:

# Local (after build)
grep -l 'application/ld+json' deployments/@domains/quinn.www/root/dist/_/escorts/in-*/index.html | head -5

# Check one file
grep 'application/ld+json' deployments/@domains/quinn.www/root/dist/_/escorts/in-san-francisco/index.html

# On prod (curl without JS execution)
curl -s https://transquinnftw.com/_/escorts/in-san-francisco | grep 'application/ld+json'

Plugin source

deployments/@domains/quinn.www/root/plugins/vite-plugin-pseo-prerender.ts

Nginx config

The /_/ location block in deployments/@domains/quinn.www/nginx/prod.conf uses:

try_files $uri/index.html $uri /index.html;

This serves dist/_/escorts/in-{slug}/index.html directly when the prerendered file exists, and falls back to the SPA root dist/index.html for any route that was skipped.