## Multi-Administrator System
- Add role-based access control (RBAC) with predefined roles (Super Admin, Admin, Editor, Viewer)
- Add granular permissions system for all admin operations
- Add admin user management page with invite functionality
- Add email invitation system for new administrators
- Add permission middleware protecting all admin routes
- Add PermissionGate component for frontend permission checks
- Track event creator (created_by) for audit purposes
## Backup & Restore Fixes
- Fix S3 backup: endpoint URL handling, manifest loading, field name compatibility
- Fix S3 restore: add list-backups endpoint, transform S3 config from frontend format
- Fix PostgreSQL compatibility: add .returning('id') for insert operations
- Fix disk space check: use df command, handle unknown space gracefully
- Fix dry-run validation to not block on warnings
- Fix req.user → req.admin in restore routes
## Database Migrations
- 054: Add roles table with predefined roles
- 055: Add permissions table
- 056: Add role_permissions junction table
- 057: Add role_id to admin_users
- 058: Add admin_invitations table
- 059: Add admin email templates
- 060: Add created_by to events table
## Other Improvements
- Update .gitignore to exclude planning docs and local backup directory
- Remove SQLite database file from tracking
- Add i18n translations for user management (EN/DE)
76 lines
2.2 KiB
JavaScript
76 lines
2.2 KiB
JavaScript
/**
|
|
* Accept Invitation Routes (Public)
|
|
* Handles invitation token validation and account creation
|
|
*/
|
|
|
|
const express = require('express');
|
|
const { body, param } = require('express-validator');
|
|
const { handleAsync, validateRequest, successResponse } = require('../utils/routeHelpers');
|
|
const { validatePasswordStrength } = require('../utils/passwordGenerator');
|
|
const userManagementService = require('../services/userManagementService');
|
|
const router = express.Router();
|
|
|
|
/**
|
|
* GET /:token
|
|
* Validate invitation token
|
|
* Public endpoint - no auth required
|
|
*/
|
|
router.get('/:token', [
|
|
param('token').isLength({ min: 64, max: 64 }).withMessage('Invalid invitation token')
|
|
], handleAsync(async (req, res) => {
|
|
validateRequest(req);
|
|
|
|
const invitation = await userManagementService.validateInvitationToken(req.params.token);
|
|
|
|
if (!invitation) {
|
|
return res.status(404).json({ error: 'Invalid or expired invitation' });
|
|
}
|
|
|
|
res.json({
|
|
valid: true,
|
|
email: invitation.email,
|
|
role: invitation.role_name,
|
|
expiresAt: invitation.expires_at
|
|
});
|
|
}));
|
|
|
|
/**
|
|
* POST /:token
|
|
* Accept invitation and create account
|
|
* Public endpoint - no auth required
|
|
*/
|
|
router.post('/:token', [
|
|
param('token').isLength({ min: 64, max: 64 }).withMessage('Invalid invitation token'),
|
|
body('username')
|
|
.trim()
|
|
.isLength({ min: 3, max: 50 })
|
|
.withMessage('Username must be 3-50 characters')
|
|
.matches(/^[a-zA-Z0-9_-]+$/)
|
|
.withMessage('Username can only contain letters, numbers, underscores, and hyphens'),
|
|
body('password')
|
|
.isLength({ min: 12 })
|
|
.withMessage('Password must be at least 12 characters')
|
|
.custom((value) => {
|
|
const validation = validatePasswordStrength(value);
|
|
if (!validation.isValid) {
|
|
throw new Error(validation.messages.join(', '));
|
|
}
|
|
return true;
|
|
})
|
|
], handleAsync(async (req, res) => {
|
|
validateRequest(req);
|
|
|
|
const result = await userManagementService.acceptInvitation({
|
|
token: req.params.token,
|
|
username: req.body.username,
|
|
password: req.body.password
|
|
});
|
|
|
|
successResponse(res, {
|
|
message: 'Account created successfully. You can now log in.',
|
|
email: result.email
|
|
}, 201);
|
|
}));
|
|
|
|
module.exports = router;
|