mirror of
https://github.com/thedrewen/protojx-manager.git
synced 2026-03-21 09:48:56 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fda9afb83 | |||
|
|
3d2a1125b3 | ||
| ecb12f12b7 | |||
| 55e4cf3e6c | |||
| 104023162a | |||
| 58403fd32e |
@@ -18,8 +18,4 @@ DB_DATABASE=protojx_manager
|
|||||||
DB_LOGGING=false
|
DB_LOGGING=false
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
# Protected IPS
|
|
||||||
PROTOJX_ROUTER_1=
|
|
||||||
PROTOJX_ROUTER_2=
|
|
||||||
6
.github/workflows/deploy.yml
vendored
6
.github/workflows/deploy.yml
vendored
@@ -35,14 +35,14 @@ jobs:
|
|||||||
ssh -p ${{ secrets.VPS_PORT }} ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} "
|
ssh -p ${{ secrets.VPS_PORT }} ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }} "
|
||||||
cd /home/${{ secrets.VPS_USER }}/protojx/protojx-manager &&
|
cd /home/${{ secrets.VPS_USER }}/protojx/protojx-manager &&
|
||||||
git pull &&
|
git pull &&
|
||||||
|
|
||||||
|
# Build new image
|
||||||
|
docker buildx build -t protojx_manager . &&
|
||||||
|
|
||||||
# Stop and remove old container if exists
|
# Stop and remove old container if exists
|
||||||
docker stop protojx_manager 2>/dev/null || true &&
|
docker stop protojx_manager 2>/dev/null || true &&
|
||||||
docker rm protojx_manager 2>/dev/null || true &&
|
docker rm protojx_manager 2>/dev/null || true &&
|
||||||
|
|
||||||
# Build new image
|
|
||||||
docker buildx build -t protojx_manager . &&
|
|
||||||
|
|
||||||
# Run new container
|
# Run new container
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name protojx_manager \
|
--name protojx_manager \
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -137,3 +137,4 @@ dist
|
|||||||
# Vite logs files
|
# Vite logs files
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
|
note.txt
|
||||||
@@ -5,8 +5,8 @@ WORKDIR /app
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y iputils-ping
|
RUN apt-get update && apt-get install -y iputils-ping
|
||||||
RUN npm i
|
RUN npm clean-install
|
||||||
|
|
||||||
# RUN npm run register
|
RUN npm run register
|
||||||
|
|
||||||
CMD [ "npm", "run", "start" ]
|
CMD [ "npm", "run", "start" ]
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ import { AppDataSource } from "../../data-source";
|
|||||||
import { Follow } from "../../entity/follow.entity";
|
import { Follow } from "../../entity/follow.entity";
|
||||||
import statusService from "../../services/status.service";
|
import statusService from "../../services/status.service";
|
||||||
|
|
||||||
const cmd : CommandDefinition = {
|
const cmd: CommandDefinition = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('follow')
|
.setName('follow')
|
||||||
.setDescription('Enables/disables the receipt of service status notifications.')
|
.setDescription('Enables/disables the receipt of service status notifications.')
|
||||||
|
.addStringOption((o) =>
|
||||||
|
o.setName('service')
|
||||||
|
.setDescription('Select a service to follow')
|
||||||
|
.setRequired(true)
|
||||||
|
.setAutocomplete(true)
|
||||||
|
)
|
||||||
.setIntegrationTypes(
|
.setIntegrationTypes(
|
||||||
ApplicationIntegrationType.UserInstall
|
ApplicationIntegrationType.UserInstall
|
||||||
)
|
)
|
||||||
@@ -15,41 +21,47 @@ const cmd : CommandDefinition = {
|
|||||||
InteractionContextType.BotDM,
|
InteractionContextType.BotDM,
|
||||||
InteractionContextType.Guild,
|
InteractionContextType.Guild,
|
||||||
InteractionContextType.PrivateChannel
|
InteractionContextType.PrivateChannel
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setRequired(true)
|
|
||||||
.addChoices(...statusService.hosts.filter((v) => v.notify).map((s) => ({name: s.name, value: s.host})))
|
|
||||||
.setName('host')
|
|
||||||
.setDescription('Host enable/disable.')
|
|
||||||
),
|
),
|
||||||
async execute(interaction : ChatInputCommandInteraction) {
|
async execute(interaction: ChatInputCommandInteraction) {
|
||||||
const userRepo = AppDataSource.getRepository(Follow);
|
const userRepo = AppDataSource.getRepository(Follow);
|
||||||
const hostvalue = interaction.options.getString('host');
|
const hostvalue = interaction.options.getString('service');
|
||||||
|
|
||||||
const realHost = statusService.hosts.filter((v) => v.host == hostvalue);
|
const services = await statusService.serviceRepo.find();
|
||||||
if(!hostvalue || realHost.length == 0) {
|
const realHost = services.find((v) => v.notify && v.name == hostvalue);
|
||||||
await interaction.reply({content: '⚠️ Host not found !', flags: [MessageFlags.Ephemeral]});
|
|
||||||
}else{
|
if (!hostvalue || !realHost) {
|
||||||
let follow = await userRepo.findOne({where: {user_discord: interaction.user.id, host: hostvalue}});
|
await interaction.reply({ content: '⚠️ Host not found !', flags: [MessageFlags.Ephemeral] });
|
||||||
if(!follow) {
|
} else {
|
||||||
|
let follow = await userRepo.findOne({ where: { user_discord: interaction.user.id, service: { id: realHost.id } } });
|
||||||
|
if (!follow) {
|
||||||
follow = new Follow();
|
follow = new Follow();
|
||||||
follow.user_discord = interaction.user.id;
|
follow.user_discord = interaction.user.id;
|
||||||
follow.host = hostvalue;
|
follow.service = realHost;
|
||||||
await userRepo.save(follow);
|
await userRepo.save(follow);
|
||||||
}
|
}
|
||||||
|
|
||||||
follow.enable = !follow.enable;
|
follow.enable = !follow.enable;
|
||||||
|
|
||||||
await userRepo.save(follow);
|
await userRepo.save(follow);
|
||||||
|
|
||||||
await interaction.reply({content: `✅ Notification successfully ${follow.enable ? 'enabled 🔔' : 'disabled 🔕'} for ${realHost[0]?.name}!`, flags: [MessageFlags.Ephemeral]});
|
await interaction.reply({ content: `✅ Notification successfully ${follow.enable ? 'enabled 🔔' : 'disabled 🔕'} for ${realHost.name}!`, flags: [MessageFlags.Ephemeral] });
|
||||||
|
|
||||||
if(follow.enable) {
|
if (follow.enable) {
|
||||||
await interaction.user.send({content: `🔔 Notifications have been successfully enabled for ${realHost[0]?.name} ! To disable: /follow host:${realHost[0]?.name}`})
|
await interaction.user.send({ content: `🔔 Notifications have been successfully enabled for ${realHost.name} ! To disable: /follow host:${realHost.name}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
autocompletes: [
|
||||||
|
{
|
||||||
|
name: 'service',
|
||||||
|
execute: async (interaction) => {
|
||||||
|
|
||||||
|
const services = await statusService.serviceRepo.find({where: {notify: true}});
|
||||||
|
|
||||||
|
interaction.respond(services.map((v) => ({name: v.name, value: v.name})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default cmd;
|
export default cmd;
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import "reflect-metadata"
|
import "reflect-metadata"
|
||||||
import { DataSource } from "typeorm"
|
import { DataSource } from "typeorm"
|
||||||
import { configDotenv } from "dotenv"
|
import { configDotenv } from "dotenv"
|
||||||
|
import { Follow } from "./entity/follow.entity"
|
||||||
|
import { Guild } from "./entity/guild.entity"
|
||||||
|
import { HostsLog } from "./entity/hostslog.entity"
|
||||||
|
import { Service } from "./entity/service.entity"
|
||||||
|
|
||||||
configDotenv()
|
configDotenv()
|
||||||
|
|
||||||
@@ -13,7 +17,7 @@ export const AppDataSource = new DataSource({
|
|||||||
database: process.env.DB_DATABASE,
|
database: process.env.DB_DATABASE,
|
||||||
synchronize: process.env.NODE_ENV !== "production",
|
synchronize: process.env.NODE_ENV !== "production",
|
||||||
logging: process.env.DB_LOGGING === "true",
|
logging: process.env.DB_LOGGING === "true",
|
||||||
entities: [__dirname + '/**/*.entity.js'],
|
entities: [Follow, Guild, HostsLog, Service],
|
||||||
migrations: [__dirname + "/**/*.migration.js"],
|
migrations: [__dirname + "/**/*.migration.js"],
|
||||||
subscribers: [__dirname + "/**/*.subscriber.js"],
|
subscribers: [__dirname + "/**/*.subscriber.js"],
|
||||||
})
|
})
|
||||||
@@ -2,6 +2,8 @@ import path from "path";
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { configDotenv } from "dotenv";
|
import { configDotenv } from "dotenv";
|
||||||
import { REST, Routes } from "discord.js";
|
import { REST, Routes } from "discord.js";
|
||||||
|
import "reflect-metadata";
|
||||||
|
import { AppDataSource } from "./data-source";
|
||||||
|
|
||||||
configDotenv();
|
configDotenv();
|
||||||
|
|
||||||
@@ -57,6 +59,10 @@ const rest = new REST().setToken(process.env.TOKEN);
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
// Initialize DataSource first
|
||||||
|
await AppDataSource.initialize();
|
||||||
|
console.log("Data Source initialized for command deployment!");
|
||||||
|
|
||||||
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||||
|
|
||||||
const data = await rest.put(
|
const data = await rest.put(
|
||||||
@@ -65,6 +71,10 @@ const rest = new REST().setToken(process.env.TOKEN);
|
|||||||
) as any[];
|
) as any[];
|
||||||
|
|
||||||
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||||
|
|
||||||
|
// Close the connection
|
||||||
|
await AppDataSource.destroy();
|
||||||
|
process.exit(0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ERROR] Failed to deploy commands:', error);
|
console.error('[ERROR] Failed to deploy commands:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
import { Service } from "./service.entity";
|
||||||
|
|
||||||
@Entity({name: 'follows'})
|
@Entity({name: 'follows'})
|
||||||
export class Follow {
|
export class Follow {
|
||||||
@@ -8,8 +9,12 @@ export class Follow {
|
|||||||
@Column()
|
@Column()
|
||||||
user_discord: string;
|
user_discord: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Service, service => service.follows)
|
||||||
|
@JoinColumn({name: 'serviceId'})
|
||||||
|
service: Service;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
host: string;
|
serviceId: number;
|
||||||
|
|
||||||
@Column({default: false})
|
@Column({default: false})
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from "typeorm";
|
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
import { Service } from "./service.entity";
|
||||||
|
|
||||||
@Entity({name: 'hosts_logs'})
|
@Entity({name: 'hosts_logs'})
|
||||||
export class HostsLog {
|
export class HostsLog {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => Service, service => service.logs)
|
||||||
|
@JoinColumn({name: 'serviceId'})
|
||||||
|
service: Service;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
host: string;
|
serviceId: number;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
status: boolean;
|
status: boolean;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
}
|
}
|
||||||
34
src/entity/service.entity.ts
Normal file
34
src/entity/service.entity.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
import { HostsLog } from "./hostslog.entity";
|
||||||
|
import { Follow } from "./follow.entity";
|
||||||
|
|
||||||
|
@Entity({name: 'services'})
|
||||||
|
export class Service {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
host: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
alive: boolean;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
ping_type: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
notify: boolean
|
||||||
|
|
||||||
|
|
||||||
|
@OneToMany(() => HostsLog, log => log.service)
|
||||||
|
logs: HostsLog[];
|
||||||
|
|
||||||
|
@OneToMany(() => Follow, follow => follow.service)
|
||||||
|
follows: Follow[];
|
||||||
|
}
|
||||||
15
src/index.ts
15
src/index.ts
@@ -60,6 +60,21 @@ client.on(Events.InteractionCreate, async interaction => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}else if(interaction.isAutocomplete()){
|
||||||
|
const option = interaction.options.getFocused(true);
|
||||||
|
|
||||||
|
commands.forEach((value) => {
|
||||||
|
if(value.autocompletes) {
|
||||||
|
const auto = value.autocompletes.filter((a) => a.name == option.name);
|
||||||
|
if(auto.length >= 1){
|
||||||
|
auto.forEach((a) => {
|
||||||
|
a.execute(interaction)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import ping from "ping";
|
import ping from "ping";
|
||||||
import * as cron from 'cron';
|
import * as cron from 'cron';
|
||||||
import { ActivityType, Client, ContainerBuilder, MessageFlags } from "discord.js";
|
import { ActivityType, Client, ContainerBuilder, MessageFlags } from "discord.js";
|
||||||
import { Host, InfraType } from "../type";
|
import { InfraType } from "../type";
|
||||||
import { AppDataSource } from "../data-source";
|
import { AppDataSource } from "../data-source";
|
||||||
import { HostsLog } from "../entity/hostslog.entity";
|
import { HostsLog } from "../entity/hostslog.entity";
|
||||||
import { Repository } from "typeorm";
|
import { Repository } from "typeorm";
|
||||||
@@ -9,137 +9,24 @@ import { Follow } from "../entity/follow.entity";
|
|||||||
import { Guild } from "../entity/guild.entity";
|
import { Guild } from "../entity/guild.entity";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
import { Canvas } from "canvas";
|
import { Canvas } from "canvas";
|
||||||
|
import { Service } from "../entity/service.entity";
|
||||||
|
|
||||||
type Nofity = {time: Date, name : string, alive : boolean, type : InfraType, host: string};
|
type Nofity = {time: Date, name : string, alive : boolean, type : string, host: Service};
|
||||||
|
|
||||||
export class StatusService {
|
export class StatusService {
|
||||||
|
|
||||||
public hosts: Host[] = [
|
|
||||||
{
|
|
||||||
host: 'https://protojx.com',
|
|
||||||
name: 'Protojx Website',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'website',
|
|
||||||
type: 'website',
|
|
||||||
notify: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: 'https://manager.protojx.com',
|
|
||||||
name: 'Espace Client',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'website',
|
|
||||||
type: 'website',
|
|
||||||
notify: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.4',
|
|
||||||
name: 'RYZEN 01',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'ryzen',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.240',
|
|
||||||
name: 'RYZEN 02',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'ryzen',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.5',
|
|
||||||
name: 'RYZEN 03',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'ryzen',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '144.76.35.26',
|
|
||||||
name: 'RYZEN7 04',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'ryzen',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.177',
|
|
||||||
name: 'XEON 01',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'xeon',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '154.16.254.45',
|
|
||||||
name: 'XEON 02',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'xeon',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.232',
|
|
||||||
name: 'XEON 03',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'xeon',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.53',
|
|
||||||
name: 'RYZEN-GAME 01',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'games',
|
|
||||||
notify: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: '5.178.99.17',
|
|
||||||
name: 'RYZEN-GAME 02',
|
|
||||||
alive: false,
|
|
||||||
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
|
|
||||||
},
|
|
||||||
// Routers
|
|
||||||
{
|
|
||||||
host: process.env.PROTOJX_ROUTER_1 as string,
|
|
||||||
name: 'ROUTER-FR 01',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'router',
|
|
||||||
notify: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
host: process.env.PROTOJX_ROUTER_2 as string,
|
|
||||||
name: 'ROUTER-FR 02',
|
|
||||||
alive: false,
|
|
||||||
ping_type: 'ping',
|
|
||||||
type: 'router',
|
|
||||||
notify: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
private client: Client | null = null;
|
private client: Client | null = null;
|
||||||
private hostsLogRepo: Repository<HostsLog>;
|
private hostsLogRepo: Repository<HostsLog>;
|
||||||
private followRepo: Repository<Follow>;
|
private followRepo: Repository<Follow>;
|
||||||
private guildRepo: Repository<Guild>;
|
private guildRepo: Repository<Guild>;
|
||||||
|
public serviceRepo: Repository<Service>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
this.hostsLogRepo = AppDataSource.getRepository(HostsLog);
|
this.hostsLogRepo = AppDataSource.getRepository(HostsLog);
|
||||||
this.followRepo = AppDataSource.getRepository(Follow);
|
this.followRepo = AppDataSource.getRepository(Follow);
|
||||||
this.guildRepo = AppDataSource.getRepository(Guild);
|
this.guildRepo = AppDataSource.getRepository(Guild);
|
||||||
|
this.serviceRepo = AppDataSource.getRepository(Service);
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await this.fetch()
|
await this.fetch()
|
||||||
@@ -175,7 +62,7 @@ export class StatusService {
|
|||||||
await message.edit({components: [await this.getUpdatedContainer(true)]});
|
await message.edit({components: [await this.getUpdatedContainer(true)]});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error + ' GuildIdInDB : '+gdb.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -263,8 +150,9 @@ export class StatusService {
|
|||||||
|
|
||||||
private async updateClientStatus() {
|
private async updateClientStatus() {
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
const hosts = this.hosts.length;
|
const hosts_db = await this.serviceRepo.find();
|
||||||
const hostsAlive = this.hosts.filter((h) => h.alive).length;
|
const hosts = hosts_db.length;
|
||||||
|
const hostsAlive = hosts_db.filter((h) => h.alive).length;
|
||||||
|
|
||||||
this.client.user?.setActivity({
|
this.client.user?.setActivity({
|
||||||
name: (
|
name: (
|
||||||
@@ -274,56 +162,59 @@ export class StatusService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchAlive(host: Host, notifs : Nofity[]) {
|
private async fetchAlive(service: Service, notifs : Nofity[]) {
|
||||||
|
|
||||||
const latestLog = await this.hostsLogRepo.findOne({ where: { host: host.host }, order: { created_at: 'DESC' } });
|
const latestLog = await this.hostsLogRepo.findOne({ where: { service }, order: { created_at: 'DESC' } });
|
||||||
|
|
||||||
// ? Ping and Request Hosts
|
// ? Ping and Request Hosts
|
||||||
if (host.ping_type === 'ping') {
|
if (service.ping_type === 'ping') {
|
||||||
let res = await ping.promise.probe(host.host, { timeout: 10 });
|
let res = await ping.promise.probe(service.host, { timeout: 10 });
|
||||||
host.alive = res.alive;
|
service.alive = res.alive;
|
||||||
} else if (host.ping_type === 'website') {
|
} else if (service.ping_type === 'website') {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(host.host, { method: 'HEAD', signal: AbortSignal.timeout(10000) });
|
const response = await fetch(service.host, { method: 'HEAD', signal: AbortSignal.timeout(10000) });
|
||||||
host.alive = response.ok;
|
service.alive = response.ok;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
host.alive = false;
|
service.alive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ? Notification System :
|
// ? Notification System :
|
||||||
if (!latestLog || latestLog.status != host.alive) {
|
if (!latestLog || latestLog.status != service.alive) {
|
||||||
const log = new HostsLog();
|
const log = new HostsLog();
|
||||||
log.host = host.host;
|
log.service = service;
|
||||||
log.status = host.alive;
|
log.status = service.alive;
|
||||||
|
|
||||||
this.hostsLogRepo.save(log);
|
this.hostsLogRepo.save(log);
|
||||||
|
|
||||||
if(latestLog && host.notify) {
|
if(latestLog && service.notify) {
|
||||||
notifs.push({alive: host.alive, name: host.name, time: new Date(), type: host.type, host: host.host});
|
notifs.push({alive: service.alive, name: service.name, time: new Date(), type: service.type, host: service});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return host;
|
this.serviceRepo.save(service);
|
||||||
|
|
||||||
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetch(max = 1, notifs : Nofity[] = []) {
|
private async fetch(max = 1, notifs : Nofity[] = []) {
|
||||||
|
|
||||||
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 services = await this.serviceRepo.find();
|
||||||
|
const hosts = services.filter((value, index) => index < max * max_ping && index >= (max - 1) * max_ping);
|
||||||
|
|
||||||
const fetchPromises = hosts.map(host => this.fetchAlive(host, notifs));
|
const fetchPromises = hosts.map(host => this.fetchAlive(host, notifs));
|
||||||
const updatedHosts = await Promise.all(fetchPromises);
|
const updatedHosts = await Promise.all(fetchPromises);
|
||||||
|
|
||||||
updatedHosts.forEach((updatedHost, index) => {
|
updatedHosts.forEach((updatedHost, index) => {
|
||||||
const originalIndex = (max - 1) * max_ping + index;
|
const originalIndex = (max - 1) * max_ping + index;
|
||||||
if (originalIndex < this.hosts.length) {
|
if (originalIndex < services.length) {
|
||||||
this.hosts[originalIndex] = updatedHost;
|
services[originalIndex] = updatedHost;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.hosts.length > max * max_ping) {
|
if (services.length > max * max_ping) {
|
||||||
await this.fetch(max + 1, notifs);
|
await this.fetch(max + 1, notifs);
|
||||||
}else if(notifs.length > 0){
|
}else if(notifs.length > 0){
|
||||||
// ? Notification System (part 2 !):
|
// ? Notification System (part 2 !):
|
||||||
@@ -338,7 +229,7 @@ export class StatusService {
|
|||||||
const users = await this.followRepo.find({where: {enable: true}});
|
const users = await this.followRepo.find({where: {enable: true}});
|
||||||
const hosts = notifs.map((n) => n.host);
|
const hosts = notifs.map((n) => n.host);
|
||||||
const users_ids : string[] = [];
|
const users_ids : string[] = [];
|
||||||
users.filter(v => hosts.includes(v.host)).forEach(async (user) => {
|
users.filter(v => hosts.includes(v.service)).forEach(async (user) => {
|
||||||
if(!users_ids.includes(user.user_discord)) {
|
if(!users_ids.includes(user.user_discord)) {
|
||||||
users_ids.push(user.user_discord)
|
users_ids.push(user.user_discord)
|
||||||
try {
|
try {
|
||||||
@@ -353,7 +244,9 @@ export class StatusService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getUpdatedContainer(live : boolean = false): Promise<ContainerBuilder> {
|
public async getUpdatedContainer(live : boolean = false): Promise<ContainerBuilder> {
|
||||||
const hostTexts = this.hosts.map((s) => {
|
const services = await this.serviceRepo.find({order: {id: 'ASC'}});
|
||||||
|
|
||||||
|
const hostTexts = services.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`}` };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
12
src/type.d.ts
vendored
12
src/type.d.ts
vendored
@@ -1,12 +1,4 @@
|
|||||||
import { ButtonInteraction, ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
import { AutocompleteInteraction, ButtonInteraction, ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||||
|
|
||||||
export type InfraType = 'website' | 'ryzen' | 'xeon' | 'games' | 'router';
|
export type InfraType = 'website' | 'ryzen' | 'xeon' | 'games' | 'router';
|
||||||
export type Host = {
|
export type CommandDefinition = { data: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder, execute: (interaction: ChatInputCommandInteraction) => void, buttons?: { id: string, handle: (interaction: ButtonInteraction) => void}[], autocompletes?: {name: string, execute: (interaction: AutocompleteInteraction) => void}[]};
|
||||||
host: string,
|
|
||||||
name: string,
|
|
||||||
alive: boolean,
|
|
||||||
ping_type: 'ping' | 'website',
|
|
||||||
type: InfraType,
|
|
||||||
notify: boolean;
|
|
||||||
};
|
|
||||||
export type CommandDefinition = { data: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder, execute: (interaction: ChatInputCommandInteraction) => void, buttons?: { id: string, handle: (interaction: ButtonInteraction) => void}[]};
|
|
||||||
77
test.js
77
test.js
@@ -1,77 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
var canvas_1 = require("canvas");
|
|
||||||
var dayjs = require("dayjs");
|
|
||||||
var fs_1 = require("fs");
|
|
||||||
function createUptimeBar(uptimes) {
|
|
||||||
var now = dayjs();
|
|
||||||
var week = now.clone().subtract(1, 'week');
|
|
||||||
var canvas = new canvas_1.Canvas(100, 2, "image");
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
ctx.fillStyle = "#27FF00";
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
var maxTime = (now.unix() - week.unix());
|
|
||||||
var ranges = [];
|
|
||||||
var minTime = null;
|
|
||||||
uptimes.map(function (element, index) {
|
|
||||||
var positionForMaxTime = (element.date.unix() - week.unix());
|
|
||||||
var percent = Math.round((positionForMaxTime / maxTime) * 100);
|
|
||||||
if (ranges.length == 0 && minTime == null) {
|
|
||||||
if (element.up && minTime == null) {
|
|
||||||
ranges.push({
|
|
||||||
min: 0,
|
|
||||||
max: percent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
minTime = percent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!element.up) {
|
|
||||||
minTime = percent;
|
|
||||||
if (minTime != null && index == uptimes.length - 1) {
|
|
||||||
ranges.push({
|
|
||||||
min: minTime,
|
|
||||||
max: 100
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (minTime) {
|
|
||||||
ranges.push({
|
|
||||||
min: minTime,
|
|
||||||
max: percent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ctx.fillStyle = '#ff0000';
|
|
||||||
ranges.map(function (value) {
|
|
||||||
ctx.fillRect(value.min, 0, value.max - value.min, canvas.height);
|
|
||||||
});
|
|
||||||
(0, fs_1.writeFile)('test.png', canvas.toBuffer('image/png'), function (err) {
|
|
||||||
if (err)
|
|
||||||
throw err;
|
|
||||||
console.log('Image saved!');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
createUptimeBar([
|
|
||||||
{
|
|
||||||
up: true,
|
|
||||||
date: dayjs().subtract(6, 'day')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
up: false,
|
|
||||||
date: dayjs().subtract(3, 'day')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
up: true,
|
|
||||||
date: dayjs().subtract(1, 'day')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
up: false,
|
|
||||||
date: dayjs().subtract(1, 'hour')
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
81
test.ts
81
test.ts
@@ -1,81 +0,0 @@
|
|||||||
import { Canvas } from "canvas";
|
|
||||||
import * as dayjs from "dayjs";
|
|
||||||
import { Dayjs } from "dayjs";
|
|
||||||
import { writeFile } from "fs";
|
|
||||||
|
|
||||||
function createUptimeBar(uptimes: { up: boolean, date: Dayjs }[]) {
|
|
||||||
const now = dayjs();
|
|
||||||
const week = now.clone().subtract(1, 'week');
|
|
||||||
|
|
||||||
const canvas = new Canvas(100, 2, "image");
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
ctx.fillStyle = "#27FF00";
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
const maxTime = (now.unix() - week.unix());
|
|
||||||
const ranges: { min: number, max: number }[] = [];
|
|
||||||
|
|
||||||
let minTime: number | null = null;
|
|
||||||
uptimes.map((element, index) => {
|
|
||||||
const positionForMaxTime = (element.date.unix() - week.unix());
|
|
||||||
const percent = Math.round((positionForMaxTime / maxTime) * 100);
|
|
||||||
|
|
||||||
if (ranges.length == 0 && minTime == null) {
|
|
||||||
if (element.up && minTime == null) {
|
|
||||||
ranges.push({
|
|
||||||
min: 0,
|
|
||||||
max: percent
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
minTime = percent;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!element.up) {
|
|
||||||
minTime = percent;
|
|
||||||
|
|
||||||
if(minTime != null && index == uptimes.length - 1) {
|
|
||||||
ranges.push({
|
|
||||||
min: minTime,
|
|
||||||
max: 100
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (minTime) {
|
|
||||||
ranges.push({
|
|
||||||
min: minTime,
|
|
||||||
max: percent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ctx.fillStyle = '#ff0000';
|
|
||||||
ranges.map((value) => {
|
|
||||||
ctx.fillRect(value.min, 0, value.max - value.min, canvas.height);
|
|
||||||
});
|
|
||||||
|
|
||||||
writeFile('test.png', canvas.toBuffer('image/png'), (err) => {
|
|
||||||
if (err) throw err;
|
|
||||||
console.log('Image saved!');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUptimeBar([
|
|
||||||
{
|
|
||||||
up: true,
|
|
||||||
date: dayjs().subtract(6, 'day')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
up: false,
|
|
||||||
date: dayjs().subtract(3, 'day')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
up: true,
|
|
||||||
date: dayjs().subtract(1, 'day')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
up: false,
|
|
||||||
date: dayjs().subtract(1, 'hour')
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
Reference in New Issue
Block a user