diff --git a/backend/src/ws-gateways/ai-end/ai-end.gateway.ts b/backend/src/ws-gateways/ai-end/ai-end.gateway.ts index 78573b5..3a37d50 100644 --- a/backend/src/ws-gateways/ai-end/ai-end.gateway.ts +++ b/backend/src/ws-gateways/ai-end/ai-end.gateway.ts @@ -1,4 +1,4 @@ -import { Inject, forwardRef } from '@nestjs/common'; +import { Inject, Logger, forwardRef } from '@nestjs/common'; import { WebSocketServer, SubscribeMessage, @@ -38,8 +38,13 @@ interface CameraConfig { serveClient: false, }) export class AiEndGateway implements OnGatewayConnection, OnGatewayDisconnect { + private readonly logger = new Logger(AiEndGateway.name); + @WebSocketServer() wsServer: Server; + + connecetedClients: Map = new Map(); + constructor( @Inject(forwardRef(() => CameraService)) private cameraService: CameraService, @@ -51,14 +56,12 @@ export class AiEndGateway implements OnGatewayConnection, OnGatewayDisconnect { private utilsService: UtilsService, ) {} - connecetedClients: Map = new Map(); - @SubscribeMessage('message') handleMessage( @MessageBody() body: string, @ConnectedSocket() client: Socket, ) { - console.log('message received: ', body); + this.logger.log(`message received: ${body}`); client.emit('message', 'hello from server'); } @@ -67,11 +70,9 @@ export class AiEndGateway implements OnGatewayConnection, OnGatewayDisconnect { @MessageBody() body: AlarmData, @ConnectedSocket() client: Socket, ) { - console.log('alarm received: '); - const { cameraID } = client.data as ClientInfo; const sourceCamera = new Camera(); - sourceCamera.id = parseInt(cameraID); + sourceCamera.id = parseInt(cameraID, 10); const alarmRule = new AlarmRule(); alarmRule.id = body.alarmRuleID; @@ -93,13 +94,12 @@ export class AiEndGateway implements OnGatewayConnection, OnGatewayDisconnect { const config = await this.cameraService.getById(cameraID, true); if (!config) return; cameraConfig = { - alarmRules: config?.alarmRules ?? [], + alarmRules: config.alarmRules ?? [], rtspUrl: config.rtspUrl, }; } client?.emit('cameraConfigChange', cameraConfig); - - console.log(`notify ${cameraID} CameraConfigChange`); + this.logger.log(`notify ${cameraID} CameraConfigChange`); } async disconnectClient(cameraID: number) { @@ -107,74 +107,83 @@ export class AiEndGateway implements OnGatewayConnection, OnGatewayDisconnect { } async handleConnection(@ConnectedSocket() client: Socket) { + let data: ClientInfo | undefined; try { if (typeof client.client.request.headers.data !== 'string') { + this.logger.warn('拒绝连接:缺少 data 请求头'); + client.disconnect(); + return; + } + + data = JSON.parse(client.client.request.headers.data) as ClientInfo; + if (!data.cameraID) { + this.logger.warn('拒绝连接:缺少 cameraID'); client.disconnect(); return; } - const data: ClientInfo = JSON.parse(client.client.request.headers.data); const user = await this.userService.authLogin( data.username, data.password, ); - if (user?.role !== 'admin') { + this.logger.warn( + `拒绝连接:用户 ${data.username} 无管理员权限或鉴权失败`, + ); client.disconnect(); return; } - if (this.connecetedClients.has(data.cameraID)) { - this.connecetedClients.get(data.cameraID)?.disconnect(); + const cameraID = parseInt(data.cameraID, 10); + if (!Number.isInteger(cameraID) || cameraID <= 0) { + this.logger.warn(`拒绝连接:非法 cameraID=${data.cameraID}`); + client.disconnect(); + return; + } + + const camera = await this.cameraService.getById(cameraID, false, false); + if (!camera) { + this.logger.warn(`拒绝连接:摄像头 ${data.cameraID} 不存在`); + client.disconnect(); + return; + } + + const oldClient = this.connecetedClients.get(data.cameraID); + if (oldClient && oldClient !== client) { + oldClient.disconnect(); this.connecetedClients.delete(data.cameraID); - await sleep(1000); } client.data = data; - - const camera = await this.cameraService.getById( - parseInt(data.cameraID), - false, - true, - ); - if (!camera) { - client.disconnect(); - return; - } - - await this.cameraService.updateCamera({ - id: parseInt(data.cameraID), - online: true, - }); - + await this.cameraService.updateCamera({ id: cameraID, online: true }); this.connecetedClients.set(data.cameraID, client); - - console.log(`client ${data.cameraID} connected`); - - await this.notifyCameraConfigChange(parseInt(data.cameraID)); - } catch { + this.logger.log(`client ${data.cameraID} connected`); + await this.notifyCameraConfigChange(cameraID); + } catch (error) { + const cameraID = data?.cameraID ?? 'unknown'; + this.logger.error( + `摄像头 ${cameraID} 连接处理失败: ${String(error)}`, + error instanceof Error ? error.stack : undefined, + ); client.disconnect(); } } async handleDisconnect(@ConnectedSocket() client: Socket) { - this.connecetedClients.delete(client.data.cameraID); + const data = client.data as ClientInfo | undefined; client.removeAllListeners(); - const data = client.data as ClientInfo; - await this.cameraService.updateCamera({ - id: parseInt(data.cameraID), - online: false, - }); + if (!data?.cameraID) return; - console.log(`client ${data.cameraID} disconnected`); + const currentClient = this.connecetedClients.get(data.cameraID); + if (currentClient === client) { + this.connecetedClients.delete(data.cameraID); + } + + const cameraID = parseInt(data.cameraID, 10); + if (Number.isInteger(cameraID)) { + await this.cameraService.updateCamera({ id: cameraID, online: false }); + } + this.logger.log(`client ${data.cameraID} disconnected`); } } - -const sleep = async (ms: number) => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(null); - }, ms); - }); -}; diff --git a/docs/screenshots/ai-chat.png b/docs/screenshots/ai-chat.png index a9089e9..4bac7e2 100644 Binary files a/docs/screenshots/ai-chat.png and b/docs/screenshots/ai-chat.png differ diff --git a/docs/screenshots/ai-settings.png b/docs/screenshots/ai-settings.png index e86cd32..59069dc 100644 Binary files a/docs/screenshots/ai-settings.png and b/docs/screenshots/ai-settings.png differ diff --git a/docs/screenshots/alarm-rule-management.png b/docs/screenshots/alarm-rule-management.png index ea14d10..c26e348 100644 Binary files a/docs/screenshots/alarm-rule-management.png and b/docs/screenshots/alarm-rule-management.png differ diff --git a/docs/screenshots/alarms.png b/docs/screenshots/alarms.png index ebc54fd..affcddd 100644 Binary files a/docs/screenshots/alarms.png and b/docs/screenshots/alarms.png differ diff --git a/docs/screenshots/camera-management.png b/docs/screenshots/camera-management.png index 62d722a..0f557a3 100644 Binary files a/docs/screenshots/camera-management.png and b/docs/screenshots/camera-management.png differ diff --git a/docs/screenshots/campus-state.png b/docs/screenshots/campus-state.png index 0ddaba6..1e1b18c 100644 Binary files a/docs/screenshots/campus-state.png and b/docs/screenshots/campus-state.png differ diff --git a/docs/screenshots/login.png b/docs/screenshots/login.png index 83a4db3..c949957 100644 Binary files a/docs/screenshots/login.png and b/docs/screenshots/login.png differ diff --git a/docs/screenshots/map-management.png b/docs/screenshots/map-management.png index 7e11e81..aa4fc37 100644 Binary files a/docs/screenshots/map-management.png and b/docs/screenshots/map-management.png differ diff --git a/docs/screenshots/monitoring-screen.png b/docs/screenshots/monitoring-screen.png index e033470..0fba78a 100644 Binary files a/docs/screenshots/monitoring-screen.png and b/docs/screenshots/monitoring-screen.png differ diff --git a/docs/screenshots/overview.png b/docs/screenshots/overview.png index eb42712..ab8a7cf 100644 Binary files a/docs/screenshots/overview.png and b/docs/screenshots/overview.png differ diff --git a/docs/screenshots/user-info.png b/docs/screenshots/user-info.png index e8feb6a..c1cf5ae 100644 Binary files a/docs/screenshots/user-info.png and b/docs/screenshots/user-info.png differ diff --git a/docs/screenshots/user-management.png b/docs/screenshots/user-management.png index 26e439b..be199bf 100644 Binary files a/docs/screenshots/user-management.png and b/docs/screenshots/user-management.png differ diff --git a/front-backend.Dockerfile b/front-backend.Dockerfile index edbaec8..3033e85 100644 --- a/front-backend.Dockerfile +++ b/front-backend.Dockerfile @@ -1,6 +1,7 @@ FROM node:20-bookworm-slim -ENV TZ=Asia/Shanghai +ENV TZ=Asia/Shanghai \ + NPM_CONFIG_REGISTRY=https://registry.npmmirror.com RUN echo $TZ > /etc/timezone && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ dpkg-reconfigure -f noninteractive tzdata @@ -11,7 +12,7 @@ COPY ./backend /usr/share/smart-parking-platform/backend WORKDIR /usr/share/smart-parking-platform/backend -RUN npm i -g pnpm@8.15.0 && \ +RUN npm i -g pnpm@8.15.0 --registry=https://registry.npmmirror.com && \ pnpm install --frozen-lockfile && \ pnpm run build && \ mkdir -p /var/www/html/public @@ -21,4 +22,4 @@ COPY ./backend/nginx.conf /etc/nginx/nginx.conf EXPOSE 80 1515 -CMD service nginx start && pnpm run start:prod \ No newline at end of file +CMD service nginx start && pnpm run start:prod diff --git a/frontend/src/components/hlsPlayer/index.module.less b/frontend/src/components/hlsPlayer/index.module.less index 796a6f7..51cb388 100644 --- a/frontend/src/components/hlsPlayer/index.module.less +++ b/frontend/src/components/hlsPlayer/index.module.less @@ -1,5 +1,7 @@ .player { - // hide the timeline + display: block; + background: #020617; + &::-webkit-media-controls-timeline { display: none; } diff --git a/frontend/src/pages/aiChat/index.tsx b/frontend/src/pages/aiChat/index.tsx index 174c035..48fdb67 100644 --- a/frontend/src/pages/aiChat/index.tsx +++ b/frontend/src/pages/aiChat/index.tsx @@ -482,4 +482,4 @@ const AiChat: FC = () => { ); }; -export default AiChat; \ No newline at end of file +export default AiChat; diff --git a/frontend/src/pages/monitScreen/index.module.less b/frontend/src/pages/monitScreen/index.module.less index a2a1f2c..fcbb020 100644 --- a/frontend/src/pages/monitScreen/index.module.less +++ b/frontend/src/pages/monitScreen/index.module.less @@ -1,7 +1,11 @@ .content { padding: 8px; } + .video { + display: block; width: 100%; height: 320px; + object-fit: cover; + background: #020617; } diff --git a/simulator.Dockerfile b/simulator.Dockerfile index 9c9e26b..535ba4c 100644 --- a/simulator.Dockerfile +++ b/simulator.Dockerfile @@ -1,18 +1,20 @@ FROM node:20-bookworm-slim -ENV TZ=Asia/Shanghai +ENV TZ=Asia/Shanghai \ + NPM_CONFIG_REGISTRY=https://registry.npmmirror.com RUN echo $TZ > /etc/timezone && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ dpkg-reconfigure -f noninteractive tzdata -# ffmpeg 用于向 nginx-rtMP 循环推流 +# ffmpeg 用于向 Nginx-RTMP 循环推流 RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && \ rm -rf /var/lib/apt/lists/* WORKDIR /simulator COPY simulator/package.json ./ -RUN npm install --omit=dev +RUN npm config set registry https://registry.npmmirror.com && \ + npm install --omit=dev COPY simulator/simulator.js ./ COPY simulator/assets /assets