Просмотр исходного кода

feat(auth): implement user login functionality with RabbitMQ integration

- Added AuthController and AuthService for handling user login requests.
- Integrated JWT for access token generation with configurable expiration.
- Implemented RabbitMQ publisher to send user login events.
- Updated environment configuration files to include RabbitMQ settings.
- Created LoginDto for validating login request payloads.
- Added response and error handling interceptors for consistent API responses.
- Removed unused correlation and logging interceptors from common library.
Dave 4 месяцев назад
Родитель
Сommit
696bbdb507
27 измененных файлов с 355 добавлено и 206 удалено
  1. 7 1
      .env.app
  2. 7 1
      .env.app.dev
  3. 10 3
      .env.app.test
  4. 6 0
      .env.mgnt
  5. 19 0
      apps/box-app-api/src/app.module.ts
  6. 51 0
      apps/box-app-api/src/feature/auth/auth.controller.ts
  7. 28 0
      apps/box-app-api/src/feature/auth/auth.module.ts
  8. 65 0
      apps/box-app-api/src/feature/auth/auth.service.ts
  9. 31 0
      apps/box-app-api/src/feature/auth/login.dto.ts
  10. 104 0
      apps/box-app-api/src/rabbitmq/rabbitmq-publisher.service.ts
  11. 11 0
      apps/box-app-api/src/rabbitmq/rabbitmq.module.ts
  12. 1 1
      apps/box-stats-api/src/feature/rabbitmq/rabbitmq-consumer.service.ts
  13. 0 10
      apps/box-stats-api/src/feature/user-login-history/user-login-event.dto.ts
  14. 1 1
      apps/box-stats-api/src/feature/user-login-history/user-login-history.service.ts
  15. 14 0
      libs/common/src/events/user-login-event.dto.ts
  16. 0 5
      libs/common/src/interceptors/correlation.interceptor.d.ts
  17. 0 31
      libs/common/src/interceptors/correlation.interceptor.js
  18. 0 1
      libs/common/src/interceptors/correlation.interceptor.js.map
  19. 0 6
      libs/common/src/interceptors/logging.interceptor.d.ts
  20. 0 32
      libs/common/src/interceptors/logging.interceptor.js
  21. 0 1
      libs/common/src/interceptors/logging.interceptor.js.map
  22. 0 14
      libs/common/src/interceptors/operation-log.interceptor.d.ts
  23. 0 60
      libs/common/src/interceptors/operation-log.interceptor.js
  24. 0 1
      libs/common/src/interceptors/operation-log.interceptor.js.map
  25. 0 5
      libs/common/src/interceptors/response.interceptor.d.ts
  26. 0 32
      libs/common/src/interceptors/response.interceptor.js
  27. 0 1
      libs/common/src/interceptors/response.interceptor.js.map

+ 7 - 1
.env.app

@@ -25,4 +25,10 @@ APP_CORS_ORIGIN=*
 
 # JWT
 JWT_SECRET=047df8aaa3d17dc1173c5a9a3052ba66c2b0bd96937147eb643319a0c90d132f
-JWT_ACCESS_TOKEN_TTL=43200
+JWT_ACCESS_TOKEN_TTL=43200
+
+# RabbitMQ Config
+RABBITMQ_URL="amqp://boxrabbit:BoxRabbit%232025@localhost:5672"
+RABBITMQ_LOGIN_EXCHANGE="stats.user"
+RABBITMQ_LOGIN_QUEUE="stats.user.login.q"
+RABBITMQ_LOGIN_ROUTING_KEY="user.login"

+ 7 - 1
.env.app.dev

@@ -23,4 +23,10 @@ APP_CORS_ORIGIN=*
 
 # JWT
 JWT_SECRET=047df8aaa3d17dc1173c5a9a3052ba66c2b0bd96937147eb643319a0c90d132f
