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:
265
backend-old-20260125/dist/services/databaseService.js
vendored
Normal file
265
backend-old-20260125/dist/services/databaseService.js
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// Import the existing backup service
|
||||
const databaseService_1 = __importDefault(require("./backup-services/databaseService"));
|
||||
// Extend the backup service with new user management methods
|
||||
class EnhancedDatabaseService {
|
||||
constructor() {
|
||||
this.backupService = databaseService_1.default;
|
||||
}
|
||||
// Delegate all existing methods to backup service
|
||||
async query(text, params) {
|
||||
return this.backupService.query(text, params);
|
||||
}
|
||||
async getClient() {
|
||||
return this.backupService.getClient();
|
||||
}
|
||||
async close() {
|
||||
return this.backupService.close();
|
||||
}
|
||||
async initializeTables() {
|
||||
return this.backupService.initializeTables();
|
||||
}
|
||||
// User methods from backup service
|
||||
async createUser(user) {
|
||||
return this.backupService.createUser(user);
|
||||
}
|
||||
async getUserByEmail(email) {
|
||||
return this.backupService.getUserByEmail(email);
|
||||
}
|
||||
async getUserById(id) {
|
||||
return this.backupService.getUserById(id);
|
||||
}
|
||||
async updateUserRole(email, role) {
|
||||
return this.backupService.updateUserRole(email, role);
|
||||
}
|
||||
async updateUserLastSignIn(email) {
|
||||
return this.backupService.updateUserLastSignIn(email);
|
||||
}
|
||||
async getUserCount() {
|
||||
return this.backupService.getUserCount();
|
||||
}
|
||||
async updateUserApprovalStatus(email, status) {
|
||||
return this.backupService.updateUserApprovalStatus(email, status);
|
||||
}
|
||||
async getApprovedUserCount() {
|
||||
return this.backupService.getApprovedUserCount();
|
||||
}
|
||||
async getAllUsers() {
|
||||
return this.backupService.getAllUsers();
|
||||
}
|
||||
async deleteUser(email) {
|
||||
return this.backupService.deleteUser(email);
|
||||
}
|
||||
async getPendingUsers() {
|
||||
return this.backupService.getPendingUsers();
|
||||
}
|
||||
// NEW: Enhanced user management methods
|
||||
async completeUserOnboarding(email, onboardingData) {
|
||||
const query = `
|
||||
UPDATE users
|
||||
SET phone = $1,
|
||||
organization = $2,
|
||||
onboarding_data = $3,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $4
|
||||
RETURNING *
|
||||
`;
|
||||
const result = await this.query(query, [
|
||||
onboardingData.phone,
|
||||
onboardingData.organization,
|
||||
JSON.stringify(onboardingData),
|
||||
email
|
||||
]);
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
async approveUser(userEmail, approvedBy, newRole) {
|
||||
const query = `
|
||||
UPDATE users
|
||||
SET status = 'active',
|
||||
approval_status = 'approved',
|
||||
approved_by = $1,
|
||||
approved_at = CURRENT_TIMESTAMP,
|
||||
role = COALESCE($2, role),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $3
|
||||
RETURNING *
|
||||
`;
|
||||
const result = await this.query(query, [approvedBy, newRole, userEmail]);
|
||||
// Log audit
|
||||
if (result.rows[0]) {
|
||||
await this.createAuditLog('user_approved', userEmail, approvedBy, { newRole });
|
||||
}
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
async rejectUser(userEmail, rejectedBy, reason) {
|
||||
const query = `
|
||||
UPDATE users
|
||||
SET status = 'deactivated',
|
||||
approval_status = 'denied',
|
||||
rejected_by = $1,
|
||||
rejected_at = CURRENT_TIMESTAMP,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $2
|
||||
RETURNING *
|
||||
`;
|
||||
const result = await this.query(query, [rejectedBy, userEmail]);
|
||||
// Log audit
|
||||
if (result.rows[0]) {
|
||||
await this.createAuditLog('user_rejected', userEmail, rejectedBy, { reason });
|
||||
}
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
async deactivateUser(userEmail, deactivatedBy) {
|
||||
const query = `
|
||||
UPDATE users
|
||||
SET status = 'deactivated',
|
||||
deactivated_by = $1,
|
||||
deactivated_at = CURRENT_TIMESTAMP,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $2
|
||||
RETURNING *
|
||||
`;
|
||||
const result = await this.query(query, [deactivatedBy, userEmail]);
|
||||
// Log audit
|
||||
if (result.rows[0]) {
|
||||
await this.createAuditLog('user_deactivated', userEmail, deactivatedBy, {});
|
||||
}
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
async reactivateUser(userEmail, reactivatedBy) {
|
||||
const query = `
|
||||
UPDATE users
|
||||
SET status = 'active',
|
||||
deactivated_by = NULL,
|
||||
deactivated_at = NULL,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE email = $1
|
||||
RETURNING *
|
||||
`;
|
||||
const result = await this.query(query, [userEmail]);
|
||||
// Log audit
|
||||
if (result.rows[0]) {
|
||||
await this.createAuditLog('user_reactivated', userEmail, reactivatedBy, {});
|
||||
}
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
async createAuditLog(action, userEmail, performedBy, details) {
|
||||
const query = `
|
||||
INSERT INTO user_audit_log (action, user_email, performed_by, action_details)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
`;
|
||||
await this.query(query, [action, userEmail, performedBy, JSON.stringify(details)]);
|
||||
}
|
||||
async getUserAuditLog(userEmail) {
|
||||
const query = `
|
||||
SELECT * FROM user_audit_log
|
||||
WHERE user_email = $1
|
||||
ORDER BY created_at DESC
|
||||
`;
|
||||
const result = await this.query(query, [userEmail]);
|
||||
return result.rows;
|
||||
}
|
||||
async getUsersWithFilters(filters) {
|
||||
let query = 'SELECT * FROM users WHERE 1=1';
|
||||
const params = [];
|
||||
let paramIndex = 1;
|
||||
if (filters.status) {
|
||||
query += ` AND status = $${paramIndex}`;
|
||||
params.push(filters.status);
|
||||
paramIndex++;
|
||||
}
|
||||
if (filters.role) {
|
||||
query += ` AND role = $${paramIndex}`;
|
||||
params.push(filters.role);
|
||||
paramIndex++;
|
||||
}
|
||||
if (filters.search) {
|
||||
query += ` AND (LOWER(name) LIKE LOWER($${paramIndex}) OR LOWER(email) LIKE LOWER($${paramIndex}) OR LOWER(organization) LIKE LOWER($${paramIndex}))`;
|
||||
params.push(`%${filters.search}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
query += ' ORDER BY created_at DESC';
|
||||
const result = await this.query(query, params);
|
||||
return result.rows;
|
||||
}
|
||||
// Fix for first user admin issue
|
||||
async getActiveUserCount() {
|
||||
const query = "SELECT COUNT(*) as count FROM users WHERE status = 'active'";
|
||||
const result = await this.query(query);
|
||||
return parseInt(result.rows[0].count);
|
||||
}
|
||||
async isFirstUser() {
|
||||
// Check if there are any active or approved users
|
||||
const query = "SELECT COUNT(*) as count FROM users WHERE status = 'active' OR approval_status = 'approved'";
|
||||
const result = await this.query(query);
|
||||
return parseInt(result.rows[0].count) === 0;
|
||||
}
|
||||
// VIP methods from backup service
|
||||
async createVip(vip) {
|
||||
return this.backupService.createVip(vip);
|
||||
}
|
||||
async getVipById(id) {
|
||||
return this.backupService.getVipById(id);
|
||||
}
|
||||
async getAllVips() {
|
||||
return this.backupService.getAllVips();
|
||||
}
|
||||
async updateVip(id, vip) {
|
||||
return this.backupService.updateVip(id, vip);
|
||||
}
|
||||
async deleteVip(id) {
|
||||
return this.backupService.deleteVip(id);
|
||||
}
|
||||
async getVipsByDepartment(department) {
|
||||
return this.backupService.getVipsByDepartment(department);
|
||||
}
|
||||
// Driver methods from backup service
|
||||
async createDriver(driver) {
|
||||
return this.backupService.createDriver(driver);
|
||||
}
|
||||
async getDriverById(id) {
|
||||
return this.backupService.getDriverById(id);
|
||||
}
|
||||
async getAllDrivers() {
|
||||
return this.backupService.getAllDrivers();
|
||||
}
|
||||
async updateDriver(id, driver) {
|
||||
return this.backupService.updateDriver(id, driver);
|
||||
}
|
||||
async deleteDriver(id) {
|
||||
return this.backupService.deleteDriver(id);
|
||||
}
|
||||
async getDriversByDepartment(department) {
|
||||
return this.backupService.getDriversByDepartment(department);
|
||||
}
|
||||
async updateDriverLocation(id, location) {
|
||||
return this.backupService.updateDriverLocation(id, location);
|
||||
}
|
||||
// Schedule methods from backup service
|
||||
async createScheduleEvent(vipId, event) {
|
||||
return this.backupService.createScheduleEvent(vipId, event);
|
||||
}
|
||||
async getScheduleByVipId(vipId) {
|
||||
return this.backupService.getScheduleByVipId(vipId);
|
||||
}
|
||||
async updateScheduleEvent(vipId, eventId, event) {
|
||||
return this.backupService.updateScheduleEvent(vipId, eventId, event);
|
||||
}
|
||||
async deleteScheduleEvent(vipId, eventId) {
|
||||
return this.backupService.deleteScheduleEvent(vipId, eventId);
|
||||
}
|
||||
async getAllScheduleEvents() {
|
||||
return this.backupService.getAllScheduleEvents();
|
||||
}
|
||||
async getScheduleEventsByDateRange(startDate, endDate) {
|
||||
return this.backupService.getScheduleEventsByDateRange(startDate, endDate);
|
||||
}
|
||||
}
|
||||
// Export singleton instance
|
||||
const databaseService = new EnhancedDatabaseService();
|
||||
exports.default = databaseService;
|
||||
//# sourceMappingURL=databaseService.js.map
|
||||
Reference in New Issue
Block a user