the preview iframe was rendering Officer inside Officer

Two mistakes, and the second would have bitten immediately after the first was fixed.

The iframe pointed at /fofos-admin/preview/layout. But /fofos-admin is a FRONTEND route — the
single-page app owns it, and anything under it no router claims is answered with the app's own
index.html. So the iframe was served Officer. The plugin's API is mounted at /api/fofos-admin, which
every other call already knew: useClient defaults its base to /api, and thumbnail.ts spells the full
path out for <img> tags that cannot use it.

And an iframe cannot send an Authorization header. Neither can the stylesheet, the fonts or the images
the page goes on to request — those are all Officer requests now, so all of them need ?token=, the
same reason every <img> in this plugin carries one. The token comes off the iframe's own query string
and is propagated into every URL the rewrite touches, with the separator decided per URL because many
already carry the site's ?v= stamp.

It is stripped before the request reaches the site. The token is Officer's and the site has never
heard of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 02:01:18 +01:00
co-authored by Claude Opus 5
parent b56c8375c3
commit 6f8dc83984
3 changed files with 77 additions and 30 deletions
+2 -1
View File
@@ -6,6 +6,7 @@ import { useClient } from 'hooks/useClient';
import { readableError } from '../errors';
import { useFlash } from '../useFlash';
import { useSaveTarget } from '../SaveTarget';
import { previewUrl } from '../thumbnail';
/**
* The shell every page renders inside: the menu, the footer, the colours and the typefaces.
@@ -164,7 +165,7 @@ export const LayoutPanel = () => {
<section className="flex flex-col gap-2">
<div className="overflow-hidden rounded-lg border border-border bg-white">
<iframe
src="/fofos-admin/preview/layout"
src={previewUrl('layout')}
title="The site's layout"
className="h-[420px] w-full border-0"
/* Nothing is typed into it and nothing is submitted from it. It is a window. */