Fix Store tab stuck on Loading; adjustable idle time for family devices; videos count as in use
build-container / build (push) Successful in 3m35s
build-container / build (push) Successful in 3m35s
- A bare #/store now opens at #/store/books so the Book store page draws - Admins choose how long a family device waits before returning to the board (1 to 30 minutes) - Any playing video or audio keeps a family device signed in - Test updated Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c6b1a8458
commit
aac6f6efc2
5 files changed
+18
-7
No files matched your search
@@ -24,6 +24,10 @@ function checkPin(uid, pin) {
|
||||
if (ok) { if (r.fails) { r.fails = 0; savePins(); } return true; }
|
||||
r.fails = (r.fails || 0) + 1; if (r.fails >= MAX_FAILS) { r.fails = 0; r.until = Date.now() + LOCK_MS; } savePins(); return false;
|
||||
}
|
||||
// how long a family device sits idle before it goes back to the board (minutes; a signed-in session on a family device lasts 30)
|
||||
const SET_FILE = () => path.join(process.env.DATA_DIR || path.join(__dirname, 'data'), 'board-settings.json');
|
||||
const IDLE_CHOICES = [1, 2, 3, 5, 10, 15, 30];
|
||||
const idleMin = () => { try { const n = +JSON.parse(fs.readFileSync(SET_FILE(), 'utf8')).idleMin; return IDLE_CHOICES.includes(n) ? n : 5; } catch { return 5; } };
|
||||
const ymdOk = s => /^\d{4}-\d{2}-\d{2}$/.test(s || '');
|
||||
const isKid = u => !!(u.kid && u.kid.on);
|
||||
const palette = ['#e0742b', '#2f9e6e', '#4f7cff', '#8a5cf0', '#d9453a', '#2aa6a0', '#e5a00d'];
|
||||
@@ -76,11 +80,15 @@ async function handle(req, res) {
|
||||
identify(t, b.pin, req);
|
||||
auth.startSession(req, res, t); audit.log('family device: picked a profile', { who: t.name }, req); return json(res, 200, { ok: true });
|
||||
}
|
||||
if (p === '/api/device' && req.method === 'GET') { if (!me) throw fail(401, 'Not signed in'); return json(res, 200, { device: !!dev, pin: hasPin(me.id), devices: me.role === 'admin' ? devices.load() : undefined }); }
|
||||
if (p === '/api/device' && req.method === 'GET') { if (!me) throw fail(401, 'Not signed in'); return json(res, 200, { device: !!dev, idleMin: idleMin(), idleChoices: IDLE_CHOICES, pin: hasPin(me.id), devices: me.role === 'admin' ? devices.load() : undefined }); }
|
||||
if (p === '/api/device/pin' && (req.method === 'POST' || req.method === 'DELETE')) {
|
||||
if (!me) throw fail(401, 'Not signed in'); if (req.method === 'DELETE') { delete db()[me.id]; savePins(); return json(res, 200, { pin: false }); }
|
||||
const b = JSON.parse(await readBody(req, 5e3) || '{}'); setPin(me.id, b.pin); return json(res, 200, { pin: true });
|
||||
}
|
||||
if (p === '/api/device/idle' && req.method === 'POST') { // an admin sets how long a family device waits before going back to the board
|
||||
if (!me || me.role !== 'admin') throw fail(403, 'Only an admin can do this'); const b = JSON.parse(await readBody(req, 2e3) || '{}'), n = Math.round(+b.minutes);
|
||||
if (!IDLE_CHOICES.includes(n)) throw fail(400, 'Choose between 1 and 30 minutes'); fs.mkdirSync(path.dirname(SET_FILE()), { recursive: true }); atomicWrite(SET_FILE(), JSON.stringify({ idleMin: n }), { mode: 0o600 }); audit.log('family device idle time set', { who: me.name, detail: n + ' min' }, req); return json(res, 200, { idleMin: n });
|
||||
}
|
||||
if (p === '/api/device/enable' && req.method === 'POST') { // an admin makes this screen a family device (and it goes back to the board)
|
||||
if (!me || me.role !== 'admin') throw fail(403, 'Only an admin can do this'); const b = JSON.parse(await readBody(req, 5e3) || '{}'); auth.enrollDevice(req, res, b.name, me.name); audit.log('family device added', { who: me.name, detail: String(b.name || '').slice(0, 40) }, req);
|
||||
res.setHeader('Set-Cookie', [].concat(res.getHeader('Set-Cookie') || [], 'hub_session=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0')); return json(res, 200, { ok: true });
|
||||
|
||||
Reference in new issue
Block a user