Page Builder fixes from Marty's test: our own CSP frame-src blocked framing our member pages (preview showed broken) — add 'self'; /p/<id> now sends explicit permissive framing headers so member pages always render embedded; button label tracks whether a page exists; live progress line with step narration + elapsed seconds; stale preview hidden during rebuild and cache-busted after

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-28 08:56:32 -05:00
parent be2d2719e5
commit 6c73b5d9c5
3 changed files with 48 additions and 6 deletions
+7 -2
View File
@@ -464,7 +464,9 @@ async function handleTranslate(req,res){
return json(res,200,{t:out});
}
const CSP_BASE="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self'; frame-src https://www.youtube-nocookie.com";
// frame-src includes 'self' so our own pages (the Page Builder preview framing
// a member's /p/<id> page) are not blocked by our own policy.
const CSP_BASE="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self'; frame-src 'self' https://www.youtube-nocookie.com";
function securityHeaders(extra={}) {
// Public pages must render inside safelist / traffic-exchange iframes, so framing stays open here; admin.html re-locks it via ADMIN_FRAME_HEADERS.
return {
@@ -670,7 +672,10 @@ async function handleApi(req,res,pathname){
const rec=suitePages.load(pid);
if(!rec||!rec.copy)return json(res,404,{error:'No page here yet.'});
const html=suitePages.render(rec);
res.writeHead(200,{'Content-Type':'text/html; charset=utf-8','Cache-Control':'public, max-age=120'});
// Member pages must ALWAYS be viewable inside a frame — the builder previews
// them, and members share them into contexts that embed. Never send
// X-Frame-Options here, and keep frame-ancestors open.
res.writeHead(200,securityHeaders({'Content-Type':'text/html; charset=utf-8','Cache-Control':'public, max-age=120'}));
return res.end(html);
}
if(req.method==='GET'&&pathname==='/api/public/suite-page'){