Files
landing-app/landing/src/server/middleware/not-found-status.ts
pigui 439f951f2e Give projects and members their own pages
The API has always served a project's and a member's detail, with
`path` and `alternates` in three languages, and the site had nowhere to
show it. A project now has its page — tagline, status, technologies,
description, technical decisions, what was learned, gallery, client,
dates, lead, team, repositories and open calls — and so does a member:
headline, bio, links, technologies and projects. Cards on the landing
link to both.

Addresses follow the API's rule read backwards (core/routes.ts):
pages at the root, records under a section named in their language,
the default language unprefixed. One catch-all route serves them all.
Not a route per shape: Analog registers each loader as an internal
Nitro route, and Nitro's router cannot hold `/:prefix/:slug` beside
`/:prefix/:section/:slug` — the first became unreachable.

The frame around a record (header, footer, lights, 404/503, title,
description, canonical, hreflang) is one component now, PageShell, and
each type only brings its body, all sharing one layout partial.

Also fixes chips that stepped down one row after the first, on the
landing's cards too: @pi/ui's reset gives every `li + li` a top margin,
right for prose and wrong for a row spaced by `gap`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:57:13 +02:00

43 lines
1.4 KiB
TypeScript

import { defineEventHandler, getRequestURL, setResponseStatus } from 'h3';
import { resolveRoute } from '../../app/core/routes';
/**
* 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, plus the addresses records live at.
*/
const KNOWN_PATHS = new Set(['/', '/index.html']);
const PASSTHROUGH_PREFIXES = ['/api/', '/_analog/', '/assets/', '/@', '/src/', '/node_modules/'];
export default defineEventHandler((event) => {
const { pathname } = getRequestURL(event);
if (KNOWN_PATHS.has(pathname)) {
return;
}
// Static files answer for themselves — a missing one is Nitro's 404, not a
// page, and stamping this status on a hit would break the asset.
if (
PASSTHROUGH_PREFIXES.some((prefix) => pathname.startsWith(prefix)) ||
/\.[a-z0-9]+$/i.test(pathname)
) {
return;
}
// Shaped like a record's address (`/sobre-nosotros`, `/proyectos/x`,
// `/en/team/x`). 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
// sets the 404 itself when the API has nothing there. See `ui/page-shell.ts`.
if (resolveRoute(pathname)) {
return;
}
setResponseStatus(event, 404);
});