// Who may create an account. The administrator picks one of three settings: // open anyone who can reach the server (the original behaviour) // invite only someone with an unused invite code from the administrator // closed nobody new // The very first account can always be made, so a fresh install can't lock itself out. import { randomBytes } from 'crypto'; export const SIGNUP_MODES = ['open', 'invite', 'closed']; export const isSignupMode = (m) => SIGNUP_MODES.includes(m); // -> { ok: true } or { ok: false, status, error }. `inviteOk` says whether the code given is a valid unused one. export function checkSignup({ mode, userCount, inviteOk }) { if (userCount === 0 || mode === 'open') return { ok: true }; if (mode === 'closed') return { ok: false, status: 403, error: 'Sign-up is closed. Ask the administrator for an account.' }; if (!inviteOk) return { ok: false, status: 403, error: 'Sign-up needs an invite code from the administrator.' }; return { ok: true }; } // An easy-to-read invite code (no look-alike characters), e.g. K7QP-4MXD-9TRW. const ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789'; export function generateInviteCode() { const chars = Array.from(randomBytes(12), (b) => ALPHABET[b % ALPHABET.length]).join(''); return `${chars.slice(0, 4)}-${chars.slice(4, 8)}-${chars.slice(8, 12)}`; } export const normalizeInviteCode = (code) => String(code ?? '').toUpperCase().replace(/[^A-Z0-9]/g, '').replace(/^(.{4})(.{4})(.{4})$/, '$1-$2-$3');