Harden the server and add a Links page
Security headers, cross-site write refusal, atomic JSON writes, admin backup download, /healthz and Docker health check, OnlyOffice requires a JWT secret, npm test, CI audit, arm64 hub image. New Links tab for personal bookmarks, saved with per-person settings. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b0d3aec811
commit
1f8c9493b7
16 files changed
+147
-25
No files matched your search
@@ -1,6 +1,7 @@
|
||||
// Calendar agenda from private ICS feeds (Google "secret address", Outlook/iCloud published calendars).
|
||||
// Feeds are fetched server-side, cached briefly, parsed, and recurring events are expanded for the requested window.
|
||||
const fs = require('fs');
|
||||
const atomicWrite = require('./atomic');
|
||||
const path = require('path');
|
||||
const dns = require('dns').promises;
|
||||
const net = require('net');
|
||||
@@ -12,7 +13,7 @@ const users = require('./users');
|
||||
const here = f => path.join(users.dir(), f); // each person's own files
|
||||
const FILE_NAME = 'calendars.json';
|
||||
const load = () => { try { return JSON.parse(fs.readFileSync(here(FILE_NAME), 'utf8')); } catch { return []; } };
|
||||
const save = v => fs.writeFileSync(here(FILE_NAME), JSON.stringify(v, null, 2), { mode: 0o600 });
|
||||
const save = v => atomicWrite(here(FILE_NAME), JSON.stringify(v, null, 2), { mode: 0o600 });
|
||||
|
||||
// ---- Fetching, with SSRF protection: https only, and never to loopback/private/link-local addresses.
|
||||
const isPrivate = ip => {
|
||||
@@ -224,7 +225,7 @@ function expand(events, win) { // win: { from, to (ms), fd, td (local YYYY-MM-DD
|
||||
// ---- The NE Hub calendar: events created in the app, stored here, and published as a subscribe feed.
|
||||
const EV_FILE = 'events.json', FEED_FILE = 'feed.json';
|
||||
const readJ = (f, d) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return d; } };
|
||||
const writeJ = (f, v) => fs.writeFileSync(here(f), JSON.stringify(v, null, 2), { mode: 0o600 });
|
||||
const writeJ = (f, v) => atomicWrite(here(f), JSON.stringify(v, null, 2), { mode: 0o600 });
|
||||
const loadLocal = () => readJ(here(EV_FILE), []);
|
||||
const LOCAL = { id: 'local', name: 'NE Hub', color: '#4f7cff' };
|
||||
function feedToken(reset) {
|
||||
|
||||
Reference in new issue
Block a user