Replaces the blunt registrationEnabled toggle with per-invite access.
Invites are tokenized, single-use, optionally locked to an email, can
grant user or admin role, and expire (default 7d, max 90d).
- Invite model with tokenHash (bcrypt), email, role, expiresAt,
usedAt/usedByUserId.
- inviteService: create, list, revoke, findInviteByToken, consumeInvite.
Token is shown exactly once at creation.
- /admin/invites page: list with status (Active/Used/Expired), generate
with email lock + role + custom expiry, copy one-shot URL, revoke.
- /register?invite=TOKEN: accepts invite even when registrationEnabled
is false; shows a banner; enforces email lock; applies the invite's
role on creation; consumes the invite on success.
- Linked from the admin navbar.