import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, UseGuards, } from '@nestjs/common'; import { IsBoolean, IsIn, IsInt, IsOptional, IsString, Max, MaxLength, Min } from 'class-validator'; import { Type } from 'class-transformer'; import { HqAuthGuard } from '../../common/guards/hq-auth.guard'; import { HqPermissionGuard, RequireHqPermissions, } from '../../common/guards/hq-permission.guard'; import { CurrentUser } from '../../common/decorators/current-user.decorator'; import type { AuthUser } from '../../common/guards/jwt-auth.guard'; import { TestWhitelistService } from '../../common/test-whitelist/test-whitelist.service'; import { SystemConfigService } from '../../common/system-config/system-config.service'; class ListPhonesQueryDto { @IsOptional() @IsString() phone?: string; @IsOptional() @Type(() => Number) @IsInt() @Min(1) page?: number = 1; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(100) pageSize?: number = 20; } class AddPhoneDto { @IsString() phone!: string; @IsOptional() @IsString() @MaxLength(256) note?: string; } class UpdatePhoneDto { @IsOptional() @IsString() @MaxLength(256) note?: string | null; } class ListAccountsQueryDto { @IsIn(['user', 'store_account', 'partner', 'store', 'order']) type!: 'user' | 'store_account' | 'partner' | 'store' | 'order'; @IsOptional() @IsString() phone?: string; @IsOptional() @Type(() => Number) @IsInt() @Min(1) page?: number = 1; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(100) pageSize?: number = 20; } class UpdateMockFlagsDto { @IsOptional() @IsBoolean() mockSms?: boolean; @IsOptional() @IsBoolean() mockWechat?: boolean; @IsOptional() @IsBoolean() mockPay?: boolean; } @Controller('admin/test-whitelist') @UseGuards(HqAuthGuard, HqPermissionGuard) @RequireHqPermissions('test_whitelist') export class AdminTestWhitelistController { constructor( private readonly testWhitelist: TestWhitelistService, private readonly systemConfig: SystemConfigService, ) {} @Get('mock-flags') async getMockFlags() { const form = await this.systemConfig.getForm(['feature']); const v = form.values; return { mockSms: v.MOCK_SMS === 'true' || v.MOCK_SMS === '1', mockWechat: v.MOCK_WECHAT === 'true' || v.MOCK_WECHAT === '1', mockPay: v.MOCK_PAY === 'true' || v.MOCK_PAY === '1', }; } @Put('mock-flags') async updateMockFlags(@Body() dto: UpdateMockFlagsDto) { const values: Record = {}; if (dto.mockSms !== undefined) values.MOCK_SMS = String(dto.mockSms); if (dto.mockWechat !== undefined) values.MOCK_WECHAT = String(dto.mockWechat); if (dto.mockPay !== undefined) values.MOCK_PAY = String(dto.mockPay); if (Object.keys(values).length) { await this.systemConfig.update({ values }, ['feature']); } return this.getMockFlags(); } @Get('phones') listPhones(@Query() query: ListPhonesQueryDto) { return this.testWhitelist.listPhones(query); } @Post('phones') addPhone(@Body() dto: AddPhoneDto, @CurrentUser() actor: AuthUser) { return this.testWhitelist.addPhone({ phone: dto.phone, note: dto.note, createdByHqId: actor.actorId, }); } @Patch('phones/:id') updatePhone(@Param('id') id: string, @Body() dto: UpdatePhoneDto) { return this.testWhitelist.updatePhone(BigInt(id), { note: dto.note }); } @Delete('phones/:id') removePhone(@Param('id') id: string) { return this.testWhitelist.removePhone(BigInt(id)); } @Get('accounts') listAccounts(@Query() query: ListAccountsQueryDto) { return this.testWhitelist.listAccounts(query); } @Get('phones/:id/linked') linked(@Param('id') id: string) { return this.testWhitelist.linkedForPhoneId(BigInt(id)); } @Post('migrate-visibility') migrate(@CurrentUser() actor: AuthUser) { return this.testWhitelist.migrateVisibilityPhones(actor.actorId); } }