feat: refactor command imports, enhance status service with logging and notification features, and update entity definitions

This commit is contained in:
2025-10-31 10:02:44 +01:00
parent 03769b14fa
commit 6b10aaa009
9 changed files with 165 additions and 109 deletions

View File

@@ -1,4 +1,4 @@
import { ApplicationIntegrationType, ButtonInteraction, ButtonStyle, ChatInputCommandInteraction, CommandInteraction, ComponentType, ContainerBuilder, InteractionContextType, MessageFlags, SlashCommandBuilder } from "discord.js"; import { ApplicationIntegrationType, ButtonInteraction, ButtonStyle, ChatInputCommandInteraction, ContainerBuilder, InteractionContextType, MessageFlags, SlashCommandBuilder } from "discord.js";
import { CommandDefinition } from "../../type"; import { CommandDefinition } from "../../type";
const cmd : CommandDefinition = { const cmd : CommandDefinition = {

View File

@@ -1,8 +1,6 @@
import { ApplicationIntegrationType, ChatInputCommandInteraction, CommandInteraction, ContainerBuilder, EmbedBuilder, InteractionContextType, MessageFlags, SlashCommandBuilder } from "discord.js"; import { ApplicationIntegrationType, ChatInputCommandInteraction, InteractionContextType, MessageFlags, SlashCommandBuilder } from "discord.js";
import ping from "ping";
import statusService from "../../services/status.service"; import statusService from "../../services/status.service";
import { CommandDefinition, InfraType } from "../../type"; import { CommandDefinition} from "../../type";
import { secureHeapUsed } from "crypto";
const cmd : CommandDefinition = { const cmd : CommandDefinition = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()

View File

@@ -1,6 +1,6 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity() @Entity({name: 'follows'})
export class Follow { export class Follow {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;

View File

@@ -1,6 +1,6 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity() @Entity({name: 'guilds'})
export class Guild { export class Guild {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;

View File

@@ -0,0 +1,16 @@
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity({name: 'hosts_logs'})
export class HostsLog {
@PrimaryGeneratedColumn()
id: number;
@Column()
host: string;
@Column()
status: boolean;
@CreateDateColumn()
created_at: Date;
}

View File

@@ -1,4 +1,4 @@
import { ButtonInteraction, ChatInputCommandInteraction, Client, Collection, Events, GatewayIntentBits, MessageFlags, SlashCommandBuilder } from "discord.js"; import { Client, Collection, Events, GatewayIntentBits, MessageFlags } from "discord.js";
import { configDotenv } from "dotenv"; import { configDotenv } from "dotenv";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";

View File

@@ -2,8 +2,9 @@ import ping from "ping";
import * as cron from 'cron'; import * as cron from 'cron';
import { ActivityType, Client, ContainerBuilder } from "discord.js"; import { ActivityType, Client, ContainerBuilder } from "discord.js";
import { Host, InfraType } from "../type"; import { Host, InfraType } from "../type";
import { loadEnvFile } from "process"; import { AppDataSource } from "../data-source";
import { configDotenv } from "dotenv"; import { HostsLog } from "../entity/hostslog.entity";
import { Repository } from "typeorm";
export class StatusService { export class StatusService {
@@ -13,82 +14,106 @@ export class StatusService {
'name': 'Protojx Website', 'name': 'Protojx Website',
alive: false, alive: false,
ping_type: 'website', ping_type: 'website',
type: 'website' type: 'website',
notify: false
}, },
{ {
'host': 'https://manager.protojx.com', 'host': 'https://manager.protojx.com',
'name': 'Espace Client', 'name': 'Espace Client',
alive: false, alive: false,
ping_type: 'website', ping_type: 'website',
type: 'website' type: 'website',
notify: false
}, },
{ {
host: '5.178.99.4', host: '5.178.99.4',
name: 'RYZEN 01', name: 'RYZEN 01',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'ryzen' type: 'ryzen',
notify: true
}, },
{ {
host: '5.178.99.6', host: '5.178.99.6',
name: 'RYZEN 02', name: 'RYZEN 02',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'ryzen' type: 'ryzen',
notify: true
}, },
{ {
host: '5.178.99.5', host: '5.178.99.5',
name: 'RYZEN 03', name: 'RYZEN 03',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'ryzen' type: 'ryzen',
notify: true
}, },
{ {
host: '154.16.254.10', host: '154.16.254.10',
name: 'RYZEN7 04', name: 'RYZEN7 04',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'ryzen' type: 'ryzen',
notify: true
}, },
{ {
host: '5.178.99.177', host: '5.178.99.177',
name: 'XEON 01 (2697A V4)', name: 'XEON 01 (2697A V4)',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'xeon' type: 'xeon',
notify: true
}, },
{ {
host: '5.178.99.248', host: '5.178.99.248',
name: 'XEON 02 (2687W V4)', name: 'XEON 02 (2687W V4)',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'xeon' type: 'xeon',
notify: true
}, },
{ {
host: '5.178.99.53', host: '5.178.99.53',
name: 'RYZEN-GAME 01', name: 'RYZEN-GAME 01',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'games' type: 'games',
notify: true
}, },
{ {
host: '5.178.99.63', host: '5.178.99.63',
name: 'XEON-GAME 01', name: 'XEON-GAME 01',
alive: false, alive: false,
ping_type: 'ping', ping_type: 'ping',
type: 'games' type: 'games',
notify: true
} }
]; ];
private client: Client | null = null; private client: Client | null = null;
private hostsLogRepo: Repository<HostsLog>;
constructor() { constructor() {
this.hostsLogRepo = AppDataSource.getRepository(HostsLog);
setTimeout(async () => { setTimeout(async () => {
await this.fetch() await this.fetch()
this.updateClientStatus(); this.updateClientStatus();
}, 3000); }, 3000);
const cronJob = new cron.CronJob('*/2 * * * *', async () => { const cronJob = new cron.CronJob('*/2 * * * *', async () => {
const oneMonthAgo = new Date();
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
this.hostsLogRepo.
createQueryBuilder()
.delete()
.from(HostsLog)
.where("created_at < :date", { date: oneMonthAgo })
.execute();
try { try {
await this.fetch(); await this.fetch();
await this.updateClientStatus(); await this.updateClientStatus();
@@ -119,12 +144,11 @@ export class StatusService {
} }
} }
private async fetch(max = 1): Promise<Host[]> { private async fetchAlive(host: Host) {
const max_ping = 3; const latestLog = await this.hostsLogRepo.findOne({ where: { host: host.host }, order: { created_at: 'DESC' } });
const hosts = this.hosts.filter((value, index) => index < max * max_ping && index >= (max - 1) * max_ping); // ? Ping and Request Hosts
async function fetchAlive(host: Host) {
if (host.ping_type === 'ping') { if (host.ping_type === 'ping') {
let res = await ping.promise.probe(host.host, { timeout: 10 }); let res = await ping.promise.probe(host.host, { timeout: 10 });
host.alive = res.alive; host.alive = res.alive;
@@ -136,10 +160,26 @@ export class StatusService {
host.alive = false; host.alive = false;
} }
} }
// ? Notification System :
if (!latestLog || latestLog.status != host.alive) {
const log = new HostsLog();
log.host = host.host;
log.status = host.alive;
this.hostsLogRepo.save(log);
}
return host; return host;
} }
const fetchPromises = hosts.map(host => fetchAlive(host)); private async fetch(max = 1): Promise<Host[]> {
const max_ping = 3;
const hosts = this.hosts.filter((value, index) => index < max * max_ping && index >= (max - 1) * max_ping);
const fetchPromises = hosts.map(host => this.fetchAlive(host));
const updatedHosts = await Promise.all(fetchPromises); const updatedHosts = await Promise.all(fetchPromises);
updatedHosts.forEach((updatedHost, index) => { updatedHosts.forEach((updatedHost, index) => {

3
src/type.d.ts vendored
View File

@@ -6,6 +6,7 @@ export type Host = {
name: string, name: string,
alive: boolean, alive: boolean,
ping_type: 'ping' | 'website', ping_type: 'ping' | 'website',
type: InfraType type: InfraType,
notify: boolean;
}; };
export type CommandDefinition = { data: SlashCommandBuilder, execute: (interaction: ChatInputCommandInteraction) => void, buttons?: { id: string, handle: (interaction: ButtonInteraction) => void}[]}; export type CommandDefinition = { data: SlashCommandBuilder, execute: (interaction: ChatInputCommandInteraction) => void, buttons?: { id: string, handle: (interaction: ButtonInteraction) => void}[]};

View File

@@ -25,7 +25,8 @@
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"removeComments": false, "removeComments": false,
"preserveConstEnums": true, "preserveConstEnums": true,
"strictPropertyInitialization": false "strictPropertyInitialization": false,
"noUnusedLocals": true
}, },
"include": [ "include": [
"src/**/*" "src/**/*"