PWA for iPhone and iPad: PNG icons, splash screens, manifest; stay signed in for 180 days
build-container / build (push) Successful in 1m56s
build-container / build (push) Successful in 1m56s
- iOS ignores SVG touch icons: add 120/152/167/180 apple-touch-icons, 192/512 and maskable manifest icons, and 25 splash screens (every iPhone size, iPad portrait+landscape incl. 12.9") - Icons and splash screens are public so iOS can fetch them without a session - Manifest: id, scope, categories, shortcuts; iOS standalone polish (no callout, no overscroll) - Sessions last SESSION_DAYS (default 180) and renew daily while in use - tools/make-icons.py regenerates the artwork Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
916882e55d
commit
044589686c
47 files changed
+293
-16
No files matched your search
@@ -166,7 +166,7 @@ const unsigned = v => { const i = String(v || '').lastIndexOf('.'); return i > 0
|
||||
const parseCookies = req => Object.fromEntries((req.headers.cookie || '').split(/; */).filter(Boolean).map(c => { const i = c.indexOf('='); return [c.slice(0, i), decodeURIComponent(c.slice(i + 1))]; }));
|
||||
const setCookies = (req, res, obj, maxAge = 31536000) => {
|
||||
const secure = (req.headers['x-forwarded-proto'] || '') === 'https' ? '; Secure' : '';
|
||||
res.setHeader('Set-Cookie', Object.entries(obj).map(([k, v]) => `${k}=${encodeURIComponent(v)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${v === '' ? 0 : maxAge}${secure}`));
|
||||
for (const [k, v] of Object.entries(obj)) auth.addCookie(res, `${k}=${encodeURIComponent(v)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${v === '' ? 0 : maxAge}${secure}`);
|
||||
};
|
||||
const plexTv = async (path, token, opts = {}) => {
|
||||
const r = await fetch('https://plex.tv' + path, { ...opts, headers: { ...PLEX_HEADERS, ...(token ? { 'X-Plex-Token': token } : {}) } });
|
||||
@@ -289,8 +289,9 @@ http.createServer((req, res) => {
|
||||
if (pathname.startsWith('/cal/feed/')) return cal.feed(req, res); // subscribe feed: protected by its own secret token, not the session
|
||||
// Everything else needs a passkey session, except the sign-in page and its assets,
|
||||
// and OnlyOffice's save callback (which carries its own signed token).
|
||||
if (auth.isAuthed(req)) auth.refresh(req, res);
|
||||
const open = ['/login', '/login.js', '/webauthn.js', '/style.css', '/icon.svg', '/manifest.webmanifest', '/sw.js', '/api/oo/callback'];
|
||||
if (!open.includes(pathname) && !auth.isAuthed(req)) {
|
||||
if (!open.includes(pathname) && !pathname.startsWith('/icons/') && !auth.isAuthed(req)) {
|
||||
if (/^\/(api|sf|plex)\//.test(pathname)) { res.writeHead(401, { 'content-type': 'application/json', 'x-hub-auth': 'required' }); return res.end('{"error":"Sign in required"}'); }
|
||||
res.writeHead(302, { location: '/login' }); return res.end();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user