Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d606e11de4 | ||
|
|
95c9127e8e |
No files matched your search
@@ -4,7 +4,7 @@ const users = require('./users');
|
|||||||
const auth = require('./auth');
|
const auth = require('./auth');
|
||||||
const audit = require('./audit');
|
const audit = require('./audit');
|
||||||
|
|
||||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
const { readBody } = require('./body');
|
||||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||||
const row = u => ({ ...users.publicView(u), tabsChosen: u.tabs || [], passkeys: auth.passkeyCount(u.id) });
|
const row = u => ({ ...users.publicView(u), tabsChosen: u.tabs || [], passkeys: auth.passkeyCount(u.id) });
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ const rp = req => {
|
|||||||
};
|
};
|
||||||
const equalStr = (a, b) => { const x = Buffer.from(String(a)), y = Buffer.from(String(b)); return x.length === y.length && crypto.timingSafeEqual(x, y); };
|
const equalStr = (a, b) => { const x = Buffer.from(String(a)), y = Buffer.from(String(b)); return x.length === y.length && crypto.timingSafeEqual(x, y); };
|
||||||
|
|
||||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
const { readBody } = require('./body');
|
||||||
const send = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json', ...(res.getHeader('Set-Cookie') ? { 'Set-Cookie': res.getHeader('Set-Cookie') } : {}) }); res.end(JSON.stringify(obj)); };
|
const send = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json', ...(res.getHeader('Set-Cookie') ? { 'Set-Cookie': res.getHeader('Set-Cookie') } : {}) }); res.end(JSON.stringify(obj)); };
|
||||||
const credsOf = uid => store.creds.filter(c => c.uid === uid);
|
const credsOf = uid => store.creds.filter(c => c.uid === uid);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
'use strict';
|
||||||
|
// Reads a request body as text, and refuses one that is too big, so a huge upload cannot use up the server's memory.
|
||||||
|
function readBody(req, max = 2e6) {
|
||||||
|
return new Promise((ok, no) => {
|
||||||
|
const c = []; let n = 0, done = false;
|
||||||
|
req.on('data', d => { if (done) return; n += d.length; if (n > max) { done = true; c.length = 0; const e = new Error('That is too large'); e.status = 413; no(e); return; } c.push(d); });
|
||||||
|
req.on('end', () => { if (!done) ok(Buffer.concat(c).toString()); });
|
||||||
|
req.on('error', e => { if (!done) no(e); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
module.exports = { readBody };
|
||||||
@@ -306,7 +306,7 @@ function feed(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- HTTP API (called by server.js, behind the passkey gate)
|
// ---- HTTP API (called by server.js, behind the passkey gate)
|
||||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
const readBody = req => require('./body').readBody(req, 8e6);
|
||||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||||
|
|
||||||
/** Every event in the window from all calendars (ICS links, iCloud, NE Hub, shared): { events, errors } */
|
/** Every event in the window from all calendars (ICS links, iCloud, NE Hub, shared): { events, errors } */
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"test": "for t in smoke tasks push users cal plex player lyrics signal mail mail-imap files podcast abapi abdirect books store librivox radio vbv savefiles bible lrclib visualizer; do node tools/$t-test.js || exit 1; done",
|
"test": "for t in smoke tasks push users cal plex player lyrics signal mail mail-imap files podcast abapi abdirect books store librivox radio vbv savefiles bible lrclib visualizer body; do node tools/$t-test.js || exit 1; done",
|
||||||
"audit": "npm audit --omit=dev --audit-level=high"
|
"audit": "npm audit --omit=dev --audit-level=high"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ const sessions = new Map(); // callback id -> { token, repo, path, exp }; keeps
|
|||||||
setInterval(() => { for (const [k, v] of sessions) if (v.exp < Date.now()) sessions.delete(k); }, 3600e3).unref();
|
setInterval(() => { for (const [k, v] of sessions) if (v.exp < Date.now()) sessions.delete(k); }, 3600e3).unref();
|
||||||
const DOC_TYPE = { word: 'doc docx odt rtf txt', cell: 'xls xlsx ods csv', slide: 'ppt pptx odp' };
|
const DOC_TYPE = { word: 'doc docx odt rtf txt', cell: 'xls xlsx ods csv', slide: 'ppt pptx odp' };
|
||||||
const docType = e => Object.keys(DOC_TYPE).find(k => DOC_TYPE[k].split(' ').includes(e));
|
const docType = e => Object.keys(DOC_TYPE).find(k => DOC_TYPE[k].split(' ').includes(e));
|
||||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
const { readBody } = require('./body');
|
||||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||||
const sfGet = async (p, token) => {
|
const sfGet = async (p, token) => {
|
||||||
const r = await fetch(SEAFILE_URL + p, { headers: { Authorization: 'Token ' + token } });
|
const r = await fetch(SEAFILE_URL + p, { headers: { Authorization: 'Token ' + token } });
|
||||||
@@ -661,9 +661,13 @@ async function handleRequest(req, res) {
|
|||||||
http.createServer(async (req, res) => {
|
http.createServer(async (req, res) => {
|
||||||
try { await handleRequest(req, res); }
|
try { await handleRequest(req, res); }
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
if (e && e.status === 413) { if (!res.headersSent) { res.writeHead(413, { 'content-type': 'application/json', connection: 'close' }); res.end('{"error":"That is too large"}'); } return; }
|
||||||
console.error('[error]', req.method, req.url.split('?')[0], e && e.stack || e);
|
console.error('[error]', req.method, req.url.split('?')[0], e && e.stack || e);
|
||||||
if (!res.headersSent) { res.writeHead(500, { 'content-type': 'application/json' }); res.end(JSON.stringify({ error: 'Server error: ' + (e && e.message || 'unknown') })); } else res.destroy();
|
if (!res.headersSent) { res.writeHead(500, { 'content-type': 'application/json' }); res.end(JSON.stringify({ error: 'Server error: ' + (e && e.message || 'unknown') })); } else res.destroy();
|
||||||
}
|
}
|
||||||
}).listen(PORT, () => console.log(`Hub on :${PORT} (Seafile ${SEAFILE_URL})`));
|
}).listen(PORT, () => console.log(`Hub on :${PORT} (Seafile ${SEAFILE_URL})`));
|
||||||
process.on('unhandledRejection', e => console.error('[unhandledRejection]', e && e.stack || e));
|
process.on('unhandledRejection', e => console.error('[unhandledRejection]', e && e.stack || e));
|
||||||
process.on('uncaughtException', e => console.error('[uncaughtException]', e && e.stack || e));
|
process.on('uncaughtException', e => console.error('[uncaughtException]', e && e.stack || e));
|
||||||
|
|
||||||
|
// Docker sends SIGTERM on stop: leave at once instead of waiting to be killed.
|
||||||
|
for (const sig of ['SIGTERM', 'SIGINT']) process.on(sig, () => { console.log('[stop] ' + sig); process.exit(0); });
|
||||||
@@ -12,7 +12,7 @@ const users = require('./users');
|
|||||||
const file = () => path.join(users.dir(), 'tasks.json'); // each person has their own
|
const file = () => path.join(users.dir(), 'tasks.json'); // each person has their own
|
||||||
const load = () => { try { return JSON.parse(fs.readFileSync(file(), 'utf8')); } catch { return { lists: [{ id: 'inbox', name: 'Tasks' }], tasks: [] }; } };
|
const load = () => { try { return JSON.parse(fs.readFileSync(file(), 'utf8')); } catch { return { lists: [{ id: 'inbox', name: 'Tasks' }], tasks: [] }; } };
|
||||||
const save = d => atomicWrite(file(), JSON.stringify(d, null, 2), { mode: 0o600 });
|
const save = d => atomicWrite(file(), JSON.stringify(d, null, 2), { mode: 0o600 });
|
||||||
const readBody = req => new Promise((ok, no) => { const c = []; req.on('data', d => c.push(d)); req.on('end', () => ok(Buffer.concat(c).toString())); req.on('error', no); });
|
const { readBody } = require('./body');
|
||||||
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
const json = (res, code, obj) => { res.writeHead(code, { 'content-type': 'application/json' }); res.end(JSON.stringify(obj)); };
|
||||||
const isLocal = id => String(id || '').startsWith('local:');
|
const isLocal = id => String(id || '').startsWith('local:');
|
||||||
const COLORS = ['#4f7cff', '#2f9e6e', '#e0742b', '#d9453a', '#8a5cf0', '#e5a00d'];
|
const COLORS = ['#4f7cff', '#2f9e6e', '#e0742b', '#d9453a', '#8a5cf0', '#e5a00d'];
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
const http = require('http'), assert = require('assert'), { readBody } = require('../body');
|
||||||
|
const s = http.createServer(async (req, res) => { try { const t = await readBody(req, 1000); res.end('ok:' + t.length); } catch (e) { res.writeHead(e.status || 500); res.end(e.message); } });
|
||||||
|
s.listen(0, async () => {
|
||||||
|
const P = s.address().port, post = b => fetch(`http://127.0.0.1:${P}/`, { method: 'POST', body: b }).then(async r => [r.status, await r.text()]);
|
||||||
|
assert.deepStrictEqual(await post('x'.repeat(900)), [200, 'ok:900']);
|
||||||
|
const big = await post('x'.repeat(5000)); assert.strictEqual(big[0], 413);
|
||||||
|
console.log('body test ok'); s.close();
|
||||||
|
});
|
||||||
Reference in new issue
Block a user