Changeset 72ec6a4 in Klonkt for src/routes/guardian.js
- Timestamp:
- 07/31/2026 06:55:59 PM (6 weeks ago)
- Branches:
- main
- Children:
- 17546af
- Parents:
- bfa6fa1
- File:
-
- 1 edited
-
src/routes/guardian.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/guardian.js
rbfa6fa1 r72ec6a4 10 10 */ 11 11 import express from 'express'; 12 import crypto from 'crypto';13 import bcrypt from 'bcryptjs';14 12 import path from 'path'; 15 13 import { fileURLToPath } from 'url'; … … 595 593 }); 596 594 597 // ── Losse guardians (Guardian 2): uitnodigen en aansluiten ─────────────── 598 // De familie nodigt oma uit; zij kiest naam + wachtwoord en heeft daarmee een 599 // guardian-only account: user + minimale site (guardian_only=1). Alles wat al 600 // per slug werkt (actor, inbox, offers, push, deze PWA) werkt dan meteen. 601 602 router.post('/invite', requireAuth, (req, res) => { 603 const token = crypto.randomBytes(16).toString('base64url'); 604 db.prepare('INSERT INTO ap_guardian_invites (token, created_by) VALUES (?,?)') 605 .run(token, req.session.user.id); 606 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 607 const url = `${base}/guardian/join/${token}`; 608 res.send(`<!doctype html><meta charset="utf-8"><body style="font-family:sans-serif;max-width:480px;margin:40px auto"> 609 <h2>Invite a guardian</h2> 610 <p>Share this link. It lets one person create a guardian account here:</p> 611 <p><a href="${url}">${url}</a></p> 612 <p><a href="/guardian">Back</a></p></body>`); 613 }); 614 615 function joinForm(token, error) { 616 return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> 617 <body style="font-family:sans-serif;max-width:420px;margin:40px auto"> 618 <h2>Become a guardian</h2> 619 <p>Watch over someone you care about. Pick a name and a password; that is all.</p> 620 ${error ? `<p style="color:#b00">${error}</p>` : ''} 621 <form method="post" action="/guardian/join/${token}"> 622 <p><input name="name" placeholder="your name (grandma)" required pattern="[a-z0-9_-]{1,32}" 623 style="width:100%;padding:10px" autocapitalize="none"></p> 624 <p><input name="password" type="password" placeholder="password" required minlength="8" 625 style="width:100%;padding:10px"></p> 626 <p><button style="width:100%;padding:12px">Create my guardian account</button></p> 627 </form></body>`; 628 } 629 630 router.get('/join/:token', (req, res) => { 631 const inv = db.prepare('SELECT * FROM ap_guardian_invites WHERE token = ? AND used_at IS NULL') 632 .get(req.params.token); 633 if (!inv) return res.status(404).send('This invite is no longer valid.'); 634 res.send(joinForm(req.params.token)); 635 }); 636 637 router.post('/join/:token', express.urlencoded({ extended: false }), (req, res) => { 638 const inv = db.prepare('SELECT * FROM ap_guardian_invites WHERE token = ? AND used_at IS NULL') 639 .get(req.params.token); 640 if (!inv) return res.status(404).send('This invite is no longer valid.'); 641 const name = String(req.body.name || '').trim().toLowerCase(); 642 const password = String(req.body.password || ''); 643 if (!/^[a-z0-9_-]{1,32}$/.test(name)) return res.status(400).send(joinForm(req.params.token, 'Only lowercase letters, digits, - and _.')); 644 if (password.length < 8) return res.status(400).send(joinForm(req.params.token, 'Password: at least 8 characters.')); 645 if (db.prepare('SELECT 1 FROM sites WHERE slug = ?').get(name) || db.prepare('SELECT 1 FROM users WHERE username = ?').get(name)) { 646 return res.status(409).send(joinForm(req.params.token, 'That name is taken, pick another.')); 647 } 648 const userId = crypto.randomUUID(); 649 db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)') 650 .run(userId, name, `${name}@guardian.invalid`, bcrypt.hashSync(password, 10), 'member'); 651 db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary, guardian_only) VALUES (?,?,?,?,0,1)') 652 .run(crypto.randomUUID(), name, name, userId); 653 db.prepare('UPDATE ap_guardian_invites SET used_by = ?, used_at = CURRENT_TIMESTAMP WHERE token = ?') 654 .run(userId, req.params.token); 655 req.session.user = { id: userId, username: name, role: 'member' }; 656 res.redirect('/guardian'); 657 }); 595 // Losse guardian-accounts (guardian-lite: /invite + /join, user + site met 596 // guardian_only=1) zijn verwijderd op 31-7-2026. Een instance is een eigenaar; 597 // zo'n account was de laatste multi-user-rest en zette bovendien andermans 598 // wachtwoordhash, sessie en PRIVATE actor-sleutel in jouw database, wat een 599 // verhuizing (shaer-qw6q) onmogelijk netjes maakte. Een guardian hoort een 600 // eigen Klonkt te hebben; de adoptie loopt dan gewoon over de federatie. 658 601 659 602 export default router;
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)