Skip to content

Commit

Permalink
feat: legend threshold service backfill
Browse files Browse the repository at this point in the history
  • Loading branch information
csuvajit committed Sep 3, 2024
1 parent 930a445 commit 2aae390
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 0 deletions.
2 changes: 2 additions & 0 deletions apps/service-auth/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand All @@ -27,6 +28,7 @@ import { ClashClientModule } from '@app/clash-client';
RostersModule,
ClashClientModule,
DiscordOAuthModule,
TasksModule,
// KafkaProducerModule.forRootAsync({
// useFactory: (configService: ConfigService) => {
// return {
Expand Down
19 changes: 19 additions & 0 deletions apps/service-auth/src/tasks/tasks.controller.ts
Original file line number Diff line number Diff line change
@@ -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();
}
}
11 changes: 11 additions & 0 deletions apps/service-auth/src/tasks/tasks.module.ts
Original file line number Diff line number Diff line change
@@ -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 {}
90 changes: 90 additions & 0 deletions apps/service-auth/src/tasks/tasks.service.ts
Original file line number Diff line number Diff line change
@@ -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<LegendAttacksEntity>,
@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<Record<string, number>>([
{
$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,
}));
}
}
22 changes: 22 additions & 0 deletions libs/auth/src/guards/api-key-guard.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
const req = context.switchToHttp().getRequest<Request>();
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;
}
}
1 change: 1 addition & 0 deletions libs/entities/src/legend-attacks.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class LegendAttacksEntity {}

0 comments on commit 2aae390

Please sign in to comment.