feat: bootstrap NestJS backend with auth, stations, users, health-check, now-playing
This commit is contained in:
7
src/health-check/health-check.module.ts
Normal file
7
src/health-check/health-check.module.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HealthCheckService } from './health-check.service';
|
||||
|
||||
@Module({
|
||||
providers: [HealthCheckService],
|
||||
})
|
||||
export class HealthCheckModule {}
|
||||
72
src/health-check/health-check.service.ts
Normal file
72
src/health-check/health-check.service.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class HealthCheckService {
|
||||
private readonly logger = new Logger(HealthCheckService.name);
|
||||
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
@Cron(CronExpression.EVERY_HOUR)
|
||||
async checkAllStations() {
|
||||
this.logger.log('Starting hourly station health check...');
|
||||
const stations = await this.prisma.station.findMany();
|
||||
let onlineCount = 0;
|
||||
let offlineCount = 0;
|
||||
|
||||
for (const station of stations) {
|
||||
try {
|
||||
const isOnline = await this.checkStation(station.streamUrl);
|
||||
await this.prisma.station.update({
|
||||
where: { id: station.id },
|
||||
data: { isOnline, lastCheckAt: new Date() },
|
||||
});
|
||||
if (isOnline) onlineCount++;
|
||||
else offlineCount++;
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Failed to check station ${station.name}: ${error.message}`,
|
||||
);
|
||||
await this.prisma.station.update({
|
||||
where: { id: station.id },
|
||||
data: { isOnline: false, lastCheckAt: new Date() },
|
||||
});
|
||||
offlineCount++;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Health check complete. Online: ${onlineCount}, Offline: ${offlineCount}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async checkStation(url: string): Promise<boolean> {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 10000);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
signal: controller.signal,
|
||||
});
|
||||
clearTimeout(timeout);
|
||||
return response.status >= 200 && response.status < 400;
|
||||
} catch {
|
||||
clearTimeout(timeout);
|
||||
// Fallback to GET if HEAD fails
|
||||
try {
|
||||
const controller2 = new AbortController();
|
||||
const timeout2 = setTimeout(() => controller2.abort(), 10000);
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
signal: controller2.signal,
|
||||
});
|
||||
clearTimeout(timeout2);
|
||||
return response.status >= 200 && response.status < 400;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user