-JWT_ACCESS_TOKEN_TTL=43200
+JWT_ACCESS_TOKEN_TTL=43200
+
+# RabbitMQ Config
+RABBITMQ_URL="amqp://boxrabbit:BoxRabbit%232025@localhost:5672"
+RABBITMQ_LOGIN_EXCHANGE="stats.user"
+RABBITMQ_LOGIN_QUEUE="stats.user.login.q"
+RABBITMQ_LOGIN_ROUTING_KEY="user.login"

+ 10 - 3
.env.app.test

@@ -2,10 +2,11 @@
 APP_ENV=test
 
 # Prisma Config
-MONGO_URL="mongodb://boxuser:dwR%3D%29whu2Ze@localhost:27017/box_admin?authSource=admin"
+# Dave local
+# MONGO_URL="mongodb://admin:ZXcv%21%21996@localhost:27017/box_admin?authSource=admin&replicaSet=rs0"
 
 # office dev env
-# MONGO_URL="mongodb://msAdmin:Fl1%2A29MJe%26jLvj@localhost:27017/box_admin?authSource=admin"
+MONGO_URL="mongodb://msAdmin:Fl1%2A29MJe%26jLvj@192.168.0.100:27017/box_admin?authSource=admin&replicaSet=rs0"
 
 # Redis Config
 REDIS_HOST=127.0.0.1
@@ -22,4 +23,10 @@ APP_CORS_ORIGIN=*
 
 # JWT
 JWT_SECRET=047df8aaa3d17dc1173c5a9a3052ba66c2b0bd96937147eb643319a0c90d132f
-JWT_ACCESS_TOKEN_TTL=43200
+JWT_ACCESS_TOKEN_TTL=43200
+
+# RabbitMQ Config
+RABBITMQ_URL="amqp://boxrabbit:BoxRabbit%232025@localhost:5672"
+RABBITMQ_LOGIN_EXCHANGE="stats.user"
+RABBITMQ_LOGIN_QUEUE="stats.user.login.q"
+RABBITMQ_LOGIN_ROUTING_KEY="user.login"

+ 6 - 0
.env.mgnt

@@ -24,6 +24,12 @@ REDIS_PASSWORD=
 REDIS_DB=0
 REDIS_KEY_PREFIX=box:
 
+# RabbitMQ Config
+RABBITMQ_URL="amqp://boxrabbit:BoxRabbit%232025@localhost:5672"
+RABBITMQ_LOGIN_EXCHANGE="stats.user"
+RABBITMQ_LOGIN_QUEUE="stats.user.login.q"
+RABBITMQ_LOGIN_ROUTING_KEY="user.login"
+
 # App set to 0.0.0.0 for local LAN access
 APP_HOST=0.0.0.0
 APP_PORT=3300

+ 19 - 0
apps/box-app-api/src/app.module.ts

@@ -1,6 +1,9 @@
 // apps/box-app-api/src/app.module.ts
 import { Module } from '@nestjs/common';
 import { ConfigModule, ConfigService } from '@nestjs/config';
+import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core';
+import { ResponseInterceptor } from '@box/common/interceptors/response.interceptor';
+import { HttpExceptionFilter } from '@box/common/filters/http-exception.filter';
 import { RedisCacheModule } from './redis/redis-cache.module';
 import { HealthModule } from './health/health.module';
 import { PrismaMongoModule } from './prisma/prisma-mongo.module';
@@ -9,6 +12,8 @@ import { AdModule } from './feature/ads/ad.module';
 import { HomepageModule } from './feature/homepage/homepage.module';
 import { SysParamsModule } from './feature/sys-params/sys-params.module';
 import { RedisModule } from '@box/db/redis/redis.module';
+import { RabbitmqModule } from './rabbitmq/rabbitmq.module';
+import { AuthModule } from './feature/auth/auth.module';
 
 @Module({
   imports: [
@@ -32,6 +37,9 @@ import { RedisModule } from '@box/db/redis/redis.module';
       }),
     }),
 
+    // RabbitMQ publisher for user login events
+    RabbitmqModule,
+
     // Global Redis cache
     RedisCacheModule,
 
@@ -39,11 +47,22 @@ import { RedisModule } from '@box/db/redis/redis.module';
     PrismaMongoModule,
 
     // Simple health endpoint
