ipx-web: the page in its own container, behind Caddy (#203)
web/build.mjs builds a static site, web/dist, with the icons and typeface beside the pages; Dockerfile.web serves it with Caddy, which passes /api and /media to ipx on the same origin, so cookies, live updates and Cloudflare Access work as before. ipx serves no page any more: build.rs and every include_str! of the web files are gone, and its image is built without node. What ipx did to the page as it served it is kept another way. Caddy asks ipx before serving a page (forward_auth): /api/page-check sends a browser not signed in to /login and a non-admin away from /admin, and refuses them /admin.js; /api/login-check sends someone Access vouches for past the form; a page asked for with ?token= goes to /api/signin, which sets the cookie. The theme and the admin link, once written into the page per person, are set by web/boot.js in <head> from the theme cookie and the last visit, before the first frame, and /api/me brings the version and the account's theme from before. Caddy strips the Cf-Access-* headers from every peer but TUNNEL_PEER, the address cloudflared's requests come from, which is the boundary trusted_proxies drew; ipx then trusts only Caddy's address. The browser suite runs Caddy in front of its daemon, so it tests what production would run. Not deployed: production's cutover is on #203. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -719,6 +719,12 @@ $('#signout').onclick=async()=>{ await api('/api/logout',{method:'POST'}); locat
|
||||
api('/api/me').then(u=>{
|
||||
S.me=u;
|
||||
$('#who').textContent=u.name+(u.admin?' · admin':'');
|
||||
$('#applogo').title=`iPX ${u.version}`;
|
||||
// Whether the link to Admin shows, now and before the first frame of the next visit.
|
||||
document.documentElement.toggleAttribute('data-admin',!!u.admin);
|
||||
try{ localStorage.setItem('ipx.admin',u.admin?'1':'0'); }catch{}
|
||||
// A browser with no theme of its own takes the one the account kept from before, once.
|
||||
if(u.theme&&!/(^|; )ipx_theme=/.test(document.cookie)) setTheme(u.theme.name,u.theme.mode,true);
|
||||
}).catch(()=>{});
|
||||
$('#feedFilter').oninput=renderFeeds;
|
||||
// The feed list from the keyboard: Enter or Space opens a row, Right and Left open and close a
|
||||
|
||||
@@ -55,9 +55,9 @@ function saveTheme(){
|
||||
systemDark?.addEventListener?.('change', () => { if(theme.mode === 'auto') setTheme(); });
|
||||
(() => {
|
||||
const root = document.documentElement;
|
||||
// Without a cookie, the server sent the theme the account kept from before; this browser takes
|
||||
// it as its own, once, so nobody has to choose again.
|
||||
if(root.dataset.choice) return setTheme(root.dataset.theme, root.dataset.choice, !/(^|; )ipx_theme=/.test(document.cookie));
|
||||
// The head script put this browser's theme from its cookie on the page. Without one, /api/me's
|
||||
// answer brings the account's from before (dialogs.ts).
|
||||
if(root.dataset.choice) return setTheme(root.dataset.theme, root.dataset.choice);
|
||||
// Nothing on the account either. A theme this browser kept in localStorage, from before that,
|
||||
// becomes its cookie, once.
|
||||
let name: string | null = null, mode: string | null = null;
|
||||
|
||||
Reference in New Issue
Block a user