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

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:
2026-01-31 08:50:25 +01:00
parent 8ace1ab2c1
commit 868f7efc23
351 changed files with 44997 additions and 6276 deletions

View File

@@ -0,0 +1,106 @@
import { Injectable, NotFoundException, Logger } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { UpdateUserDto, ApproveUserDto } from './dto';
import { Role } from '@prisma/client';
@Injectable()
export class UsersService {
private readonly logger = new Logger(UsersService.name);
constructor(private prisma: PrismaService) {}
async findAll() {
return this.prisma.user.findMany({
where: { deletedAt: null },
include: { driver: true },
orderBy: { createdAt: 'desc' },
});
}
async findOne(id: string) {
const user = await this.prisma.user.findFirst({
where: { id, deletedAt: null },
include: { driver: true },
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
async update(id: string, updateUserDto: UpdateUserDto) {
const user = await this.findOne(id);
this.logger.log(`Updating user ${id}: ${JSON.stringify(updateUserDto)}`);
// Handle role change and Driver record synchronization
if (updateUserDto.role && updateUserDto.role !== user.role) {
// If changing TO DRIVER role, create a Driver record if one doesn't exist
if (updateUserDto.role === Role.DRIVER && !user.driver) {
this.logger.log(
`Creating Driver record for user ${user.email} (role change to DRIVER)`,
);
await this.prisma.driver.create({
data: {
name: user.name || user.email,
phone: user.email, // Use email as placeholder for phone
userId: user.id,
},
});
}
// If changing FROM DRIVER role to something else, remove the Driver record
if (user.role === Role.DRIVER && updateUserDto.role !== Role.DRIVER && user.driver) {
this.logger.log(
`Removing Driver record for user ${user.email} (role change from DRIVER to ${updateUserDto.role})`,
);
await this.prisma.driver.delete({
where: { id: user.driver.id },
});
}
}
return this.prisma.user.update({
where: { id: user.id },
data: updateUserDto,
include: { driver: true },
});
}
async approve(id: string, approveUserDto: ApproveUserDto) {
const user = await this.findOne(id);
this.logger.log(
`${approveUserDto.isApproved ? 'Approving' : 'Denying'} user: ${user.email}`,
);
return this.prisma.user.update({
where: { id: user.id },
data: { isApproved: approveUserDto.isApproved },
include: { driver: true },
});
}
async remove(id: string) {
const user = await this.findOne(id);
this.logger.log(`Soft deleting user: ${user.email}`);
return this.prisma.user.update({
where: { id: user.id },
data: { deletedAt: new Date() },
});
}
async getPendingUsers() {
return this.prisma.user.findMany({
where: {
deletedAt: null,
isApproved: false,
},
orderBy: { createdAt: 'asc' },
});
}
}