✨ Added frontend admin toolbar (#28058)
Staff members viewing the frontend of their Ghost site now see a floating toolbar with quick links back to the editor, settings, and analytics for the current page. The toolbar is staff-only and invisible to normal visitors — public pages remain fully cacheable. **Activation model** Admin "view site" links append `?admin=1`. Frontend middleware creates a short-lived HMAC-signed marker cookie on the site domain and redirects to the clean URL. The cookie contains no session data or PII — it only signals that staff tooling should load. On subsequent requests the middleware validates the marker and sets a response local that `ghost_head` checks before emitting the script tag. The browser bundle independently verifies the real staff session through the existing `/ghost/auth-frame/` bridge before rendering anything. This two-layer approach means the server never embeds staff-specific data in HTML, so CDN and theme caching are unaffected. **Suppression inside Admin** Admin's "view site" iframe passes `?admin=1&admin_toolbar=0`. The middleware seeds the marker cookie (so the toolbar works on normal frontend visits) but suppresses injection for that response. Suppression triggers on the explicit query parameter and on `Sec-Fetch-Dest: iframe`. Theme and announcement-bar previews, which use `fetch()` rather than iframes, append `?admin_toolbar=0` to their request URLs. The toolbar script also checks the parameter client-side as a safety net. **Package boundary** The toolbar lives in `apps/admin-toolbar` as a self-contained package with its own source, Vite build, tests, and UMD artifact. It uses Preact (~3 KB) instead of React (~40 KB) since it is a lightweight public-facing widget that only needs basic rendering — a rationale that could apply to other small public scripts where bundle size matters more than ecosystem compatibility. It renders inside Shadow DOM so theme CSS cannot affect it. In production the script is served from jsDelivr via the `adminToolbar` config in `defaults.json`, following the same CDN pattern as portal, comments-ui, search, and announcement-bar. In development the Docker setup proxies through Caddy to a local Vite preview server. --- Co-authored-by: Hannah Wolfe <github.erisds@gmail.com>
J
John O'Nolan committed
8bdc9fe04792a44177b6853ff8d51341aec20dd3
Parent: f9917cb
Committed by GitHub <noreply@github.com>
on 5/28/2026, 8:53:52 AM