При первом появлении трека подтягиваем жанр/стиль/лейбл/год из Discogs и сохраняем обложку в едином формате WebP 500x500 у себя (/covers). Дальше пользователю отдаём только из своей БД — внешние сервисы в рантайме не дёргаем. - Track: +genre/styles/label/year/discogsId/enrichStatus (миграция) - EnrichModule: DiscogsService (поиск), CoverStorageService (sharp->webp), EnrichmentService (очередь с троттлингом + бэкафилл-крон каждые 10 мин) - charts: фильтр чартов по жанру (?genre=), GET /charts/genres, жанр/стиль/лейбл/год в выдаче чарта и детальной странице - main: раздача /covers статикой; docker: volume covers_data + env DISCOGS_TOKEN/PUBLIC_BASE_URL/COVERS_DIR - убран MusicBrainz-фолбэк (заменён Discogs) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
import { NestFactory } from '@nestjs/core';
|
|
import { ValidationPipe } from '@nestjs/common';
|
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
import { join } from 'path';
|
|
import { AppModule } from './app.module';
|
|
|
|
async function bootstrap() {
|
|
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
|
|
// Раздача сохранённых обложек треков (/covers/*.webp) — свой CDN
|
|
const coversDir = process.env.COVERS_DIR || join(process.cwd(), 'data', 'covers');
|
|
app.useStaticAssets(coversDir, {
|
|
prefix: '/covers/',
|
|
maxAge: '30d',
|
|
immutable: true,
|
|
});
|
|
|
|
app.useGlobalPipes(
|
|
new ValidationPipe({
|
|
whitelist: true,
|
|
forbidNonWhitelisted: true,
|
|
transform: true,
|
|
}),
|
|
);
|
|
|
|
app.enableCors({
|
|
origin: process.env.FRONTEND_URL || '*',
|
|
credentials: true,
|
|
});
|
|
|
|
const config = new DocumentBuilder()
|
|
.setTitle('radiOLA API')
|
|
.setDescription('radiOLA backend API')
|
|
.setVersion('0.1')
|
|
.addBearerAuth()
|
|
.build();
|
|
const document = SwaggerModule.createDocument(app, config);
|
|
SwaggerModule.setup('api', app, document);
|
|
|
|
const port = process.env.PORT || 3000;
|
|
await app.listen(port);
|
|
console.log(`Application is running on: http://localhost:${port}`);
|
|
}
|
|
bootstrap();
|