Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions backend/nest-api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum Fine_Status {
PENDING
PAID
OVERDUE
COURT_CASE
}

enum Payment_Status {
Expand All @@ -35,11 +36,13 @@ model QR_Scan_History {
scan_Id String @id @default(uuid())
qr_Token String
scan_Time DateTime @default(now())
traffic_Officer_Id String
traffic_Officer_Name String
driver_Name String
location String?
license_Id String
license Driving_License @relation(fields: [license_Id], references: [license_Id])
officer Traffic_Officer @relation(fields: [traffic_Officer_Id], references: [traffic_Officer_Id])
}

model DMT_Admin {
Expand Down Expand Up @@ -77,11 +80,12 @@ model Driving_License {
nic_No String @unique
status License_Status @default(ACTIVE)
image String?
points Int @default(24)
points Int @default(0)
suspended_Until DateTime?
address String
date_of_birth DateTime
blood_Group String
issue_Date DateTime
issue_Date DateTime
user_Id String @unique
user User @relation(fields: [user_Id], references: [user_Id])
dmt_Admin_Id String
Expand All @@ -94,10 +98,10 @@ model Driving_License {

model License_Vehicle_Category {
category_Id String @id @default(uuid())
vehicle_Class String
vehicle_Class String
issue_Date DateTime
expiry_Date DateTime
restriction String?
restriction String?
license_Id String
license Driving_License @relation(fields: [license_Id], references: [license_Id], onDelete: Cascade)
}
Expand All @@ -108,6 +112,8 @@ model Temporary_License {
expiry_Date DateTime
license_Id String
license Driving_License @relation(fields: [license_Id], references: [license_Id])
issued_By String
officer Traffic_Officer @relation(fields: [issued_By], references: [traffic_Officer_Id])
}

model Division {
Expand All @@ -134,18 +140,20 @@ model Divisional_Head {
}

model Traffic_Officer {
traffic_Officer_Id String @id @default(uuid())
badge_No String @unique
email String @unique
traffic_Officer_Id String @id @default(uuid())
badge_No String @unique
email String @unique
name String
role Officer_Role @default(TRAFFIC_OFFICER)
role Officer_Role @default(TRAFFIC_OFFICER)
password String
reset_Otp String?
reset_Otp_Expires_At DateTime?
divisional_Head_Id String
divisionalHead Divisional_Head @relation(fields: [divisional_Head_Id], references: [divisional_Head_Id])
divisionalHead Divisional_Head @relation(fields: [divisional_Head_Id], references: [divisional_Head_Id])
shifts Shift[]
fines Fine[]
qrScans QR_Scan_History[]
temporaryLicenses Temporary_License[]
}

model Shift {
Expand Down Expand Up @@ -191,6 +199,7 @@ model Fine_Offence {
offense_Id String
fine Fine @relation(fields: [fine_Id], references: [fine_Id])
offenceCategory Offence_Category @relation(fields: [offense_Id], references: [offense_Id])

@@id([fine_Id, offense_Id])
}

Expand Down
9 changes: 6 additions & 3 deletions backend/nest-api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,12 @@ export class AuthService {
throw new UnauthorizedException('Invalid NIC or password.');

if (user.device_Id !== deviceId) {
throw new UnauthorizedException(
'Access Denied: You can only log in from your registered device.',
);
// Return specific error structure for new device matching
throw new ForbiddenException({
code: 'DEVICE_MISMATCH',
message: 'New device detected. OTP verification required.',
phone: user.mobile_Phone_No,
});
}

return this.generateUserToken(user);
Expand Down
18 changes: 16 additions & 2 deletions backend/nest-api/src/fines/fines.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,30 @@ export class FinesController {
@Post()
issueFine(
@Request() req: AuthRequest,
@Body() body: { licenseId: string; offenseIds: string[]; comment?: string },
@Body() body: { scanToken: string; offenseIds: string[]; comment?: string },
) {
return this.finesService.issueFine({
licenseId: body.licenseId,
scanToken: body.scanToken,
officerId: req.user.id,
offenseIds: body.offenseIds,
comment: body.comment,
});
}

@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('TRAFFIC_OFFICER')
@Get('officer-stats')
getTrafficOfficerStats(@Request() req: AuthRequest) {
return this.finesService.getTrafficOfficerStats(req.user.id);
}

@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('TRAFFIC_OFFICER')
@Get('officer-fines')
async getOfficerFines(@Request() req: AuthRequest) {
return this.finesService.getOfficerFines(req.user.id);
}

@UseGuards(JwtAuthGuard)
@Get('my-fines')
getMyFines(@Request() req: AuthRequest) {
Expand Down
8 changes: 7 additions & 1 deletion backend/nest-api/src/fines/fines.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import { Module } from '@nestjs/common';
import { FinesService } from './fines.service';
import { FinesController } from './fines.controller';
import { PrismaModule } from '../prisma/prisma.module';
import { JwtModule } from '@nestjs/jwt';

@Module({
imports: [PrismaModule],
imports: [
PrismaModule,
JwtModule.register({
secret: process.env.JWT_SECRET || 'defaultSecretKey',
}),
],
controllers: [FinesController],
providers: [FinesService],
exports: [FinesService],
Expand Down
Loading
Loading