Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .env.infra.example
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ VEHICLES_GRPC_PORT=50055

# ---------- Logger Service ----------
LOGGER_APP_NAME=LOGGER-SERVICE
LOGGER_GRPC_PORT=50058
LOGGER_GRPC_PORT=50060

# ---------- Driver Service ----------
DRIVER_APP_NAME=DRIVER-SERVICE
Expand Down
89 changes: 46 additions & 43 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions protos/routes.proto
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,14 @@ service TripsService {
returns (ListTripsByTimeRangeResponse);
rpc ListTripsByDriver(ListTripsByDriverRequest)
returns (ListTripsByDriverResponse);

// Verificaciones de existencia de viajes (para validaciones en otros servicios)
rpc HasTripsBySupervisor(HasTripsBySupervisorRequest)
returns (HasTripsBySupervisorResponse);
rpc HasTripsByDriver(HasTripsByDriverRequest)
returns (HasTripsByDriverResponse);
rpc HasTripsByVehicle(HasTripsByVehicleRequest)
returns (HasTripsByVehicleResponse);
}

message UpdateTripLocationRequest {
Expand Down Expand Up @@ -439,3 +447,31 @@ message ListTripsByDriverResponse {
repeated DriverTripDetail trips = 1;
int64 total_trips = 2;
}

// =====================================================
// Has Trips Messages (para validaciones en otros servicios)
// =====================================================

message HasTripsBySupervisorRequest {
int64 supervisor_id = 1;
}

message HasTripsBySupervisorResponse {
bool has_trips = 1;
}

message HasTripsByDriverRequest {
int64 driver_id = 1;
}

message HasTripsByDriverResponse {
bool has_trips = 1;
}

message HasTripsByVehicleRequest {
int64 vehicle_id = 1;
}

message HasTripsByVehicleResponse {
bool has_trips = 1;
}
103 changes: 102 additions & 1 deletion services/api-gateway/src/http/drivers-svc/drivers.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ import {
Req,
ParseIntPipe,
BadRequestException,
Logger,
} from '@nestjs/common';
import { Observable, from, switchMap, map } from 'rxjs';
import { Observable, from, switchMap, map, forkJoin, of, lastValueFrom } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { GrpcClientFactory } from '../../grpc/grpc-client.factory';
import { GrpcTimeout } from '../../grpc/grpc-timeout.interceptor';
import { DriversServiceClient } from '../../grpc/clients/driverms/drivers.client';
import { DriversHttpMapper } from '../../grpc/mappers/driver/drivers.mapper';
import { UserServiceClient } from '../../grpc/clients/user-svc/users.client';

@Controller('drivers')
export class DriversHttpController {
private readonly logger = new Logger(DriversHttpController.name);

constructor(private readonly factory: GrpcClientFactory) {}

private async svc(req: any): Promise<DriversServiceClient> {
Expand All @@ -31,6 +36,80 @@ export class DriversHttpController {
return client.getService<DriversServiceClient>('DriversService');
}

private async usersSvc(req: any): Promise<UserServiceClient> {
const appName = process.env.USERS_APP_NAME || 'USERS-SERVICE';
const client = await this.factory.forService(
appName,
'users',
'users.proto',
);
return client.getService<UserServiceClient>('UserService');
}

/**
* Verifica si un usuario tiene el rol DRIVER
*/
private async hasDriverRole(userId: number, req: any): Promise<boolean> {
try {
const usersService = await this.usersSvc(req);
const user = await lastValueFrom(
usersService.GetUser({ userId }, req._grpcMetadata),
);

const roles = user?.roles || [];
const hasDriver = roles.some(
(role: any) => (role.name || '').toUpperCase() === 'DRIVER',
);

if (!hasDriver) {
this.logger.warn(
`User ${userId} does not have DRIVER role. Roles: ${roles.map((r: any) => r.name).join(', ')}`,
);
}

return hasDriver;
} catch (error) {
this.logger.error(
`Error checking DRIVER role for user ${userId}: ${error.message}`,
);
return false; // Si hay error, no incluir el conductor
}
}

/**
* Filtra conductores que tienen el rol DRIVER
*/
private async filterDriversByRole(
drivers: any[],
req: any,
): Promise<any[]> {
if (!drivers || drivers.length === 0) return [];

// Verificar roles en paralelo
const driverChecks = await Promise.all(
drivers.map(async (driver) => {
const userId = Number(driver.userId || driver.user_id || 0);
if (userId <= 0) {
this.logger.warn(
`Driver ${driver.driverId || driver.driver_id} has invalid userId`,
);
return null;
}

const hasRole = await this.hasDriverRole(userId, req);
return hasRole ? driver : null;
}),
);

// Filtrar los null
const filtered = driverChecks.filter((d) => d !== null);
this.logger.log(
`Filtered ${drivers.length} drivers to ${filtered.length} with DRIVER role`,
);

return filtered;
}

@Post()
@GrpcTimeout(3000)
create(
Expand Down Expand Up @@ -69,6 +148,17 @@ export class DriversHttpController {
findAll(@Req() req: any): Observable<any> {
return from(this.svc(req)).pipe(
switchMap((s) => s.FindAll({}, req._grpcMetadata)),
switchMap((response) => {
// Filtrar conductores que no tienen el rol DRIVER
const drivers = response?.drivers || [];
return from(this.filterDriversByRole(drivers, req)).pipe(
map((filteredDrivers) => ({
...response,
drivers: filteredDrivers,
total: filteredDrivers.length,
})),
);
}),
map((response) => {
console.log('🔍 GRPC RESPONSE TYPE:', typeof response?.drivers?.[0]?.driver_id);
console.log('🔍 FIRST DRIVER_ID VALUE:', response?.drivers?.[0]?.driver_id);
Expand All @@ -83,6 +173,17 @@ export class DriversHttpController {
findAllInactive(@Req() req: any): Observable<any> {
return from(this.svc(req)).pipe(
switchMap((s) => s.FindAllInactive({}, req._grpcMetadata)),
switchMap((response) => {
// Filtrar conductores que no tienen el rol DRIVER
const drivers = response?.drivers || [];
return from(this.filterDriversByRole(drivers, req)).pipe(
map((filteredDrivers) => ({
...response,
drivers: filteredDrivers,
total: filteredDrivers.length,
})),
);
}),
map((response) => DriversHttpMapper.toDriversListResponse(response)),
);
}
Expand Down
2 changes: 1 addition & 1 deletion services/driver-ms/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { DriverLicensesModule } from './driver-licenses/driver-licenses.module';
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env'], // Solo busca .env local
envFilePath: ['.env', '../../.env'],
validate,
}),
TypeOrmModule.forRootAsync({
Expand Down
Loading