feat(shazam): глобальный лимит распознаваний (защита баланса коинов)
Скользящее окно 60с, максимум 30 реальных вызовов Shazam/мин (кэш-хиты не в счёт). Превышение → 429. Защищает платный баланс от перебора станций. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ import {
|
||||
NotFoundException,
|
||||
BadRequestException,
|
||||
ServiceUnavailableException,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { ShazamClient } from './shazam.client';
|
||||
@@ -30,6 +32,10 @@ export class ShazamService {
|
||||
// когда несколько клиентов распознают одну станцию почти одновременно.
|
||||
private readonly cache = new Map<number, CacheEntry>();
|
||||
private readonly CACHE_TTL_MS = 15000;
|
||||
// Глобальный лимит реальных вызовов Shazam (платные коины) — защита баланса
|
||||
// от перебора станций. Кэш-хиты сюда не считаются.
|
||||
private readonly recentCalls: number[] = [];
|
||||
private readonly MAX_PER_MIN = 30;
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
@@ -58,6 +64,8 @@ export class ShazamService {
|
||||
throw new BadRequestException('На этой станции нет музыки');
|
||||
}
|
||||
|
||||
this.checkRateLimit(now);
|
||||
|
||||
let result: RecognizeResponse;
|
||||
try {
|
||||
const audio = await fetchStreamChunk(station.streamUrl);
|
||||
@@ -87,6 +95,21 @@ export class ShazamService {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Скользящее окно 60с по реальным (не кэшированным) вызовам Shazam.
|
||||
private checkRateLimit(now: number): void {
|
||||
const cutoff = now - 60000;
|
||||
while (this.recentCalls.length && this.recentCalls[0] < cutoff) {
|
||||
this.recentCalls.shift();
|
||||
}
|
||||
if (this.recentCalls.length >= this.MAX_PER_MIN) {
|
||||
throw new HttpException(
|
||||
'Слишком много запросов на распознавание, попробуйте позже',
|
||||
HttpStatus.TOO_MANY_REQUESTS,
|
||||
);
|
||||
}
|
||||
this.recentCalls.push(now);
|
||||
}
|
||||
|
||||
// Если у распознанного трека нет обложки от Shazam — пробуем взять обложку
|
||||
// уже обогащённого трека из нашей БД (по тому же normKey, что и чарты).
|
||||
private async resolveCover(
|
||||
|
||||
Reference in New Issue
Block a user