Updated the passenger web portal and added more endpoints to the api

This commit is contained in:
Stephanos A
2026-06-02 14:51:12 +03:00
parent 5059a1fe58
commit ed30ab0cda
34 changed files with 3396 additions and 674 deletions

View File

@@ -1,7 +1,8 @@
import { Body, Controller, Post, HttpCode, HttpStatus } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger';
import { Body, Controller, Post, HttpCode, HttpStatus, UseGuards, Get, Request, UnauthorizedException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
import { AuthService } from './auth.service';
import { RegisterDto, LoginDto, RequestOtpDto, VerifyOtpDto, RequestPasswordResetDto, ResetPasswordDto } from './auth.dto';
import { JwtGuard } from '../../common/jwt.guard';
@ApiTags('Auth')
@Controller('auth')
@@ -78,4 +79,142 @@ export class AuthController {
@ApiResponse({ status: 404, description: 'User not found' })
@ApiBody({ type: ResetPasswordDto })
resetPassword(@Body() dto: ResetPasswordDto) { return this.service.resetPassword(dto); }
@Post('logout')
@HttpCode(HttpStatus.OK)
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Logout current user',
description: `Logout the authenticated user and invalidate their session.
### What happens:
- Invalidates the current session token
- Records logout in audit log
- Frontend should clear stored token and redirect to home
### Authentication:
- **Required**: JWT Bearer Token
- Token will be invalidated after successful logout`
})
@ApiResponse({
status: 200,
description: 'Logout successful',
schema: {
example: {
success: true,
message: 'Logged out successfully'
}
}
})
@ApiResponse({ status: 401, description: 'Unauthorized - Invalid or missing token' })
logout(@Request() req: any) {
if (!req.user || !req.user.userId) {
throw new UnauthorizedException('User not authenticated');
}
return this.service.logout(req.user.userId);
}
@Get('profile')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({
summary: 'Get current user profile',
description: `**Returns complete user profile with all connected data**
---
### Response Includes
#### User Information
- Basic details (id, email, phone, fullName, role)
- Nationality and document information
- Fayda verification status
- Account timestamps (created, last login)
#### Passenger Data (if role=PASSENGER)
- Passenger ID and preferences
- **Loyalty Account**: Tier, points balance, lifetime points
- **Wallet Account**: Balance (minor units), currency
#### User Preferences
- Language, notification settings, etc.
---
### Use Cases
1. **App Initialization**: Fetch on app load to get user context
2. **Profile Pre-fill**: Use data to auto-fill booking forms
3. **Verification Check**: Check \`faydaVerified\` before registration
4. **Loyalty Display**: Show tier and points in UI
5. **Wallet Balance**: Display available balance
---
### Authentication
- **Required**: JWT Bearer Token
- Token must be valid and not expired
- Returns profile for authenticated user only`,
})
@ApiResponse({
status: 200,
description: 'User profile retrieved successfully',
schema: {
example: {
id: 'user-uuid-123',
email: 'kelemu@email.com',
phone: '+251911234567',
fullName: 'Kelemu Abebe',
role: 'PASSENGER',
nationality: 'Ethiopian',
nationalityCode: 'ET',
nationalId: null,
passportNumber: null,
faydaVerified: true,
faydaVerifiedAt: '2024-01-15T10:30:00.000Z',
lastLoginAt: '2024-01-20T14:22:00.000Z',
createdAt: '2023-12-01T08:00:00.000Z',
passenger: {
id: 'passenger-uuid-456',
preferredLanguage: 'am',
loyalty: {
tier: 'SILVER',
pointsBalance: 1500,
lifetimePoints: 3000
},
wallet: {
balanceMinor: 50000,
currency: 'ETB'
}
},
preferences: {
emailNotifications: true,
smsNotifications: true,
language: 'am'
}
}
}
})
@ApiResponse({
status: 401,
description: 'Unauthorized - Invalid or missing JWT token',
schema: {
example: {
statusCode: 401,
message: 'Unauthorized'
}
}
})
getProfile(@Request() req: any) {
console.log('Profile request - User from JWT:', req.user);
if (!req.user || !req.user.userId) {
throw new UnauthorizedException('User not authenticated');
}
return this.service.getProfile(req.user.userId);
}
}

View File

@@ -143,4 +143,70 @@ export class AuthService {
data: { userId, action, entityType, entityId, oldData, newData }
});
}
async getProfile(userId: string) {
if (!userId) {
throw new UnauthorizedException('User ID not found in token');
}
const user = await this.prisma.user.findUnique({
where: { id: userId },
include: {
passenger: {
include: {
loyalty: true,
wallet: true,
},
},
preferences: true,
},
});
if (!user) throw new UnauthorizedException('User not found');
return {
id: user.id,
email: user.email,
phone: user.phone,
fullName: user.fullName,
role: user.role,
nationality: user.nationality,
nationalityCode: user.nationalityCode,
nationalId: user.nationalId,
passportNumber: user.passportNumber,
faydaVerified: user.faydaVerified,
faydaVerifiedAt: user.faydaVerifiedAt,
lastLoginAt: user.lastLoginAt,
createdAt: user.createdAt,
passenger: user.passenger ? {
id: user.passenger.id,
preferredLanguage: user.passenger.preferredLanguage,
loyalty: user.passenger.loyalty ? {
tier: user.passenger.loyalty.tier,
pointsBalance: user.passenger.loyalty.pointsBalance,
lifetimePoints: user.passenger.loyalty.lifetimePoints,
} : null,
wallet: user.passenger.wallet ? {
balanceMinor: user.passenger.wallet.balanceMinor,
currency: user.passenger.wallet.currency,
} : null,
} : null,
preferences: user.preferences,
};
}
async logout(userId: string) {
// Invalidate all active sessions for this user
await this.prisma.session.deleteMany({
where: { userId }
});
// Log the logout action
await this.createAuditLog(userId, 'USER_LOGOUT', 'User', userId, null, null);
return {
success: true,
message: 'Logged out successfully'
};
}
}