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>
51 lines
2.5 KiB
TypeScript
51 lines
2.5 KiB
TypeScript
import { getHeaders } from 'hooks/useClient';
|
|
|
|
/**
|
|
* A thumbnail URL for something under `originals/`, authenticated by `?token=`.
|
|
*
|
|
* ── Why the token is in the query ──
|
|
*
|
|
* An `<img>` cannot send an Authorization header. `user-middleware` and the permission gate both accept
|
|
* the token in the query string for exactly this, and the platform's own file browser does the same.
|
|
* Built without it first, and every picture 401s while the layout looks perfectly correct.
|
|
*
|
|
* ── Why this is a module ──
|
|
*
|
|
* It was copied into GalleriesPanel and again into RecordsPanel, and a third copy was about to go into
|
|
* MediaField. Three copies of a URL shape is three places to be wrong about the rung when the ladder
|
|
* changes — which it did, four times in a day.
|
|
*/
|
|
export const thumbUrl = (path: string, width: 256 | 320 | 480 | 640 | 960 | 1280 = 320): string => {
|
|
const token = getHeaders()['Authorization']?.replace('Bearer ', '') ?? '';
|
|
const encoded = path.split('/').map(encodeURIComponent).join('/');
|
|
return `/api/fofos-admin/derived/${encoded}/${width}.webp?token=${encodeURIComponent(token)}`;
|
|
};
|
|
|
|
/**
|
|
* A video's poster, for the same reason.
|
|
*
|
|
* A video has no width rungs — `poster-448.webp` and friends are cut from its still — so a slot showing
|
|
* one asks for the poster rather than a width that does not exist.
|
|
*/
|
|
export const posterUrl = (path: string): string => {
|
|
const token = getHeaders()['Authorization']?.replace('Bearer ', '') ?? '';
|
|
const encoded = path.split('/').map(encodeURIComponent).join('/');
|
|
return `/api/fofos-admin/derived/${encoded}/poster-512.webp?token=${encodeURIComponent(token)}`;
|
|
};
|
|
|
|
/**
|
|
* An ORIGINAL, rendered at a width, for something drawn at size.
|
|
*
|
|
* The derived ladder is right for a row thumbnail and wrong for a framing slot: that is most of a panel
|
|
* wide and can be zoomed on top of it, so a rung chosen in advance is a rung that is wrong. This asks
|
|
* for the width the slot actually draws, from the file that has every pixel.
|
|
*
|
|
* `width` is a hint the server clamps to 256..4096. Ask for what will be drawn — twice it, on a screen
|
|
* that is worth it — rather than a round number.
|
|
*/
|
|
export const originalUrl = (path: string, width: number): string => {
|
|
const token = getHeaders()['Authorization']?.replace('Bearer ', '') ?? '';
|
|
const encoded = path.split('/').map(encodeURIComponent).join('/');
|
|
return `/api/fofos-admin/original/${encoded}?width=${Math.round(width)}&token=${encodeURIComponent(token)}`;
|
|
};
|