From 2aae3908a19644eb565e87763853acef4c50cbf8 Mon Sep 17 00:00:00 2001 From: suvajit Date: Tue, 3 Sep 2024 22:06:53 +0530 Subject: [PATCH] feat: legend threshold service backfill --- apps/service-auth/src/app.module.ts | 2 + .../src/tasks/tasks.controller.ts | 19 ++++ apps/service-auth/src/tasks/tasks.module.ts | 11 +++ apps/service-auth/src/tasks/tasks.service.ts | 90 +++++++++++++++++++ libs/auth/src/guards/api-key-guard.ts | 22 +++++ libs/entities/src/legend-attacks.entity.ts | 1 + 6 files changed, 145 insertions(+) create mode 100644 apps/service-auth/src/tasks/tasks.controller.ts create mode 100644 apps/service-auth/src/tasks/tasks.module.ts create mode 100644 apps/service-auth/src/tasks/tasks.service.ts create mode 100644 libs/auth/src/guards/api-key-guard.ts create mode 100644 libs/entities/src/legend-attacks.entity.ts diff --git a/apps/service-auth/src/app.module.ts b/apps/service-auth/src/app.module.ts index 7f0d26b..10f7dd2 100644 --- a/apps/service-auth/src/app.module.ts +++ b/apps/service-auth/src/app.module.ts @@ -12,6 +12,7 @@ import { LinksModule } from './links/links.module'; import { PlayersModule } from './players/players.module'; import { RostersModule } from './rosters/rosters.module'; import { ClashClientModule } from '@app/clash-client'; +import { TasksModule } from './tasks/tasks.module'; @Module({ imports: [ @@ -27,6 +28,7 @@ import { ClashClientModule } from '@app/clash-client'; RostersModule, ClashClientModule, DiscordOAuthModule, + TasksModule, // KafkaProducerModule.forRootAsync({ // useFactory: (configService: ConfigService) => { // return { diff --git a/apps/service-auth/src/tasks/tasks.controller.ts b/apps/service-auth/src/tasks/tasks.controller.ts new file mode 100644 index 0000000..c03c0b6 --- /dev/null +++ b/apps/service-auth/src/tasks/tasks.controller.ts @@ -0,0 +1,19 @@ +import { ApiKeyGuard } from '@app/auth/guards/api-key-guard'; +import { Controller, Get, Post, UseGuards } from '@nestjs/common'; +import { TasksService } from './tasks.service'; + +@UseGuards(ApiKeyGuard) +@Controller('tasks') +export class TasksController { + constructor(private tasksService: TasksService) {} + + @Get('/legend-trophy-threshold') + async getLegendThresholds() { + return this.tasksService.getTrophyThresholds(); + } + + @Post('/backfill-legend-trophy-threshold') + async backfillLegendThresholds() { + return this.tasksService.backfillLegendTrophyThreshold(); + } +} diff --git a/apps/service-auth/src/tasks/tasks.module.ts b/apps/service-auth/src/tasks/tasks.module.ts new file mode 100644 index 0000000..6fa6b18 --- /dev/null +++ b/apps/service-auth/src/tasks/tasks.module.ts @@ -0,0 +1,11 @@ +import { ClashClientModule } from '@app/clash-client'; +import { Module } from '@nestjs/common'; +import { TasksController } from './tasks.controller'; +import { TasksService } from './tasks.service'; + +@Module({ + imports: [ClashClientModule], + providers: [TasksService], + controllers: [TasksController], +}) +export class TasksModule {} diff --git a/apps/service-auth/src/tasks/tasks.service.ts b/apps/service-auth/src/tasks/tasks.service.ts new file mode 100644 index 0000000..96cb365 --- /dev/null +++ b/apps/service-auth/src/tasks/tasks.service.ts @@ -0,0 +1,90 @@ +import { ClashClient } from '@app/clash-client'; +import { Collections, Tokens } from '@app/constants'; +import { LegendAttacksEntity } from '@app/entities/legend-attacks.entity'; +import { RedisClient } from '@app/redis'; +import { Inject, Injectable } from '@nestjs/common'; +import { Collection } from 'mongodb'; + +@Injectable() +export class TasksService { + constructor( + @Inject(Collections.LEGEND_ATTACKS) + private readonly legendAttacksCollection: Collection, + @Inject(Tokens.CLASH_CLIENT) private readonly clashClient: ClashClient, + @Inject(Tokens.REDIS) private readonly redis: RedisClient, + ) {} + + public async backfillLegendTrophyThreshold() { + const thresholds = await this.getTrophyThresholds(); + await this.redis.set('RAW:LEGEND-TROPHY-THRESHOLD', JSON.stringify(thresholds), { + EX: 60 * 60 * 24, + }); + return thresholds; // legend-trophy-threshold + } + + public async getTrophyThresholds() { + const seasonId = this.clashClient.util.getSeasonId(); + const [result] = await this.legendAttacksCollection + .aggregate>([ + { + $match: { + seasonId, + }, + }, + { + $sort: { + trophies: -1, + }, + }, + { + $limit: 1000, + }, + { + $group: { + _id: null, + trophies: { + $push: '$trophies', + }, + }, + }, + { + $project: { + _id: 0, + '1': { + $arrayElemAt: ['$trophies', 0], + }, + '3': { + $arrayElemAt: ['$trophies', 2], + }, + '10': { + $arrayElemAt: ['$trophies', 9], + }, + '20': { + $arrayElemAt: ['$trophies', 19], + }, + '50': { + $arrayElemAt: ['$trophies', 49], + }, + '100': { + $arrayElemAt: ['$trophies', 99], + }, + '200': { + $arrayElemAt: ['$trophies', 199], + }, + '500': { + $arrayElemAt: ['$trophies', 499], + }, + '1000': { + $arrayElemAt: ['$trophies', 999], + }, + }, + }, + ]) + .toArray(); + + return Object.entries(result ?? {}).map(([rank, minTrophies]) => ({ + rank: Number(rank), + minTrophies, + })); + } +} diff --git a/libs/auth/src/guards/api-key-guard.ts b/libs/auth/src/guards/api-key-guard.ts new file mode 100644 index 0000000..01ec47a --- /dev/null +++ b/libs/auth/src/guards/api-key-guard.ts @@ -0,0 +1,22 @@ +import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Request } from 'express'; + +@Injectable() +export class ApiKeyGuard implements CanActivate { + constructor(private readonly configService: ConfigService) {} + + public async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + const key = req.headers?.['x-api-key'] || req.query['key']; + if (!key) { + throw new UnauthorizedException('x-api-key header is missing'); + } + + if (key !== this.configService.getOrThrow('API_KEY')) { + throw new UnauthorizedException('Invalid x-api-key.'); + } + + return true; + } +} diff --git a/libs/entities/src/legend-attacks.entity.ts b/libs/entities/src/legend-attacks.entity.ts new file mode 100644 index 0000000..1e575c9 --- /dev/null +++ b/libs/entities/src/legend-attacks.entity.ts @@ -0,0 +1 @@ +export class LegendAttacksEntity {}