Files
corrosion-admin-panel/backend-nest/src/modules/status/status.service.ts
Vantz Stockwell 9240feedaf
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
fix: Wire real data sources across players, analytics, status, and maps services
- players: Primary data from player_sessions (online status, playtime aggregates);
  ban/unban status overlaid from player_actions latest action per steam_id.
  Register PlayerSession entity in PlayersModule. Extend NATS forwarding to
  include 'unban' alongside kick and ban.
- analytics: Fix retention period boundary bug — sessions were queried with only
  a lower-bound filter (MoreThan), causing all future cycles to bleed into earlier
  wipe windows. Replaced with Between(wipeDate, endDate) for correct isolation.
- status: Replace hardcoded player_count=0/max_players=0 with live data from
  most-recent server_stats row per license. Register ServerStats entity in
  StatusModule. Falls back to 0 gracefully when no stats exist yet.
- maps: File buffer was computed and discarded — never written to disk.
  Now writes to /app/map_data/{licenseId}/{timestamp}_{filename} (tenant-isolated,
  docker volume map_data). Creates directories with mkdirSync(recursive:true).
  Logs success/failure via NestJS Logger. Throws 500 on disk write failure
  instead of silently losing data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:01:45 -05:00

62 lines
2.2 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { PublicSiteConfig } from '../../entities/public-site-config.entity';
import { ServerConnection } from '../../entities/server-connection.entity';
import { ServerStats } from '../../entities/server-stats.entity';
import { License } from '../../entities/license.entity';
@Injectable()
export class StatusService {
constructor(
@InjectRepository(PublicSiteConfig)
private readonly publicSiteRepo: Repository<PublicSiteConfig>,
@InjectRepository(ServerConnection)
private readonly serverConnectionRepo: Repository<ServerConnection>,
@InjectRepository(ServerStats)
private readonly serverStatsRepo: Repository<ServerStats>,
@InjectRepository(License)
private readonly licenseRepo: Repository<License>,
) {}
async getStatus() {
const publicConfigs = await this.publicSiteRepo.find({
where: { show_on_status_page: true },
relations: ['license'],
});
const servers = await Promise.all(
publicConfigs.map(async (config) => {
const license = await this.licenseRepo.findOne({
where: { id: config.license_id },
});
const connection = await this.serverConnectionRepo.findOne({
where: { license_id: config.license_id },
});
// Fetch the most recent stats row for this server to get live player counts
const latestStats = await this.serverStatsRepo.findOne({
where: { license_id: config.license_id },
order: { recorded_at: 'DESC' },
});
return {
server_name: license?.server_name || license?.subdomain || 'Unknown Server',
subdomain: license?.subdomain || null,
status: connection?.connection_status || 'offline',
player_count: latestStats?.player_count ?? 0,
max_players: latestStats?.max_players ?? 0,
steam_connect_url: config.steam_connect_url,
motd: config.motd,
discord_invite_url: config.discord_invite_url,
theme_color: config.theme_color,
description: config.status_page_description,
};
}),
);
return { servers };
}
}