Files
radiola-backend/src/stations/stations.service.ts
nk 40a9f3968f feat(stations): корректный health-check + эндпоинт offline-ids
health-check переписан: живой = пришли заголовки 200-399 (рвём соединение сразу,
не ждём бесконечное тело аудиопотока), параллельно, прогон при старте + ежечасно.
Раньше GET висел на живых потоках до таймаута → ложный offline. Новый GET /stations/offline-ids
отдаёт station_id оффлайн-станций — клиент их скрывает.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:56:59 +03:00

124 lines
3.2 KiB
TypeScript

import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateStationDto } from './dto/create-station.dto';
import { UpdateStationDto } from './dto/update-station.dto';
@Injectable()
export class StationsService {
constructor(private readonly prisma: PrismaService) {}
async findAll(filters: {
search?: string;
source?: string;
online?: boolean;
}) {
const where: any = {};
if (filters.source) {
where.source = filters.source;
}
if (filters.online !== undefined) {
where.isOnline = filters.online;
}
if (filters.search) {
where.OR = [
{ name: { contains: filters.search, mode: 'insensitive' } },
{ genre: { contains: filters.search, mode: 'insensitive' } },
{ tags: { has: filters.search } },
];
}
return this.prisma.station.findMany({
where,
orderBy: { sortOrder: 'asc' },
include: { nowPlaying: true },
});
}
// station_id оффлайн-станций — для скрытия мёртвых плиток в клиенте
async getOfflineStationIds(): Promise<number[]> {
const rows = await this.prisma.station.findMany({
where: { isOnline: false },
select: { stationId: true },
});
return rows.map((r) => r.stationId);
}
async findOne(id: string) {
const station = await this.prisma.station.findUnique({
where: { id },
include: { nowPlaying: true },
});
if (!station) throw new NotFoundException('Station not found');
return station;
}
async findByStationId(stationId: number) {
return this.prisma.station.findUnique({
where: { stationId },
});
}
async create(dto: CreateStationDto) {
return this.prisma.station.create({
data: {
stationId: dto.stationId,
name: dto.name,
prefix: dto.prefix,
streamUrl: dto.streamUrl,
coverUrl: dto.coverUrl,
genre: dto.genre,
tags: dto.tags ?? [],
sortOrder: dto.sortOrder,
source: dto.source,
},
});
}
async update(id: string, dto: UpdateStationDto) {
return this.prisma.station.update({
where: { id },
data: {
...dto,
tags: dto.tags,
},
});
}
async remove(id: string) {
return this.prisma.station.delete({ where: { id } });
}
async upsertMany(stations: CreateStationDto[]) {
const results = [];
for (const dto of stations) {
const result = await this.prisma.station.upsert({
where: { stationId: dto.stationId },
update: {
name: dto.name,
prefix: dto.prefix,
streamUrl: dto.streamUrl,
coverUrl: dto.coverUrl,
genre: dto.genre,
tags: dto.tags ?? [],
sortOrder: dto.sortOrder,
source: dto.source,
},
create: {
stationId: dto.stationId,
name: dto.name,
prefix: dto.prefix,
streamUrl: dto.streamUrl,
coverUrl: dto.coverUrl,
genre: dto.genre,
tags: dto.tags ?? [],
sortOrder: dto.sortOrder,
source: dto.source,
},
});
results.push(result);
}
return results;
}
}