feat(invite-system): implement user invite functionality with registration mode control
This commit is contained in:
@@ -112,7 +112,27 @@ const router = Router();
|
||||
// POST /api/auth/register
|
||||
router.post('/register', registerLimiter, async (req, res) => {
|
||||
try {
|
||||
const { username, display_name, email, password } = req.body;
|
||||
const { username, display_name, email, password, invite_token } = req.body;
|
||||
|
||||
// Check registration mode
|
||||
const db = getDb();
|
||||
const regModeSetting = await db.get("SELECT value FROM settings WHERE key = 'registration_mode'");
|
||||
const registrationMode = regModeSetting?.value || 'open';
|
||||
|
||||
let validatedInvite = null;
|
||||
if (registrationMode === 'invite') {
|
||||
if (!invite_token) {
|
||||
return res.status(403).json({ error: 'Registration is currently invite-only. You need an invitation link to register.' });
|
||||
}
|
||||
// Validate the invite token
|
||||
validatedInvite = await db.get(
|
||||
'SELECT * FROM user_invites WHERE token = ? AND used_at IS NULL AND expires_at > CURRENT_TIMESTAMP',
|
||||
[invite_token]
|
||||
);
|
||||
if (!validatedInvite) {
|
||||
return res.status(403).json({ error: 'Invalid or expired invitation link.' });
|
||||
}
|
||||
}
|
||||
|
||||
if (!username || !display_name || !email || !password) {
|
||||
return res.status(400).json({ error: 'All fields are required' });
|
||||
@@ -138,7 +158,6 @@ router.post('/register', registerLimiter, async (req, res) => {
|
||||
return res.status(400).json({ error: `Password must be at least ${MIN_PASSWORD_LENGTH} characters long` });
|
||||
}
|
||||
|
||||
const db = getDb();
|
||||
const existing = await db.get('SELECT id FROM users WHERE email = ?', [email]);
|
||||
if (existing) {
|
||||
return res.status(409).json({ error: 'Email is already in use' });
|
||||
@@ -161,6 +180,14 @@ router.post('/register', registerLimiter, async (req, res) => {
|
||||
[username, display_name, email.toLowerCase(), hash, verificationToken, expires]
|
||||
);
|
||||
|
||||
// Mark invite as used if applicable
|
||||
if (validatedInvite) {
|
||||
const newUser = await db.get('SELECT id FROM users WHERE email = ?', [email.toLowerCase()]);
|
||||
if (newUser) {
|
||||
await db.run('UPDATE user_invites SET used_by = ?, used_at = CURRENT_TIMESTAMP WHERE id = ?', [newUser.id, validatedInvite.id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Build verification URL
|
||||
const baseUrl = process.env.APP_URL || `${req.protocol}://${req.get('host')}`;
|
||||
const verifyUrl = `${baseUrl}/verify-email?token=${verificationToken}`;
|
||||
@@ -189,6 +216,11 @@ router.post('/register', registerLimiter, async (req, res) => {
|
||||
[username, display_name, email.toLowerCase(), hash]
|
||||
);
|
||||
|
||||
// Mark invite as used if applicable
|
||||
if (validatedInvite) {
|
||||
await db.run('UPDATE user_invites SET used_by = ?, used_at = CURRENT_TIMESTAMP WHERE id = ?', [result.lastInsertRowid, validatedInvite.id]);
|
||||
}
|
||||
|
||||
const token = generateToken(result.lastInsertRowid);
|
||||
const user = await db.get('SELECT id, name, display_name, email, role, theme, language, avatar_color, avatar_image, email_verified FROM users WHERE id = ?', [result.lastInsertRowid]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user