P1: drivers + trips(★state machine) + matching(Redis GEO) + tariff engine + maps + realtime + seed
- drivers: apply/approve/status/location (+Redis GEO presence) - trips: request→accept→status transitions + trip_events audit + offers via socket - matching: Redis GEO nearby drivers - tariff: JSON-rule engine (time_or_distance/window/rounding) + quote endpoint - maps: straight-line routing seam (انطلق later) - realtime: Socket.IO gateway (JWT auth, tenant rooms) - seed: demo tenant siro + default tariff on boot - migration InitP1 (drivers/tariffs/trips/trip_events) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import {
|
||||
OnGatewayConnection,
|
||||
WebSocketGateway,
|
||||
WebSocketServer,
|
||||
} from '@nestjs/websockets';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
|
||||
/**
|
||||
* البوابة الحية (Socket.IO) — docs/09. المصادقة على الاتصال بـ JWT،
|
||||
* والانضمام لغرف مُنطَّقة بالمستأجر (لا تسريب بين المستأجرين).
|
||||
* غرف: tenant:{id}:driver:{driverId} · tenant:{id}:trip:{tripId} · tenant:{id}:dispatch
|
||||
*/
|
||||
@WebSocketGateway({ cors: true })
|
||||
export class RealtimeGateway implements OnGatewayConnection {
|
||||
private readonly logger = new Logger('Realtime');
|
||||
|
||||
@WebSocketServer()
|
||||
server: Server;
|
||||
|
||||
constructor(private readonly jwt: JwtService) {}
|
||||
|
||||
handleConnection(client: Socket) {
|
||||
try {
|
||||
const token =
|
||||
(client.handshake.auth?.token as string) ||
|
||||
(client.handshake.headers?.authorization as string)?.replace('Bearer ', '');
|
||||
if (!token) throw new Error('no token');
|
||||
const p: any = this.jwt.verify(token);
|
||||
const tenantId = p.tenant_id;
|
||||
client.data.user = { userId: p.sub, tenantId, role: p.role };
|
||||
client.join(`tenant:${tenantId}:user:${p.sub}`);
|
||||
if (p.role === 'driver') client.join(`tenant:${tenantId}:drivers`);
|
||||
this.logger.debug(`connected user=${p.sub} tenant=${tenantId} role=${p.role}`);
|
||||
} catch {
|
||||
client.disconnect(true);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- مساعدات البث التي تستدعيها الخدمات ----
|
||||
|
||||
/** عرض رحلة لسائق محدد. */
|
||||
offerToDriver(tenantId: string, driverUserId: string, payload: any) {
|
||||
this.server.to(`tenant:${tenantId}:user:${driverUserId}`).emit('trip:offer', payload);
|
||||
}
|
||||
|
||||
/** تحديث حالة الرحلة للراكب أو السائق. */
|
||||
tripUpdate(tenantId: string, userId: string, payload: any) {
|
||||
this.server.to(`tenant:${tenantId}:user:${userId}`).emit('trip:update', payload);
|
||||
}
|
||||
|
||||
/** بث للوحة المشغّل. */
|
||||
dispatch(tenantId: string, event: string, payload: any) {
|
||||
this.server.to(`tenant:${tenantId}:dispatch`).emit(event, payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { RealtimeGateway } from './realtime.gateway';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: async (cfg: ConfigService) => ({
|
||||
secret: cfg.get<string>('JWT_SECRET') || 'change_me_jwt_secret',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
providers: [RealtimeGateway],
|
||||
exports: [RealtimeGateway],
|
||||
})
|
||||
export class RealtimeModule {}
|
||||
Reference in New Issue
Block a user