Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Complete rewrite from Express to NestJS with enterprise-grade features: ## Backend Improvements - Migrated from Express to NestJS 11.0.1 with TypeScript - Implemented Prisma ORM 7.3.0 for type-safe database access - Added CASL authorization system replacing role-based guards - Created global exception filters with structured logging - Implemented Auth0 JWT authentication with Passport.js - Added vehicle management with conflict detection - Enhanced event scheduling with driver/vehicle assignment - Comprehensive error handling and logging ## Frontend Improvements - Upgraded to React 19.2.0 with Vite 7.2.4 - Implemented CASL-based permission system - Added AbilityContext for declarative permissions - Created ErrorHandler utility for consistent error messages - Enhanced API client with request/response logging - Added War Room (Command Center) dashboard - Created VIP Schedule view with complete itineraries - Implemented Vehicle Management UI - Added mock data generators for testing (288 events across 20 VIPs) ## New Features - Vehicle fleet management (types, capacity, status tracking) - Complete 3-day Jamboree schedule generation - Individual VIP schedule pages with PDF export (planned) - Real-time War Room dashboard with auto-refresh - Permission-based navigation filtering - First user auto-approval as administrator ## Documentation - Created CASL_AUTHORIZATION.md (comprehensive guide) - Created ERROR_HANDLING.md (error handling patterns) - Updated CLAUDE.md with new architecture - Added migration guides and best practices ## Technical Debt Resolved - Removed custom authentication in favor of Auth0 - Replaced role checks with CASL abilities - Standardized error responses across API - Implemented proper TypeScript typing - Added comprehensive logging Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
534
backend-old-20260125/dist/routes/simpleAuth.js
vendored
Normal file
534
backend-old-20260125/dist/routes/simpleAuth.js
vendored
Normal file
@@ -0,0 +1,534 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireAuth = requireAuth;
|
||||
exports.requireRole = requireRole;
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const simpleAuth_1 = require("../config/simpleAuth");
|
||||
const databaseService_1 = __importDefault(require("../services/databaseService"));
|
||||
const router = express_1.default.Router();
|
||||
// Enhanced logging for production debugging
|
||||
function logAuthEvent(event, details = {}) {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.log(`🔐 [AUTH ${timestamp}] ${event}:`, JSON.stringify(details, null, 2));
|
||||
}
|
||||
// Validate environment variables on startup
|
||||
function validateAuthEnvironment() {
|
||||
const required = ['GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GOOGLE_REDIRECT_URI', 'FRONTEND_URL'];
|
||||
const missing = required.filter(key => !process.env[key]);
|
||||
if (missing.length > 0) {
|
||||
logAuthEvent('ENVIRONMENT_ERROR', { missing_variables: missing });
|
||||
return false;
|
||||
}
|
||||
// Validate URLs
|
||||
const frontendUrl = process.env.FRONTEND_URL;
|
||||
const redirectUri = process.env.GOOGLE_REDIRECT_URI;
|
||||
if (!frontendUrl?.startsWith('http')) {
|
||||
logAuthEvent('ENVIRONMENT_ERROR', { error: 'FRONTEND_URL must start with http/https' });
|
||||
return false;
|
||||
}
|
||||
if (!redirectUri?.startsWith('http')) {
|
||||
logAuthEvent('ENVIRONMENT_ERROR', { error: 'GOOGLE_REDIRECT_URI must start with http/https' });
|
||||
return false;
|
||||
}
|
||||
logAuthEvent('ENVIRONMENT_VALIDATED', {
|
||||
frontend_url: frontendUrl,
|
||||
redirect_uri: redirectUri,
|
||||
client_id_configured: !!process.env.GOOGLE_CLIENT_ID,
|
||||
client_secret_configured: !!process.env.GOOGLE_CLIENT_SECRET
|
||||
});
|
||||
return true;
|
||||
}
|
||||
// Validate environment on module load
|
||||
const isEnvironmentValid = validateAuthEnvironment();
|
||||
// Middleware to check authentication
|
||||
function requireAuth(req, res, next) {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
logAuthEvent('AUTH_FAILED', {
|
||||
reason: 'no_token',
|
||||
ip: req.ip,
|
||||
path: req.path,
|
||||
headers_present: !!req.headers.authorization
|
||||
});
|
||||
return res.status(401).json({ error: 'No token provided' });
|
||||
}
|
||||
const token = authHeader.substring(7);
|
||||
if (!token || token.length < 10) {
|
||||
logAuthEvent('AUTH_FAILED', {
|
||||
reason: 'invalid_token_format',
|
||||
ip: req.ip,
|
||||
path: req.path,
|
||||
token_length: token?.length || 0
|
||||
});
|
||||
return res.status(401).json({ error: 'Invalid token format' });
|
||||
}
|
||||
const user = (0, simpleAuth_1.verifyToken)(token);
|
||||
if (!user) {
|
||||
logAuthEvent('AUTH_FAILED', {
|
||||
reason: 'token_verification_failed',
|
||||
ip: req.ip,
|
||||
path: req.path,
|
||||
token_prefix: token.substring(0, 10) + '...'
|
||||
});
|
||||
return res.status(401).json({ error: 'Invalid or expired token' });
|
||||
}
|
||||
logAuthEvent('AUTH_SUCCESS', {
|
||||
user_id: user.id,
|
||||
user_email: user.email,
|
||||
user_role: user.role,
|
||||
ip: req.ip,
|
||||
path: req.path
|
||||
});
|
||||
req.user = user;
|
||||
next();
|
||||
}
|
||||
catch (error) {
|
||||
logAuthEvent('AUTH_ERROR', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
ip: req.ip,
|
||||
path: req.path
|
||||
});
|
||||
return res.status(500).json({ error: 'Authentication system error' });
|
||||
}
|
||||
}
|
||||
// Middleware to check role
|
||||
function requireRole(roles) {
|
||||
return (req, res, next) => {
|
||||
const user = req.user;
|
||||
if (!user || !roles.includes(user.role)) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
// Get current user
|
||||
router.get('/me', requireAuth, (req, res) => {
|
||||
res.json(req.user);
|
||||
});
|
||||
// Setup status endpoint (required by frontend)
|
||||
router.get('/setup', async (req, res) => {
|
||||
try {
|
||||
const clientId = process.env.GOOGLE_CLIENT_ID;
|
||||
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
||||
const redirectUri = process.env.GOOGLE_REDIRECT_URI;
|
||||
const frontendUrl = process.env.FRONTEND_URL;
|
||||
logAuthEvent('SETUP_CHECK', {
|
||||
client_id_present: !!clientId,
|
||||
client_secret_present: !!clientSecret,
|
||||
redirect_uri_present: !!redirectUri,
|
||||
frontend_url_present: !!frontendUrl,
|
||||
environment_valid: isEnvironmentValid
|
||||
});
|
||||
// Check database connectivity
|
||||
let userCount = 0;
|
||||
let databaseConnected = false;
|
||||
try {
|
||||
userCount = await databaseService_1.default.getUserCount();
|
||||
databaseConnected = true;
|
||||
logAuthEvent('DATABASE_CHECK', { status: 'connected', user_count: userCount });
|
||||
}
|
||||
catch (dbError) {
|
||||
logAuthEvent('DATABASE_ERROR', {
|
||||
error: dbError instanceof Error ? dbError.message : 'Unknown database error'
|
||||
});
|
||||
return res.status(500).json({
|
||||
error: 'Database connection failed',
|
||||
details: 'Cannot connect to PostgreSQL database'
|
||||
});
|
||||
}
|
||||
const setupCompleted = !!(clientId &&
|
||||
clientSecret &&
|
||||
redirectUri &&
|
||||
frontendUrl &&
|
||||
clientId !== 'your-google-client-id-from-console' &&
|
||||
clientId !== 'your-google-client-id' &&
|
||||
isEnvironmentValid);
|
||||
const response = {
|
||||
setupCompleted,
|
||||
firstAdminCreated: userCount > 0,
|
||||
oauthConfigured: !!(clientId && clientSecret),
|
||||
databaseConnected,
|
||||
environmentValid: isEnvironmentValid,
|
||||
configuration: {
|
||||
google_oauth: !!(clientId && clientSecret),
|
||||
redirect_uri_configured: !!redirectUri,
|
||||
frontend_url_configured: !!frontendUrl,
|
||||
production_ready: setupCompleted && databaseConnected
|
||||
}
|
||||
};
|
||||
logAuthEvent('SETUP_STATUS', response);
|
||||
res.json(response);
|
||||
}
|
||||
catch (error) {
|
||||
logAuthEvent('SETUP_ERROR', {
|
||||
error: error instanceof Error ? error.message : 'Unknown setup error'
|
||||
});
|
||||
res.status(500).json({
|
||||
error: 'Setup check failed',
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
}
|
||||
});
|
||||
// Start Google OAuth flow
|
||||
router.get('/google', (req, res) => {
|
||||
try {
|
||||
const authUrl = (0, simpleAuth_1.getGoogleAuthUrl)();
|
||||
res.redirect(authUrl);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error starting Google OAuth:', error);
|
||||
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5173';
|
||||
res.redirect(`${frontendUrl}?error=oauth_not_configured`);
|
||||
}
|
||||
});
|
||||
// Handle Google OAuth callback (this is where Google redirects back to)
|
||||
router.get('/google/callback', async (req, res) => {
|
||||
const { code, error, state } = req.query;
|
||||
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:5173';
|
||||
logAuthEvent('OAUTH_CALLBACK', {
|
||||
has_code: !!code,
|
||||
has_error: !!error,
|
||||
error_type: error,
|
||||
state,
|
||||
frontend_url: frontendUrl,
|
||||
ip: req.ip,
|
||||
user_agent: req.get('User-Agent')
|
||||
});
|
||||
// Validate environment before proceeding
|
||||
if (!isEnvironmentValid) {
|
||||
logAuthEvent('OAUTH_CALLBACK_ERROR', { reason: 'invalid_environment' });
|
||||
return res.redirect(`${frontendUrl}?error=configuration_error&message=OAuth not properly configured`);
|
||||
}
|
||||
if (error) {
|
||||
logAuthEvent('OAUTH_ERROR', { error, ip: req.ip });
|
||||
return res.redirect(`${frontendUrl}?error=${error}&message=OAuth authorization failed`);
|
||||
}
|
||||
if (!code) {
|
||||
logAuthEvent('OAUTH_ERROR', { reason: 'no_authorization_code', ip: req.ip });
|
||||
return res.redirect(`${frontendUrl}?error=no_code&message=No authorization code received`);
|
||||
}
|
||||
try {
|
||||
logAuthEvent('OAUTH_TOKEN_EXCHANGE_START', { code_length: code.length });
|
||||
// Exchange code for tokens
|
||||
const tokens = await (0, simpleAuth_1.exchangeCodeForTokens)(code);
|
||||
if (!tokens || !tokens.access_token) {
|
||||
logAuthEvent('OAUTH_TOKEN_EXCHANGE_FAILED', { tokens_received: !!tokens });
|
||||
return res.redirect(`${frontendUrl}?error=token_exchange_failed&message=Failed to exchange authorization code`);
|
||||
}
|
||||
logAuthEvent('OAUTH_TOKEN_EXCHANGE_SUCCESS', { has_access_token: !!tokens.access_token });
|
||||
// Get user info
|
||||
const googleUser = await (0, simpleAuth_1.getGoogleUserInfo)(tokens.access_token);
|
||||
if (!googleUser || !googleUser.email) {
|
||||
logAuthEvent('OAUTH_USER_INFO_FAILED', { user_data: !!googleUser });
|
||||
return res.redirect(`${frontendUrl}?error=user_info_failed&message=Failed to get user information from Google`);
|
||||
}
|
||||
logAuthEvent('OAUTH_USER_INFO_SUCCESS', {
|
||||
email: googleUser.email,
|
||||
name: googleUser.name,
|
||||
verified_email: googleUser.verified_email
|
||||
});
|
||||
// Check if user exists or create new user
|
||||
let user = await databaseService_1.default.getUserByEmail(googleUser.email);
|
||||
if (!user) {
|
||||
// Determine role - first user becomes admin, others need approval
|
||||
const approvedUserCount = await databaseService_1.default.getApprovedUserCount();
|
||||
const role = approvedUserCount === 0 ? 'administrator' : 'coordinator';
|
||||
logAuthEvent('USER_CREATION', {
|
||||
email: googleUser.email,
|
||||
role,
|
||||
is_first_user: approvedUserCount === 0
|
||||
});
|
||||
user = await databaseService_1.default.createUser({
|
||||
id: googleUser.id,
|
||||
google_id: googleUser.id,
|
||||
email: googleUser.email,
|
||||
name: googleUser.name,
|
||||
profile_picture_url: googleUser.picture,
|
||||
role
|
||||
});
|
||||
// Auto-approve first admin, others need approval
|
||||
if (approvedUserCount === 0) {
|
||||
await databaseService_1.default.updateUserApprovalStatus(googleUser.email, 'approved');
|
||||
user.approval_status = 'approved';
|
||||
logAuthEvent('FIRST_ADMIN_CREATED', { email: googleUser.email });
|
||||
}
|
||||
else {
|
||||
logAuthEvent('USER_PENDING_APPROVAL', { email: googleUser.email });
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Update last sign in
|
||||
await databaseService_1.default.updateUserLastSignIn(googleUser.email);
|
||||
logAuthEvent('USER_LOGIN', {
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
approval_status: user.approval_status
|
||||
});
|
||||
}
|
||||
// Check if user is approved
|
||||
if (user.approval_status !== 'approved') {
|
||||
logAuthEvent('USER_NOT_APPROVED', { email: user.email, status: user.approval_status });
|
||||
return res.redirect(`${frontendUrl}?error=pending_approval&message=Your account is pending administrator approval`);
|
||||
}
|
||||
// Generate JWT token
|
||||
const token = (0, simpleAuth_1.generateToken)(user);
|
||||
logAuthEvent('JWT_TOKEN_GENERATED', {
|
||||
user_id: user.id,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
token_length: token.length
|
||||
});
|
||||
// Redirect to frontend with token
|
||||
const callbackUrl = `${frontendUrl}/auth/callback?token=${token}`;
|
||||
logAuthEvent('OAUTH_SUCCESS_REDIRECT', { callback_url: callbackUrl });
|
||||
res.redirect(callbackUrl);
|
||||
}
|
||||
catch (error) {
|
||||
logAuthEvent('OAUTH_CALLBACK_ERROR', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
ip: req.ip
|
||||
});
|
||||
res.redirect(`${frontendUrl}?error=oauth_failed&message=Authentication failed due to server error`);
|
||||
}
|
||||
});
|
||||
// Exchange OAuth code for JWT token (alternative endpoint for frontend)
|
||||
router.post('/google/exchange', async (req, res) => {
|
||||
const { code } = req.body;
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: 'Authorization code is required' });
|
||||
}
|
||||
try {
|
||||
// Exchange code for tokens
|
||||
const tokens = await (0, simpleAuth_1.exchangeCodeForTokens)(code);
|
||||
// Get user info
|
||||
const googleUser = await (0, simpleAuth_1.getGoogleUserInfo)(tokens.access_token);
|
||||
// Check if user exists or create new user
|
||||
let user = await databaseService_1.default.getUserByEmail(googleUser.email);
|
||||
if (!user) {
|
||||
// Determine role - first user becomes admin
|
||||
const userCount = await databaseService_1.default.getUserCount();
|
||||
const role = userCount === 0 ? 'administrator' : 'coordinator';
|
||||
user = await databaseService_1.default.createUser({
|
||||
id: googleUser.id,
|
||||
google_id: googleUser.id,
|
||||
email: googleUser.email,
|
||||
name: googleUser.name,
|
||||
profile_picture_url: googleUser.picture,
|
||||
role
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Update last sign in
|
||||
await databaseService_1.default.updateUserLastSignIn(googleUser.email);
|
||||
console.log(`✅ User logged in: ${user.name} (${user.email})`);
|
||||
}
|
||||
// Generate JWT token
|
||||
const token = (0, simpleAuth_1.generateToken)(user);
|
||||
// Return token to frontend
|
||||
res.json({
|
||||
token,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
picture: user.profile_picture_url,
|
||||
role: user.role
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error in OAuth exchange:', error);
|
||||
res.status(500).json({ error: 'Failed to exchange authorization code' });
|
||||
}
|
||||
});
|
||||
// Get OAuth URL for frontend to redirect to
|
||||
router.get('/google/url', (req, res) => {
|
||||
try {
|
||||
const authUrl = (0, simpleAuth_1.getGoogleAuthUrl)();
|
||||
res.json({ url: authUrl });
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error getting Google OAuth URL:', error);
|
||||
res.status(500).json({ error: 'OAuth not configured' });
|
||||
}
|
||||
});
|
||||
// Logout
|
||||
router.post('/logout', (req, res) => {
|
||||
// With JWT, logout is handled client-side by removing the token
|
||||
res.json({ message: 'Logged out successfully' });
|
||||
});
|
||||
// Get auth status
|
||||
router.get('/status', (req, res) => {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.json({ authenticated: false });
|
||||
}
|
||||
const token = authHeader.substring(7);
|
||||
const user = (0, simpleAuth_1.verifyToken)(token);
|
||||
if (!user) {
|
||||
return res.json({ authenticated: false });
|
||||
}
|
||||
res.json({
|
||||
authenticated: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
picture: user.profile_picture_url,
|
||||
role: user.role
|
||||
}
|
||||
});
|
||||
});
|
||||
// USER MANAGEMENT ENDPOINTS
|
||||
// List all users (admin only)
|
||||
router.get('/users', requireAuth, requireRole(['administrator']), async (req, res) => {
|
||||
try {
|
||||
const users = await databaseService_1.default.getAllUsers();
|
||||
const userList = users.map(user => ({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
picture: user.profile_picture_url,
|
||||
role: user.role,
|
||||
created_at: user.created_at,
|
||||
last_login: user.last_login,
|
||||
provider: 'google'
|
||||
}));
|
||||
res.json(userList);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error fetching users:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch users' });
|
||||
}
|
||||
});
|
||||
// Update user role (admin only)
|
||||
router.patch('/users/:email/role', requireAuth, requireRole(['administrator']), async (req, res) => {
|
||||
const { email } = req.params;
|
||||
const { role } = req.body;
|
||||
if (!['administrator', 'coordinator', 'driver'].includes(role)) {
|
||||
return res.status(400).json({ error: 'Invalid role' });
|
||||
}
|
||||
try {
|
||||
const user = await databaseService_1.default.updateUserRole(email, role);
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error updating user role:', error);
|
||||
res.status(500).json({ error: 'Failed to update user role' });
|
||||
}
|
||||
});
|
||||
// Delete user (admin only)
|
||||
router.delete('/users/:email', requireAuth, requireRole(['administrator']), async (req, res) => {
|
||||
const { email } = req.params;
|
||||
const currentUser = req.user;
|
||||
// Prevent admin from deleting themselves
|
||||
if (email === currentUser.email) {
|
||||
return res.status(400).json({ error: 'Cannot delete your own account' });
|
||||
}
|
||||
try {
|
||||
const deletedUser = await databaseService_1.default.deleteUser(email);
|
||||
if (!deletedUser) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
res.json({ success: true, message: 'User deleted successfully' });
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error deleting user:', error);
|
||||
res.status(500).json({ error: 'Failed to delete user' });
|
||||
}
|
||||
});
|
||||
// Get user by email (admin only)
|
||||
router.get('/users/:email', requireAuth, requireRole(['administrator']), async (req, res) => {
|
||||
const { email } = req.params;
|
||||
try {
|
||||
const user = await databaseService_1.default.getUserByEmail(email);
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
res.json({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
picture: user.profile_picture_url,
|
||||
role: user.role,
|
||||
created_at: user.created_at,
|
||||
last_login: user.last_login,
|
||||
provider: 'google',
|
||||
approval_status: user.approval_status
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error fetching user:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch user' });
|
||||
}
|
||||
});
|
||||
// USER APPROVAL ENDPOINTS
|
||||
// Get pending users (admin only)
|
||||
router.get('/users/pending/list', requireAuth, requireRole(['administrator']), async (req, res) => {
|
||||
try {
|
||||
const pendingUsers = await databaseService_1.default.getPendingUsers();
|
||||
const userList = pendingUsers.map(user => ({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
picture: user.profile_picture_url,
|
||||
role: user.role,
|
||||
created_at: user.created_at,
|
||||
provider: 'google',
|
||||
approval_status: user.approval_status
|
||||
}));
|
||||
res.json(userList);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error fetching pending users:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch pending users' });
|
||||
}
|
||||
});
|
||||
// Approve or deny user (admin only)
|
||||
router.patch('/users/:email/approval', requireAuth, requireRole(['administrator']), async (req, res) => {
|
||||
const { email } = req.params;
|
||||
const { status } = req.body;
|
||||
if (!['approved', 'denied'].includes(status)) {
|
||||
return res.status(400).json({ error: 'Invalid approval status. Must be "approved" or "denied"' });
|
||||
}
|
||||
try {
|
||||
const user = await databaseService_1.default.updateUserApprovalStatus(email, status);
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'User not found' });
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
message: `User ${status} successfully`,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
approval_status: user.approval_status
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error updating user approval:', error);
|
||||
res.status(500).json({ error: 'Failed to update user approval' });
|
||||
}
|
||||
});
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=simpleAuth.js.map
|
||||
Reference in New Issue
Block a user