Serve the pages the panel publishes

The landing was one address. It now also answers on the pages the panel
publishes -- "Sobre nosotros" and the like -- at the addresses the API
itself chooses: `path` and `alternates` put the default language
unprefixed and every other one under its code, so `/sobre-nosotros`,
`/en/about-us`, `/ca/qui-som` are the routes. `core/api.ts#contentRoute`
is the single rule for that shape, and `/es/…` is deliberately not one
of them, so a page has one address rather than two.

Two routes share one load function, because Analog wants a `.server.ts`
beside each `.page.ts`: `[slug]` for the default language, and
`[locale]/[slug]` for the rest.

Whether a slug exists is the API's answer, not something the route table
knows, so the 404 middleware lets page-shaped addresses through and
`ui/content-page.ts` sets the status itself from `ngOnInit` -- the same
`injectResponse()` the landing uses for its 503, and `ngOnInit` because
an input has no value in the constructor. Body HTML comes from the
panel's editor and goes through Angular's sanitiser.

The header shows the featured pages beside the section anchors; the
footer lists them all. On a page the language switcher follows the API's
`alternates`, and a language with no translation leads to the landing in
that language instead of a 404.

Verified: lint, 20 unit tests, build, and 6 e2e -- including a new one
that a page-shaped address the API does not know answers 404 with
`robots: noindex`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
pigui
2026-09-28 09:55:10 +02:00
parent 47c9af682c
commit 80a1924519
19 changed files with 775 additions and 19 deletions

View File

@@ -1,12 +1,14 @@
import { defineEventHandler, getRequestURL, setResponseStatus } from 'h3';
import { contentRoute } from '../../app/core/api';
/**
* Answers 404 for addresses this app does not serve.
*
* The status cannot be set from the catch-all page's `load`: Analog resolves
* loaders through an internal request, so anything set on that response never
* reaches the browser. Here it can be, and the route table is small enough to
* state outright — the landing is one page.
* state outright — the landing, plus the pages the panel publishes.
*/
const KNOWN_PATHS = new Set(['/', '/index.html']);
@@ -28,5 +30,13 @@ export default defineEventHandler((event) => {
return;
}
// Shaped like a content page (`/sobre-nosotros`, `/en/about-us`). Only the
// API knows whether it exists, and asking it here would be a second request
// for what the page asks anyway — so the page component sets the 404 itself
// when the API has no such page. See `ui/content-page.ts`.
if (contentRoute(pathname)) {
return;
}
setResponseStatus(event, 404);
});