Always revalidate the app's files, tell open pages when a newer version exists, and log why a session is refused
build-container / build (push) Successful in 1m58s
build-container / build (push) Successful in 1m58s
An open page kept running the old version after a deploy (the single page never reloads), so the new People screens did not appear. Static files are now sent with no-cache + ETag (304 when unchanged), icons are cached for a week, /api/config reports a build stamp, and the app shows "A new version is available - Reload" when it comes back into view or every 10 minutes. The service worker cache name is bumped. The server also logs, at most once a minute per reason, why it refused a session cookie (unknown account, turned off, signed out everywhere, bad signature, expired) without printing the cookie. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2d17ee9939
commit
12a1e50a0e
6 files changed
+57
-9
No files matched your search
@@ -2,6 +2,7 @@
|
||||
// (Seafile sends no CORS headers, so the browser talks to /sf/* instead).
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { Readable } = require('stream');
|
||||
|
||||
@@ -75,14 +76,21 @@ function serveStatic(req, res) {
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) { res.writeHead(404); return res.end('Not found'); }
|
||||
const headers = { 'content-type': TYPES[path.extname(file)] || 'application/octet-stream' };
|
||||
if (p === '/sw.js') headers['cache-control'] = 'no-cache';
|
||||
if (p.startsWith('/icons/')) headers['cache-control'] = 'public, max-age=604800'; // icons and splash screens rarely change
|
||||
else { // everything else: the browser may keep a copy but must check with us first, so a new version is never missed
|
||||
headers['cache-control'] = 'no-cache';
|
||||
headers.etag = `W/"${data.length}-${crypto.createHash('sha1').update(data).digest('hex').slice(0, 16)}"`;
|
||||
if (req.headers['if-none-match'] === headers.etag) { res.writeHead(304, { etag: headers.etag, 'cache-control': 'no-cache' }); return res.end(); }
|
||||
}
|
||||
res.writeHead(200, headers);
|
||||
res.end(data);
|
||||
});
|
||||
}
|
||||
|
||||
// Changes whenever the app's files change, so an open page can tell that a newer version is available.
|
||||
const BUILD = (() => { try { return crypto.createHash('sha1').update(['app.js', 'index.html', 'style.css'].map(f => fs.readFileSync(path.join(PUBLIC, f))).join('')).digest('hex').slice(0, 12); } catch { return String(Date.now()); } })();
|
||||
|
||||
// ---- OnlyOffice in-app editing (JWT-signed editor config + save-back callback to Seafile)
|
||||
const crypto = require('crypto');
|
||||
const JWT_SECRET = process.env.ONLYOFFICE_JWT_SECRET || '';
|
||||
const PUBLIC_URL = (process.env.PUBLIC_URL || '').replace(/\/$/, '');
|
||||
const b64u = b => Buffer.from(b).toString('base64url');
|
||||
@@ -404,7 +412,7 @@ async function handleRequest(req, res) {
|
||||
const c = t => users.can(user, t);
|
||||
res.writeHead(200, { 'content-type': 'application/json' });
|
||||
return res.end(JSON.stringify({ seafile: c('files') || c('office') ? SEAFILE_URL : '', onlyoffice: c('office') ? ONLYOFFICE_URL : '', audiobooks: c('audio') ? AUDIOBOOKS_URL : '', verse: c('verse') ? VERSE_URL : '',
|
||||
podcast: c('podcast') ? PODCAST_URL : '', editor: c('office') || c('files'), jwt: !!JWT_SECRET }));
|
||||
podcast: c('podcast') ? PODCAST_URL : '', editor: c('office') || c('files'), jwt: !!JWT_SECRET, build: BUILD }));
|
||||
}
|
||||
if (req.method === 'POST' && req.url === '/api/oo/config') return ooConfig(req, res);
|
||||
if (pathname === '/api/ddg') return ddgAnswer(req, res);
|
||||
|
||||
Reference in new issue
Block a user