Files
vip-coordinator/backend-old-20260125/dist/services/databaseService.js
kyle 868f7efc23
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
Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
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>
2026-01-31 08:50:25 +01:00

265 lines
9.0 KiB
JavaScript

"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