+    AuthModule,
     HealthModule,
     VideoModule,
     AdModule,
     HomepageModule,
     SysParamsModule,
   ],
+  providers: [
+    {
+      provide: APP_INTERCEPTOR,
+      useClass: ResponseInterceptor,
+    },
+    {
+      provide: APP_FILTER,
+      useClass: HttpExceptionFilter,
+    },
+  ],
 })
 export class AppModule {}

+ 51 - 0
apps/box-app-api/src/feature/auth/auth.controller.ts

@@ -0,0 +1,51 @@
+// apps/box-app-api/src/auth/auth.controller.ts
+import { Body, Controller, Post, Req } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { Request } from 'express';
+import { LoginDto } from './login.dto';
+import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
+
+@ApiTags('认证')
+@Controller('auth')
+export class AuthController {
+  constructor(private readonly authService: AuthService) {}
+
+  @Post('login')
+  @ApiOperation({
+    summary: '用户登录',
+    description: '用户登录接口,返回访问令牌(Access Token)',
+  })
+  @ApiResponse({
+    status: 200,
+    description: '登录成功,返回访问令牌',
+    schema: {
+      type: 'object',
+      properties: {
+        accessToken: {
+          type: 'string',
+          example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
+        },
+      },
+    },
+  })
+  @ApiResponse({ status: 400, description: '请求参数错误' })
+  async login(@Body() body: LoginDto, @Req() req: Request) {
+    const ip =
+      (req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() ||
+      (req.headers['x-real-ip'] as string) ||
+      req.ip;
+
+    const userAgent = req.headers['user-agent'];
+
+    const result = await this.authService.login({
+      uid: body.uid,
+      ip,
+      userAgent,
+      appVersion: body.appVersion,
+      os: body.os,
+      // add any other auth-related fields as needed
+    });
+
+    return result;
+  }
+}

+ 28 - 0
apps/box-app-api/src/feature/auth/auth.module.ts

@@ -0,0 +1,28 @@
+import { Module } from '@nestjs/common';
+import { JwtModule } from '@nestjs/jwt';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { PrismaMongoModule } from '../../prisma/prisma-mongo.module';
+import { RabbitmqModule } from '../../rabbitmq/rabbitmq.module';
+import { AuthController } from './auth.controller';
+import { AuthService } from './auth.service';
+
+@Module({
+  imports: [
+    PrismaMongoModule,
+    RabbitmqModule,
+    JwtModule.registerAsync({
+      imports: [ConfigModule],
+      inject: [ConfigService],
+      useFactory: (configService: ConfigService) => ({
+        secret: configService.get<string>('JWT_SECRET') || 'default-secret-key',
+        signOptions: {
+          expiresIn: configService.get<string>('JWT_EXPIRES_IN') || '7d',
+        },
+      }),
+    }),
+  ],
+  controllers: [AuthController],
+  providers: [AuthService],
+  exports: [AuthService],
+})
+export class AuthModule {}

+ 65 - 0
apps/box-app-api/src/feature/auth/auth.service.ts

@@ -0,0 +1,65 @@
+// apps/box-app-api/src/auth/auth.service.ts
+import { Injectable } from '@nestjs/common';
+import { JwtService } from '@nestjs/jwt';
+import { UserLoginEventPayload } from '@box/common/events/user-login-event.dto';
+import { RabbitmqPublisherService } from '../../rabbitmq/rabbitmq-publisher.service';
+
+@Injectable()
+export class AuthService {
+  constructor(
+    private readonly jwtService: JwtService,
+    private readonly rabbitmqPublisher: RabbitmqPublisherService,
+  ) {}
+
+  async login(params: {
+    uid: string;
+    ip: string;
+    userAgent?: string;
+    appVersion?: string;
+    os?: string;
+    // plus whatever you need like account, password, etc.
+  }): Promise<{ accessToken: string }> {
+    const { uid, ip, userAgent, appVersion, os } = params;
+
+    // 1) Your existing auth logic (validate user, etc.)
+    // const user = await this.validateUser(...);
+
+    // 2) Generate tokenId (jti) and JWT
+    const tokenId = crypto.randomUUID(); // Node 18+; or use uuid library
+
+    const now = Date.now(); // number (ms since epoch)
+
+    const payload = {
+      sub: uid, // or your userId
+      uid,
+      jti: tokenId,
+      // ... other claims
+    };
+
+    const accessToken = await this.jwtService.signAsync(payload);
+
+    // 3) Build login event payload
+    const loginEvent: UserLoginEventPayload = {
+      uid,
+      ip,
+      userAgent,
+      appVersion,
+      os,
+      tokenId,
+      loginAt: now,
+    };
+
+    // 4) Fire-and-forget publish (but wait for broker confirm)
+    try {
+      await this.rabbitmqPublisher.publishUserLogin(loginEvent);
+    } catch (error) {
+      // Decide your policy:
+      //   - Either just log and continue (login succeeds even if stats fail),
+      //   - Or throw to fail login if stats are critical.
+      // For now, let's log and continue.
+      // If you want stricter behaviour, re-throw.
+    }
+
+    return { accessToken };
+  }
+}

+ 31 - 0
apps/box-app-api/src/feature/auth/login.dto.ts

@@ -0,0 +1,31 @@
+import { IsNotEmpty, IsString, IsOptional } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class LoginDto {
+  @ApiProperty({
+    description: '设备唯一标识符(Device ID)',
+    example: 'device-123-abc-xyz',
+  })
+  @IsNotEmpty()
+  @IsString()
+  uid: string;
+
+  @ApiProperty({
+    description: '应用版本号',
+    example: '1.0.0',
+    required: false,
+  })
+  @IsOptional()
+  @IsString()
+  appVersion?: string;
+
+  @ApiProperty({
+    description: '操作系统类型',
+    example: 'Android',
+    required: false,
+  })
+  @IsOptional()
+  @IsString()
+  os?: string;
+  // plus account/password etc.
+}

+ 104 - 0
apps/box-app-api/src/rabbitmq/rabbitmq-publisher.service.ts

@@ -0,0 +1,104 @@
+// apps/box-app-api/src/rabbitmq/rabbitmq-publisher.service.ts
+import {
+  Injectable,
+  Logger,
+  OnModuleDestroy,
+  OnModuleInit,
+} from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { Connection, Channel, ConfirmChannel } from 'amqplib';
+import * as amqp from 'amqplib';
+import { UserLoginEventPayload } from '@box/common/events/user-login-event.dto';
+
+@Injectable()
+export class RabbitmqPublisherService implements OnModuleInit, OnModuleDestroy {
+  private readonly logger = new Logger(RabbitmqPublisherService.name);
+
+  private connection?: Connection;
+  private channel?: ConfirmChannel;
+
+  private exchange!: string;
+  private routingKeyLogin!: string;
+
+  constructor(private readonly config: ConfigService) {}
+
+  async onModuleInit(): Promise<void> {
+    const url = this.config.get<string>('RABBITMQ_URL');
+    this.exchange =
+      this.config.get<string>('RABBITMQ_LOGIN_EXCHANGE') ?? 'stats.user';
+    this.routingKeyLogin =
+      this.config.get<string>('RABBITMQ_LOGIN_ROUTING_KEY') ?? 'user.login';
+
+    if (!url) {
+      this.logger.error(
+        'RABBITMQ_URL is not set. Login stats will NOT be published.',
+      );
+      return;
+    }
+
+    this.logger.log(`Connecting to RabbitMQ at ${url} ...`);
+
+    this.connection = await amqp.connect(url);
+
+    // Use a confirm channel so we know when broker has accepted the message
+    this.channel = await this.connection.createConfirmChannel();
+
+    await this.channel.assertExchange(this.exchange, 'topic', {
+      durable: true,
+    });
+
+    this.logger.log(
+      `RabbitMQ publisher ready. exchange="${this.exchange}", loginRoutingKey="${this.routingKeyLogin}"`,
+    );
+  }
+
+  async onModuleDestroy(): Promise<void> {
+    try {
+      await this.channel?.close();
+      await this.connection?.close();
+    } catch (error: any) {
+      this.logger.error('Error while closing RabbitMQ connection', error.stack);
+    }
+  }
+
+  /**
+   * Publish a user.login event.
+   * We wait for the confirm callback, so we know if the broker accepted or rejected the message.
+   */
+  async publishUserLogin(event: UserLoginEventPayload): Promise<void> {
+    if (!this.channel) {
+      this.logger.warn(
+        'RabbitMQ channel not ready. Skipping user.login publish.',
+      );
+      return;
+    }
+
+    const payloadBuffer = Buffer.from(JSON.stringify(event));
+
+    return new Promise((resolve, reject) => {
+      this.channel!.publish(
+        this.exchange,
+        this.routingKeyLogin,
+        payloadBuffer,
+        {
+          persistent: true,
+          contentType: 'application/json',
+        },
+        (err) => {
+          if (err) {
+            this.logger.error(
+              `Failed to publish user.login event for uid=${event.uid}: ${err.message}`,
+              err.stack,
+            );
+            return reject(err);
+          }
+          // Broker confirmed the message
+          this.logger.debug(
+            `Published user.login event for uid=${event.uid} to exchange=${this.exchange} routingKey=${this.routingKeyLogin}`,
+          );
+          resolve();
+        },
+      );
+    });
+  }
+}

+ 11 - 0
apps/box-app-api/src/rabbitmq/rabbitmq.module.ts

@@ -0,0 +1,11 @@
+// apps/box-app-api/src/rabbitmq/rabbitmq.module.ts
+import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
+import { RabbitmqPublisherService } from './rabbitmq-publisher.service';
+
+@Module({
+  imports: [ConfigModule],
+  providers: [RabbitmqPublisherService],
+  exports: [RabbitmqPublisherService],
+})
+export class RabbitmqModule {}

+ 1 - 1
apps/box-stats-api/src/feature/rabbitmq/rabbitmq-consumer.service.ts

@@ -8,7 +8,7 @@ import { ConfigService } from '@nestjs/config';
 import { Connection, Channel, ConsumeMessage } from 'amqplib';
 import * as amqp from 'amqplib';
 import { UserLoginHistoryService } from '../user-login-history/user-login-history.service';
-import { UserLoginEventPayload } from '../user-login-history/user-login-event.dto';
+import { UserLoginEventPayload } from '@box/common/events/user-login-event.dto';
 
 @Injectable()
 export class RabbitmqConsumerService implements OnModuleInit, OnModuleDestroy {

+ 0 - 10
apps/box-stats-api/src/feature/user-login-history/user-login-event.dto.ts

@@ -1,10 +0,0 @@
-// This is the shape that box-app-api will publish to RabbitMQ
-export interface UserLoginEventPayload {
-  uid: string;
-  ip: string;
-  userAgent?: string;
-  appVersion?: string;
-  os?: string;
-  tokenId?: string;
-  loginAt: number | bigint; // epoch millis (will be stored as BigInt)
-}

+ 1 - 1
apps/box-stats-api/src/feature/user-login-history/user-login-history.service.ts

@@ -1,5 +1,5 @@
 import { Injectable, Logger } from '@nestjs/common';
-import { UserLoginEventPayload } from './user-login-event.dto';
+import { UserLoginEventPayload } from '@box/common/events/user-login-event.dto';
 import { PrismaMongoService } from '../../prisma/prisma-mongo.service';
 
 @Injectable()

+ 14 - 0
libs/common/src/events/user-login-event.dto.ts

@@ -0,0 +1,14 @@
+// libs/common/src/events/user-login-event.dto.ts
+
+export interface UserLoginEventPayload {
+  uid: string; // device ID
+  ip: string; // login IP
+  userAgent?: string; // optional, but useful
+  appVersion?: string; // optional
+  os?: string; // iOS / Android / Browser / Web
+
+  tokenId?: string; // JWT jti or session token ID (optional for now)
+
+  // epoch millis; will be stored as BigInt in Mongo
+  loginAt: number | bigint;
+}

+ 0 - 5
libs/common/src/interceptors/correlation.interceptor.d.ts

@@ -1,5 +0,0 @@
-import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
-import { Observable } from 'rxjs';
-export declare class CorrelationInterceptor implements NestInterceptor {
-    intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
-}

+ 0 - 31
libs/common/src/interceptors/correlation.interceptor.js

@@ -1,31 +0,0 @@
-"use strict";
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.CorrelationInterceptor = void 0;
-const common_1 = require("@nestjs/common");
-const crypto_1 = require("crypto");
-const operators_1 = require("rxjs/operators");
-let CorrelationInterceptor = class CorrelationInterceptor {
-    intercept(context, next) {
-        const ctx = context.switchToHttp();
-        const req = ctx.getRequest();
-        const res = ctx.getResponse();
-        const correlationId = req.headers['x-request-id'] ||
-            req.headers['x-correlation-id'] ||
-            (0, crypto_1.randomUUID)();
-        req.correlationId = correlationId;
-        res.header('x-request-id', correlationId);
-        res.header('x-correlation-id', correlationId);
-        return next.handle().pipe((0, operators_1.tap)(() => { }));
-    }
-};
-exports.CorrelationInterceptor = CorrelationInterceptor;
-exports.CorrelationInterceptor = CorrelationInterceptor = __decorate([
-    (0, common_1.Injectable)()
-], CorrelationInterceptor);
-//# sourceMappingURL=correlation.interceptor.js.map

+ 0 - 1
libs/common/src/interceptors/correlation.interceptor.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"correlation.interceptor.js","sourceRoot":"","sources":["correlation.interceptor.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAKwB;AACxB,mCAAoC;AAGpC,8CAAqC;AAG9B,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACjC,SAAS,CAAC,OAAyB,EAAE,IAAiB;QACpD,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,EAAkB,CAAC;QAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,EAAgB,CAAC;QAG5C,MAAM,aAAa,GAChB,GAAG,CAAC,OAAO,CAAC,cAAc,CAAY;YACtC,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAY;YAC3C,IAAA,mBAAU,GAAE,CAAC;QAGd,GAAW,CAAC,aAAa,GAAG,aAAa,CAAC;QAG3C,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QAC1C,GAAG,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAA,eAAG,EAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;CACF,CAAA;AArBY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;GACA,sBAAsB,CAqBlC"}

+ 0 - 6
libs/common/src/interceptors/logging.interceptor.d.ts

@@ -1,6 +0,0 @@
-import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
-import { Observable } from 'rxjs';
-export declare class LoggingInterceptor implements NestInterceptor {
-    private readonly logger;
-    intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
-}

+ 0 - 32
libs/common/src/interceptors/logging.interceptor.js

@@ -1,32 +0,0 @@
-"use strict";
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var LoggingInterceptor_1;
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.LoggingInterceptor = void 0;
-const common_1 = require("@nestjs/common");
-const operators_1 = require("rxjs/operators");
-let LoggingInterceptor = LoggingInterceptor_1 = class LoggingInterceptor {
-    constructor() {
-        this.logger = new common_1.Logger(LoggingInterceptor_1.name);
-    }
-    intercept(context, next) {
-        const req = context.switchToHttp().getRequest();
-        const content = `${req.method} -> ${req.url}`;
-        this.logger.log(`+++ 请求:${content}`);
-        const now = Date.now();
-        return next.handle().pipe((0, operators_1.tap)(() => {
-            const duration = Date.now() - now;
-            this.logger.log(`--- 响应:${content} +${duration}ms`);
-        }));
-    }
-};
-exports.LoggingInterceptor = LoggingInterceptor;
-exports.LoggingInterceptor = LoggingInterceptor = LoggingInterceptor_1 = __decorate([
-    (0, common_1.Injectable)()
-], LoggingInterceptor);
-//# sourceMappingURL=logging.interceptor.js.map

+ 0 - 1
libs/common/src/interceptors/logging.interceptor.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"logging.interceptor.js","sourceRoot":"","sources":["logging.interceptor.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAMwB;AAGxB,8CAAqC;AAG9B,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAAxB;QACY,WAAM,GAAG,IAAI,eAAM,CAAC,oBAAkB,CAAC,IAAI,CAAC,CAAC;IAchE,CAAC;IAZC,SAAS,CAAC,OAAyB,EAAE,IAAiB;QACpD,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAkB,CAAC;QAChE,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CACvB,IAAA,eAAG,EAAC,GAAG,EAAE;YACP,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAA;AAfY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;GACA,kBAAkB,CAe9B"}

+ 0 - 14
libs/common/src/interceptors/operation-log.interceptor.d.ts

@@ -1,14 +0,0 @@
-import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
-import { Reflector } from '@nestjs/core';
-import { Observable } from 'rxjs';
-import { ApiResponse } from '../interfaces/api-response.interface';
-import { IOperationLogger } from '../interfaces/operation-logger.interface';
-import { ExceptionService } from '../services/exception.service';
-export declare class OperationLogInterceptor implements NestInterceptor {
-    private readonly reflector;
-    private readonly operationLogger;
-    private readonly exceptionService;
-    constructor(reflector: Reflector, operationLogger: IOperationLogger | null, exceptionService: ExceptionService);
-    intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
-    log(context: ExecutionContext, data: ApiResponse<unknown>, status: boolean): Promise<void>;
-}

+ 0 - 60
libs/common/src/interceptors/operation-log.interceptor.js

@@ -1,60 +0,0 @@
-"use strict";
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var __metadata = (this && this.__metadata) || function (k, v) {
-    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
-};
-var __param = (this && this.__param) || function (paramIndex, decorator) {
-    return function (target, key) { decorator(target, key, paramIndex); }
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.OperationLogInterceptor = void 0;
-const common_1 = require("@nestjs/common");
-const core_1 = require("@nestjs/core");
-const operators_1 = require("rxjs/operators");
-const operation_log_decorator_1 = require("../decorators/operation-log.decorator");
-const operation_logger_interface_1 = require("../interfaces/operation-logger.interface");
-const exception_service_1 = require("../services/exception.service");
-let OperationLogInterceptor = class OperationLogInterceptor {
-    constructor(reflector, operationLogger, exceptionService) {
-        this.reflector = reflector;
-        this.operationLogger = operationLogger;
-        this.exceptionService = exceptionService;
-    }
-    intercept(context, next) {
-        return next.handle().pipe((0, operators_1.tap)({
-            next: (data) => {
-                this.log(context, data, true).then();
-            },
-            error: (e) => {
-                const responseObj = this.exceptionService.getHttpResponse(e);
-                this.log(context, responseObj, false).then();
-            },
-        }));
-    }
-    async log(context, data, status) {
-        if (!this.operationLogger) {
-            return;
-        }
-        const options = this.reflector.get(operation_log_decorator_1.OPERATION_LOG_KEY, context.getHandler());
-        if (!options)
-            return;
-        const className = context.getClass().name;
-        const methodKey = context.getHandler().name;
-        const callMethod = `${className}.${methodKey}()`;
-        const req = context.switchToHttp().getRequest();
-        await this.operationLogger.createLog(req, options, data, callMethod, status);
-    }
-};
-exports.OperationLogInterceptor = OperationLogInterceptor;
-exports.OperationLogInterceptor = OperationLogInterceptor = __decorate([
-    (0, common_1.Injectable)(),
-    __param(1, (0, common_1.Optional)()),
-    __param(1, (0, common_1.Inject)(operation_logger_interface_1.OPERATION_LOGGER)),
-    __metadata("design:paramtypes", [core_1.Reflector, Object, exception_service_1.ExceptionService])
-], OperationLogInterceptor);
-//# sourceMappingURL=operation-log.interceptor.js.map

+ 0 - 1
libs/common/src/interceptors/operation-log.interceptor.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"operation-log.interceptor.js","sourceRoot":"","sources":["operation-log.interceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAOwB;AACxB,uCAAyC;AAGzC,8CAAqC;AACrC,mFAG+C;AAE/C,yFAGkD;AAClD,qEAAiE;AAG1D,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAClC,YACmB,SAAoB,EAGpB,eAAwC,EACxC,gBAAkC;QAJlC,cAAS,GAAT,SAAS,CAAW;QAGpB,oBAAe,GAAf,eAAe,CAAyB;QACxC,qBAAgB,GAAhB,gBAAgB,CAAkB;IAClD,CAAC;IAEJ,SAAS,CAAC,OAAyB,EAAE,IAAiB;QACpD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CACvB,IAAA,eAAG,EAAC;YACF,IAAI,EAAE,CAAC,IAA0B,EAAE,EAAE;gBACnC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YACvC,CAAC;YACD,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE;gBACX,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;gBAC7D,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;YAC/C,CAAC;SACF,CAAC,CACH,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CACP,OAAyB,EACzB,IAA0B,EAC1B,MAAe;QAEf,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAE1B,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAChC,2CAAiB,EACjB,OAAO,CAAC,UAAU,EAAE,CACrB,CAAC;QACF,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC;QAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC;QAC5C,MAAM,UAAU,GAAG,GAAG,SAAS,IAAI,SAAS,IAAI,CAAC;QACjD,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAkB,CAAC;QAEhE,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,CAClC,GAAG,EACH,OAAO,EACP,IAAI,EACJ,UAAU,EACV,MAAM,CACP,CAAC;IACJ,CAAC;CACF,CAAA;AApDY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;IAIR,WAAA,IAAA,iBAAQ,GAAE,CAAA;IACV,WAAA,IAAA,eAAM,EAAC,6CAAgB,CAAC,CAAA;qCAFG,gBAAS,UAIF,oCAAgB;GAN1C,uBAAuB,CAoDnC"}

+ 0 - 5
libs/common/src/interceptors/response.interceptor.d.ts

@@ -1,5 +0,0 @@
-import { CallHandler, ExecutionContext, NestInterceptor, StreamableFile } from '@nestjs/common';
-import { ApiResponse } from '../interfaces/api-response.interface';
-export declare class ResponseInterceptor implements NestInterceptor {
-    intercept(_context: ExecutionContext, next: CallHandler): import("rxjs").Observable<ApiResponse<unknown> | StreamableFile>;
-}

+ 0 - 32
libs/common/src/interceptors/response.interceptor.js

@@ -1,32 +0,0 @@
-"use strict";
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.ResponseInterceptor = void 0;
-const common_1 = require("@nestjs/common");
-const operators_1 = require("rxjs/operators");
-let ResponseInterceptor = class ResponseInterceptor {
-    intercept(_context, next) {
-        return next.handle().pipe((0, operators_1.map)((data) => {
-            if (data instanceof common_1.StreamableFile) {
-                return data;
-            }
-            return {
-                error: '',
-                status: 1,
-                code: 'OK',
-                data,
-                timestamp: new Date().toISOString(),
-            };
-        }));
-    }
-};
-exports.ResponseInterceptor = ResponseInterceptor;
-exports.ResponseInterceptor = ResponseInterceptor = __decorate([
-    (0, common_1.Injectable)()
-], ResponseInterceptor);
-//# sourceMappingURL=response.interceptor.js.map

+ 0 - 1
libs/common/src/interceptors/response.interceptor.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"response.interceptor.js","sourceRoot":"","sources":["response.interceptor.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAMwB;AACxB,8CAAqC;AAI9B,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,SAAS,CAAC,QAA0B,EAAE,IAAiB;QACrD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CACvB,IAAA,eAAG,EAAC,CAAC,IAAI,EAAyC,EAAE;YAClD,IAAI,IAAI,YAAY,uBAAc,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO;gBACL,KAAK,EAAE,EAAE;gBACT,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,IAAI;gBACV,IAAI;gBACJ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC;QACJ,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAA;AAlBY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;GACA,mBAAmB,CAkB/B"}