use the platform's dev proxy instead of a worse copy of it

The preview went through a proxy written into this plugin. The platform already had one —
/api/dev-proxy/:port/* — and it is better in exactly the ways the copy was worst.

Authentication. An iframe navigation cannot carry an Authorization header, and the copy answered that
by putting a token on the URL and on every asset the page then requested. The platform's answer is a
short-lived cookie obtained by an ordinary authenticated request, and its own comments say why: a
token on an iframe URL is a guess about which key the browser stored it under. It had been tried
there and rejected before this plugin repeated it.

Coverage. The copy rewrote URLs with a regex over the markup, which catches only what is written into
the document. The platform injects a script that patches fetch, XMLHttpRequest and WebSocket, so URLs
the page BUILDS at runtime are moved as well — which is most of them in a modern dev server. It also
normalises `..`, forwards HMR, and has nineteen tests behind it.

So the plugin's proxy is gone, previewUrl with it, and the panel does what the platform's Preview
panel does: ask for the session, then mount the frame at the dev port.

No sandbox attribute, for the reason that panel gives. Without allow-same-origin a frame has an opaque
origin and no storage; with allow-same-origin and allow-scripts it can reach up and remove its own
sandbox attribute. Claiming a protection that does not hold is worse than not claiming it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 02:07:18 +01:00
co-authored by Claude Opus 5
parent 6f8dc83984
commit 82862ed340
3 changed files with 82 additions and 154 deletions
-15
View File
@@ -48,18 +48,3 @@ export const originalUrl = (path: string, width: number): string => {
const encoded = path.split('/').map(encodeURIComponent).join('/');
return `/api/fofos-admin/original/${encoded}?width=${Math.round(width)}&token=${encodeURIComponent(token)}`;
};
/**
* The site's own page, proxied through Officer, for the layout preview's iframe.
*
* Here rather than in the panel for the reason this module exists at all: it is a URL shape carrying a
* token, and a second copy of one is a second place to be wrong about it.
*
* `/api/fofos-admin/...` and NOT `/fofos-admin/...`. The second is the single-page app's own route, so
* anything under it that no router claims is answered with Officer's index.html — an iframe pointed
* there renders Officer inside Officer, which is precisely what it did.
*/
export const previewUrl = (path: string): string => {
const token = getHeaders()['Authorization']?.replace('Bearer ', '') ?? '';
return `/api/fofos-admin/preview/${path.replace(/^\/+/, '')}?token=${encodeURIComponent(token)}`;
};