Skip to content

Commit 95c99c8

Browse files
committed
feat(api): add GET /player/stale route for worker sweeper
Exposes stale players (expiresAt older than N days) as a worker-only endpoint so the external worker can refresh them via its own Hypixel keys without a direct MongoDB connection. - Add PlayerStaleDto with limit (default 10k, max 50k) and staleAfterDays (default 7) - Add PlayerService.getStale: queries MongoDB sorted by expiresAt asc - Add GET /player/stale controller route (WORKER role, weight 10) - Add mongo index on Player.expiresAt to keep the query fast
1 parent 20e84d6 commit 95c99c8

4 files changed

Lines changed: 50 additions & 1 deletion

File tree

‎apps/api/src/dtos/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export * from "./verify-code.dto.js";
1717
export * from "./command.dto.js";
1818
export * from "./update-player.dto.js";
1919
export * from "./player-group.dto.js";
20+
export * from "./player-stale.dto.js";
2021
export * from "./player-search.dto.js";
2122
export * from "./player.dto.js";
2223
export * from "./key.dto.js";
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright (c) Statsify
3+
*
4+
* This source code is licensed under the GNU GPL v3 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
* https://github.com/Statsify/statsify/blob/main/LICENSE
7+
*/
8+
9+
import { ApiPropertyOptional } from "@nestjs/swagger";
10+
import { IsNumber, IsOptional, Max, Min } from "class-validator";
11+
import { Transform } from "class-transformer";
12+
13+
export class PlayerStaleDto {
14+
@Transform((params) => +params.value)
15+
@IsNumber()
16+
@IsOptional()
17+
@Min(1)
18+
@Max(50_000)
19+
@ApiPropertyOptional({ default: 10_000 })
20+
public limit: number = 10_000;
21+
22+
@Transform((params) => +params.value)
23+
@IsNumber()
24+
@IsOptional()
25+
@Min(1)
26+
@ApiPropertyOptional({ default: 7 })
27+
public staleAfterDays: number = 7;
28+
}

‎apps/api/src/player/player.controller.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from "@nestjs/swagger";
1616
import { Auth, AuthRole } from "#auth";
1717
import { Body, Controller, Delete, Get, Post, Query } from "@nestjs/common";
18-
import { CachedPlayerDto, PlayerDto, PlayerGroupDto, UpdatePlayerDto } from "#dtos";
18+
import { CachedPlayerDto, PlayerDto, PlayerGroupDto, PlayerStaleDto, UpdatePlayerDto } from "#dtos";
1919
import {
2020
DeletePlayerResponse,
2121
ErrorResponse,
@@ -63,6 +63,13 @@ export class PlayerController {
6363
return this.playerService.getPlayers(start, end);
6464
}
6565

66+
@ApiOperation({ summary: "Get Stale Players" })
67+
@Auth({ role: AuthRole.WORKER, weight: 10 })
68+
@Get("/stale")
69+
public getStale(@Query() { limit, staleAfterDays }: PlayerStaleDto) {
70+
return this.playerService.getStale(limit, staleAfterDays);
71+
}
72+
6673
@ApiOperation({ summary: "Deletes a Player" })
6774
@ApiOkResponse({ type: DeletePlayerResponse })
6875
@ApiBadRequestResponse({ type: ErrorResponse })

‎apps/api/src/player/player.service.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ export class PlayerService {
103103
);
104104
}
105105

106+
public async getStale(limit: number, staleAfterDays: number): Promise<string[]> {
107+
const threshold = Date.now() - staleAfterDays * 24 * 60 * 60 * 1000;
108+
109+
const players = await this.playerModel
110+
.find({ expiresAt: { $lt: threshold } } as any, { uuid: 1, _id: 0 })
111+
.sort({ expiresAt: 1 })
112+
.limit(limit)
113+
.lean()
114+
.exec();
115+
116+
return (players as any[]).map((p) => p.uuid);
117+
}
118+
106119
public async getStatus(tag: string) {
107120
const player = await this.get(tag, CacheLevel.CACHE, {
108121
uuid: true,

0 commit comments

Comments
 (0)