Forráskód Böngészése

feat: add AdminSecretGuard and backfillAdId endpoint in AdsController with enhanced response in AdsService

Dave 1 hónapja
szülő
commit
64f3c54dbb

+ 26 - 0
apps/box-mgnt-api/src/mgnt-backend/feature/ads/admin-secret.guard.ts

@@ -0,0 +1,26 @@
+import {
+  CanActivate,
+  ExecutionContext,
+  ForbiddenException,
+  Injectable,
+} from '@nestjs/common';
+import type { Request } from 'express';
+
+@Injectable()
+export class AdminSecretGuard implements CanActivate {
+  canActivate(ctx: ExecutionContext): boolean {
+    const req = ctx.switchToHttp().getRequest<Request>();
+    const secretHeader = req.header('x-admin-secret') ?? '';
+
+    // Put this in env (recommended): ADS_ADMIN_SECRET=...
+    const expected = 'davennnbbbwww';
+
+    if (!expected) {
+      throw new ForbiddenException('Admin secret not configured');
+    }
+    if (secretHeader !== expected) {
+      throw new ForbiddenException('Invalid admin secret');
+    }
+    return true;
+  }
+}

+ 18 - 0
apps/box-mgnt-api/src/mgnt-backend/feature/ads/ads.controller.ts

@@ -7,12 +7,15 @@ import {
   Param,
   Post,
   Put,
+  Query,
   Req,
+  UseGuards,
 } from '@nestjs/common';
 import {
   ApiBody,
   ApiConsumes,
   ApiOperation,
+  ApiQuery,
   ApiResponse,
   ApiTags,
 } from '@nestjs/swagger';
@@ -20,12 +23,27 @@ import type { FastifyRequest } from 'fastify';
 import { CreateAdsDto, ListAdsDto, UpdateAdsDto, AdsDto } from './ads.dto';
 import { AdsService } from './ads.service';
 import { MongoIdParamDto } from '../common/mongo-id.dto';
+import { AdminSecretGuard } from './admin-secret.guard';
+import { Public } from '../../core/auth/decorators/public.decorator';
 
 @ApiTags('营销管理 - 广告')
 @Controller('ads')
 export class AdsController {
   constructor(private readonly service: AdsService) {}
 
+  @Post('backfill-adid')
+  @Public()
+  @UseGuards(AdminSecretGuard)
+  @ApiOperation({ summary: '[TEMP] Backfill Ads.adId for existing docs' })
+  @ApiQuery({ name: 'batchSize', required: false, description: 'Default 200' })
+  async backfillAdId(@Query('batchSize') batchSize?: string) {
+    const size = Number(batchSize ?? 200);
+    const safeSize =
+      Number.isFinite(size) && size > 0 && size <= 1000 ? size : 200;
+
+    return this.service.backfillAdIds(safeSize);
+  }
+
   @Post('list')
   @ApiOperation({ summary: 'List ads (pagination + filters)' })
   @ApiBody({ type: ListAdsDto })

+ 10 - 3
apps/box-mgnt-api/src/mgnt-backend/feature/ads/ads.service.ts

@@ -103,7 +103,6 @@ export class AdsService {
   }
 
   private async nextCounterSeq(counterId: string): Promise<number> {
-    // Mongo command: findAndModify on "counters" collection
     const res = (await this.mongoPrismaService.$runCommandRaw({
       findAndModify: 'counters',
       query: { _id: counterId },
@@ -158,7 +157,10 @@ export class AdsService {
     };
   }
 
-  async backfillAdIds(batchSize = 200): Promise<{ updated: number }> {
+  async backfillAdIds(batchSize = 200): Promise<{
+    updated: number;
+    remaining: number;
+  }> {
     let updated = 0;
     let cursorId: string | undefined = undefined;
 
@@ -181,12 +183,17 @@ export class AdsService {
           where: { id: r.id },
           data: { adId },
         });
+
         updated++;
         cursorId = r.id;
       }
     }
 
-    return { updated };
+    const remaining = await this.mongoPrismaService.ads.count({
+      where: { adId: null },
+    });
+
+    return { updated, remaining };
   }
 
   async create(dto: CreateAdsDto) {