mirror of
https://github.com/thedrewen/protojx-manager.git
synced 2026-03-23 05:01:54 +01:00
feat: refactor command imports, enhance status service with logging and notification features, and update entity definitions
This commit is contained in:
@@ -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 = {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
16
src/entity/hostslog.entity.ts
Normal file
16
src/entity/hostslog.entity.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -2,93 +2,118 @@ 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 {
|
||||||
|
|
||||||
public hosts: Host[] = [
|
public hosts: Host[] = [
|
||||||
{
|
{
|
||||||
'host': 'https://protojx.com',
|
'host': 'https://protojx.com',
|
||||||
'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',
|
{
|
||||||
'name': 'Espace Client',
|
'host': 'https://manager.protojx.com',
|
||||||
alive: false,
|
'name': 'Espace Client',
|
||||||
ping_type: 'website',
|
alive: false,
|
||||||
type: 'website'
|
ping_type: 'website',
|
||||||
},
|
type: 'website',
|
||||||
{
|
notify: false
|
||||||
host: '5.178.99.4',
|
},
|
||||||
name: 'RYZEN 01',
|
{
|
||||||
alive: false,
|
host: '5.178.99.4',
|
||||||
ping_type: 'ping',
|
name: 'RYZEN 01',
|
||||||
type: 'ryzen'
|
alive: false,
|
||||||
},
|
ping_type: 'ping',
|
||||||
{
|
type: 'ryzen',
|
||||||
host: '5.178.99.6',
|
notify: true
|
||||||
name: 'RYZEN 02',
|
},
|
||||||
alive: false,
|
{
|
||||||
ping_type: 'ping',
|
host: '5.178.99.6',
|
||||||
type: 'ryzen'
|
name: 'RYZEN 02',
|
||||||
},
|
alive: false,
|
||||||
{
|
ping_type: 'ping',
|
||||||
host: '5.178.99.5',
|
type: 'ryzen',
|
||||||
name: 'RYZEN 03',
|
notify: true
|
||||||
alive: false,
|
},
|
||||||
ping_type: 'ping',
|
{
|
||||||
type: 'ryzen'
|
host: '5.178.99.5',
|
||||||
},
|
name: 'RYZEN 03',
|
||||||
{
|
alive: false,
|
||||||
host: '154.16.254.10',
|
ping_type: 'ping',
|
||||||
name: 'RYZEN7 04',
|
type: 'ryzen',
|
||||||
alive: false,
|
notify: true
|
||||||
ping_type: 'ping',
|
},
|
||||||
type: 'ryzen'
|
{
|
||||||
},
|
host: '154.16.254.10',
|
||||||
{
|
name: 'RYZEN7 04',
|
||||||
host: '5.178.99.177',
|
alive: false,
|
||||||
name: 'XEON 01 (2697A V4)',
|
ping_type: 'ping',
|
||||||
alive: false,
|
type: 'ryzen',
|
||||||
ping_type: 'ping',
|
notify: true
|
||||||
type: 'xeon'
|
},
|
||||||
},
|
{
|
||||||
{
|
host: '5.178.99.177',
|
||||||
host: '5.178.99.248',
|
name: 'XEON 01 (2697A 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.248',
|
||||||
name: 'RYZEN-GAME 01',
|
name: 'XEON 02 (2687W V4)',
|
||||||
alive: false,
|
alive: false,
|
||||||
ping_type: 'ping',
|
ping_type: 'ping',
|
||||||
type: 'games'
|
type: 'xeon',
|
||||||
},
|
notify: true
|
||||||
{
|
},
|
||||||
host: '5.178.99.63',
|
{
|
||||||
name: 'XEON-GAME 01',
|
host: '5.178.99.53',
|
||||||
alive: false,
|
name: 'RYZEN-GAME 01',
|
||||||
ping_type: 'ping',
|
alive: false,
|
||||||
type: 'games'
|
ping_type: 'ping',
|
||||||
}
|
type: 'games',
|
||||||
];
|
notify: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
host: '5.178.99.63',
|
||||||
|
name: 'XEON-GAME 01',
|
||||||
|
alive: false,
|
||||||
|
ping_type: 'ping',
|
||||||
|
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,27 +144,42 @@ export class StatusService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async fetchAlive(host: Host) {
|
||||||
|
|
||||||
|
const latestLog = await this.hostsLogRepo.findOne({ where: { host: host.host }, order: { created_at: 'DESC' } });
|
||||||
|
|
||||||
|
// ? Ping and Request Hosts
|
||||||
|
if (host.ping_type === 'ping') {
|
||||||
|
let res = await ping.promise.probe(host.host, { timeout: 10 });
|
||||||
|
host.alive = res.alive;
|
||||||
|
} else if (host.ping_type === 'website') {
|
||||||
|
try {
|
||||||
|
const response = await fetch(host.host, { method: 'HEAD', signal: AbortSignal.timeout(10000) });
|
||||||
|
host.alive = response.ok;
|
||||||
|
} catch (error) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
private async fetch(max = 1): Promise<Host[]> {
|
private async fetch(max = 1): Promise<Host[]> {
|
||||||
|
|
||||||
const max_ping = 3;
|
const max_ping = 3;
|
||||||
|
|
||||||
const hosts = this.hosts.filter((value, index) => index < max * max_ping && index >= (max - 1) * max_ping);
|
const hosts = this.hosts.filter((value, index) => index < max * max_ping && index >= (max - 1) * max_ping);
|
||||||
async function fetchAlive(host: Host) {
|
|
||||||
if (host.ping_type === 'ping') {
|
|
||||||
let res = await ping.promise.probe(host.host, { timeout: 10 });
|
|
||||||
host.alive = res.alive;
|
|
||||||
} else if (host.ping_type === 'website') {
|
|
||||||
try {
|
|
||||||
const response = await fetch(host.host, { method: 'HEAD', signal: AbortSignal.timeout(10000) });
|
|
||||||
host.alive = response.ok;
|
|
||||||
} catch (error) {
|
|
||||||
host.alive = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchPromises = hosts.map(host => fetchAlive(host));
|
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) => {
|
||||||
@@ -156,16 +196,16 @@ export class StatusService {
|
|||||||
return this.hosts;
|
return this.hosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUpdatedContainer() : ContainerBuilder {
|
public getUpdatedContainer(): ContainerBuilder {
|
||||||
const hostTexts = this.hosts.map((s) => {
|
const hostTexts = this.hosts.map((s) => {
|
||||||
return {type: s.type, value: `- ${s.name} : ${s.alive ? `${process.env.EMOJI_STATUS_ONLINE} Online` : `${process.env.EMOJI_STATUS_OFFLINE} Offline`}`};
|
return { type: s.type, value: `- ${s.name} : ${s.alive ? `${process.env.EMOJI_STATUS_ONLINE} Online` : `${process.env.EMOJI_STATUS_OFFLINE} Offline`}` };
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = new ContainerBuilder()
|
const container = new ContainerBuilder()
|
||||||
.setAccentColor(0x0000ed)
|
.setAccentColor(0x0000ed)
|
||||||
.addTextDisplayComponents((text) => text.setContent('# Status of protojx services'));
|
.addTextDisplayComponents((text) => text.setContent('# Status of protojx services'));
|
||||||
|
|
||||||
const sections : {title: string, type: InfraType, thumbnail: string}[] = [
|
const sections: { title: string, type: InfraType, thumbnail: string }[] = [
|
||||||
{
|
{
|
||||||
title: 'Websites',
|
title: 'Websites',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
@@ -194,17 +234,17 @@ export class StatusService {
|
|||||||
(section) =>
|
(section) =>
|
||||||
section.addTextDisplayComponents(
|
section.addTextDisplayComponents(
|
||||||
(text) =>
|
(text) =>
|
||||||
text.setContent('## '+sectionData.title+'\n'+hostTexts.filter((v) => v.type == sectionData.type).map((v) => v.value).join('\n'))
|
text.setContent('## ' + sectionData.title + '\n' + hostTexts.filter((v) => v.type == sectionData.type).map((v) => v.value).join('\n'))
|
||||||
)
|
|
||||||
.setThumbnailAccessory(
|
|
||||||
(acc) =>
|
|
||||||
acc.setURL(sectionData.thumbnail)
|
|
||||||
)
|
)
|
||||||
|
.setThumbnailAccessory(
|
||||||
|
(acc) =>
|
||||||
|
acc.setURL(sectionData.thumbnail)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
container.addTextDisplayComponents((text) => text.setContent(`${now.getDate()}-${now.getMonth() + 1}-${now.getFullYear()} ${(now.getHours()+'').padStart(2, "0")}:${(now.getMinutes()+'').padStart(2, "0")}`));
|
container.addTextDisplayComponents((text) => text.setContent(`${now.getDate()}-${now.getMonth() + 1}-${now.getFullYear()} ${(now.getHours() + '').padStart(2, "0")}:${(now.getMinutes() + '').padStart(2, "0")}`));
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/type.d.ts
vendored
3
src/type.d.ts
vendored
@@ -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}[]};
|
||||||
@@ -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/**/*"
|
||||||
|
|||||||
Reference in New Issue
Block a user