diff --git a/package.json b/package.json index 707e959..3eefffc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudservices-rewrite", - "version": "1.1.0", + "version": "1.2.0", "description": "The official LOC Cloud Services system, this is a rewrite of the original version. ", "main": "dist/Client.js", "scripts": { @@ -16,6 +16,7 @@ "eris": "^0.10.1", "eris-pagination": "bsian03/eris-pagination", "fs-extra": "^8.1.0", + "ioredis": "^4.14.1", "moment": "^2.24.0", "moment-precise-range-plugin": "^1.3.0", "mongoose": "^5.7.4", @@ -25,6 +26,7 @@ }, "devDependencies": { "@types/fs-extra": "^8.0.0", + "@types/ioredis": "^4.0.18", "@types/mongoose": "^5.5.20", "@types/nodemailer": "^6.2.1", "@types/signale": "^1.2.1", diff --git a/src/Client.ts b/src/Client.ts index 11a09d6..9d3317e 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -1,8 +1,8 @@ import Eris from 'eris'; +import Redis from 'ioredis'; import mongoose from 'mongoose'; import signale from 'signale'; import fs from 'fs-extra'; -import path from 'path'; import config from './config.json'; import { Account, AccountInterface, Moderation, ModerationInterface, Domain, DomainInterface } from './models'; import { emojis } from './stores'; @@ -19,6 +19,8 @@ export default class Client extends Eris.Client { public db: { Account: mongoose.Model; Domain: mongoose.Model; Moderation: mongoose.Model; }; + public redis: Redis.Redis; + public stores: { emojis: { success: string, loading: string, error: string }; }; public signale: signale.Signale; @@ -31,6 +33,7 @@ export default class Client extends Eris.Client { this.util = new Util(this); this.commands = new Collection(); this.db = { Account, Domain, Moderation }; + this.redis = new Redis(); this.stores = { emojis }; this.signale = signale; this.signale.config({ @@ -82,13 +85,6 @@ export default class Client extends Eris.Client { public async init() { const evtFiles = await fs.readdir('./events/'); Object.values(commands).forEach((c: Function) => this.loadCommand(c)); - /* - const commands = await fs.readdir(path.join(__dirname, './commands/')); - commands.forEach((command) => { - if (command === 'index.js') return; - this.loadCommand(`./commands/${command}`); - }); - */ evtFiles.forEach((file) => { const eventName = file.split('.')[0]; @@ -105,6 +101,13 @@ export default class Client extends Eris.Client { this.on('ready', () => { this.signale.info(`Connected to Discord as ${this.user.username}#${this.user.discriminator}`); }); + const intervals = await fs.readdir('./intervals'); + intervals.forEach((interval) => { + // eslint-disable-next-line + if (interval === 'index.js') return; + require(`./intervals/${interval}`).default(this); + this.signale.complete(`Loaded interval ${interval.split('.')[0]}`); + }); } } diff --git a/src/class/Util.ts b/src/class/Util.ts index b1b9b99..2d3cf4b 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -232,11 +232,11 @@ export default class Util { let color: string; let archType: string; switch (type) { - default: archType = 'Moderator'; embedTitle = 'Cloud Account | Generic'; color = '0892e1'; break; + default: archType = 'Staff'; embedTitle = 'Cloud Account | Generic'; color = '0892e1'; break; case 0: archType = 'Administrator'; embedTitle = 'Cloud Account | Create'; color = '00ff00'; break; - case 1: archType = 'Moderator'; embedTitle = 'Account Warning | Warn'; color = 'ffff00'; break; - case 2: archType = 'Supervisor'; embedTitle = 'Account Infraction | Lock'; color = 'ff6600'; break; - case 3: archType = 'Supervisor'; embedTitle = 'Account Reclaim | Unlock'; color = '0099ff'; break; + case 1: archType = 'Staff'; embedTitle = 'Account Warning | Warn'; color = 'ffff00'; break; + case 2: archType = 'Moderator'; embedTitle = 'Account Infraction | Lock'; color = 'ff6600'; break; + case 3: archType = 'Moderator'; embedTitle = 'Account Reclaim | Unlock'; color = '0099ff'; break; case 4: archType = 'Administrator'; embedTitle = 'Cloud Account | Delete'; color = 'ff0000'; break; } const embed = new RichEmbed() diff --git a/src/commands/announce.ts b/src/commands/announce.ts index de33fb3..f0b271f 100644 --- a/src/commands/announce.ts +++ b/src/commands/announce.ts @@ -9,7 +9,7 @@ export default class Announce extends Command { this.description = 'Sends an announcement to all active terminals'; this.usage = `${this.client.config.prefix}announce Hi there! | ${this.client.config.prefix}announce -e EMERGENCY!`; this.aliases = ['ann']; - this.permissions = { roles: ['608095934399643649', '521312697896271873'] }; + this.permissions = { roles: ['475817826251440128', '525441307037007902'] }; this.enabled = true; } diff --git a/src/commands/cwg_delete.ts b/src/commands/cwg_delete.ts index 3998fab..cf58e16 100644 --- a/src/commands/cwg_delete.ts +++ b/src/commands/cwg_delete.ts @@ -53,7 +53,9 @@ export default class CWG_Delete extends Command { await this.client.util.exec('systemctl reload nginx'); edit.edit(`***${this.client.stores.emojis.success} Domain ${domain.domain} with port ${domain.port} has been successfully deleted.***`); // @ts-ignore - return message.channel.createMessage({ embed }); + this.client.createMessage('580950455581147146', { embed }); + // @ts-ignore + return this.client.getDMChannel(domain.account.userID).then((channel) => channel.createMessage({ embed })).catch(() => {}); } catch (error) { return this.client.util.handleError(error, message, this); } diff --git a/src/commands/deleteaccount.ts b/src/commands/deleteaccount.ts index 3480f74..eb58625 100644 --- a/src/commands/deleteaccount.ts +++ b/src/commands/deleteaccount.ts @@ -1,6 +1,6 @@ import { Message, PrivateChannel } from 'eris'; import uuid from 'uuid/v4'; -import { Command, RichEmbed } from '../class'; +import { Command } from '../class'; import { Client } from '..'; export default class DeleteAccount extends Command { diff --git a/src/commands/disk.ts b/src/commands/disk.ts index fe656c5..2db92e4 100644 --- a/src/commands/disk.ts +++ b/src/commands/disk.ts @@ -3,6 +3,7 @@ import moment from 'moment'; import { Client } from '..'; import { RichEmbed, Command } from '../class'; import { dataConversion } from '../functions'; +// eslint-disable-next-line import/no-unresolved import 'moment-precise-range-plugin'; export default class Disk extends Command { @@ -12,7 +13,7 @@ export default class Disk extends Command { this.description = 'Checks the used disk space by a user'; this.usage = `${this.client.config.prefix}disk [Username/User ID/Email]`; this.permissions = { roles: ['446104438969466890'] }; - this.enabled = true; + this.enabled = false; } async run(message: Message, args: string[]) { diff --git a/src/commands/lock.ts b/src/commands/lock.ts index 1f367c3..18ae3ee 100644 --- a/src/commands/lock.ts +++ b/src/commands/lock.ts @@ -8,7 +8,7 @@ export default class Lock extends Command { super(client); this.name = 'lock'; this.description = 'Locks an account.'; - this.permissions = { roles: ['608095934399643649', '521312697896271873'] }; + this.permissions = { roles: ['455972169449734144', '643619219988152321'] }; this.enabled = true; } diff --git a/src/commands/parse.ts b/src/commands/parse.ts index 1e8c1d7..ef2fef0 100644 --- a/src/commands/parse.ts +++ b/src/commands/parse.ts @@ -18,7 +18,13 @@ export default class Parse extends Command { if (!args.length) return this.client.commands.get('help').run(message, [this.name]); const account = await this.client.db.Account.findOne({ $or: [{ username: args[0] }, { userID: args[0].replace(/[<@!>]/gi, '') }] }); if (!account) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot find user.***`); - const dir = await fs.readdir(`/home/${account.username}/Validation`); + let dir: string[]; + try { + dir = await fs.readdir(`/home/${account.username}/Validation`); + } catch (err) { + return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate Validation directory.***`); + } + if (!dir.length) return message.channel.createMessage(`***${this.client.stores.emojis.error} Cannot locate certificate.***`); const cert = parseCert(`/home/${account.username}/Validation/${dir[0]}`); const subjectCommonName = cert.subject.commonName ? cert.subject.commonName : 'Not Specified'; const subjectEmailAddress = cert.subject.emailAddress ? cert.subject.emailAddress : 'Not Specified'; diff --git a/src/commands/unlock.ts b/src/commands/unlock.ts index 11be5f8..1d04afb 100644 --- a/src/commands/unlock.ts +++ b/src/commands/unlock.ts @@ -8,7 +8,7 @@ export default class Unlock extends Command { super(client); this.name = 'unlock'; this.description = 'Unlocks an account.'; - this.permissions = { roles: ['608095934399643649', '521312697896271873'] }; + this.permissions = { roles: ['455972169449734144', '643619219988152321'] }; this.enabled = true; } diff --git a/src/commands/whois.ts b/src/commands/whois.ts index e109a95..0799ca3 100644 --- a/src/commands/whois.ts +++ b/src/commands/whois.ts @@ -3,6 +3,7 @@ import moment from 'moment'; import { Message } from 'eris'; import { Client } from '..'; import { Command, RichEmbed } from '../class'; +import { dataConversion } from '../functions'; export default class Whois extends Command { constructor(client: Client) { @@ -30,6 +31,11 @@ export default class Whois extends Command { embed.addField('Email Address', account.emailAddress, true); embed.addField('Created By', `<@${this.client.users.get(account.createdBy).id}>`, true); embed.addField('Created At', moment(account.createdAt).format('dddd, MMMM Do YYYY, h:mm:ss A'), true); + const cpuUsage = await this.client.util.exec(`top -b -n 1 -u ${account.username} | awk 'NR>7 { sum += $9; } END { print sum; }'`); + embed.addField('CPU Usage', cpuUsage.split('\n')[0] ? `${cpuUsage.split('\n')[0]}%` : '0%', true); + embed.addField('Memory', dataConversion(Number(await this.client.util.exec(`memory ${account.username}`)) * 1000), true); + const data = await this.client.redis.get(`storage-${account.username}`) ? dataConversion(Number(await this.client.redis.get(`storage-${account.username}`))) : 'N/A'; + embed.addField('Storage', data, true); let details = ''; if (account.locked) details += 'This account is currently locked.\n'; if (account.permissions.engineer) details += 'This account belongs to an Engineer.\n'; diff --git a/src/functions/dataConversion.ts b/src/functions/dataConversion.ts index e38337d..f72a611 100644 --- a/src/functions/dataConversion.ts +++ b/src/functions/dataConversion.ts @@ -1,5 +1,8 @@ -export default function dataConversion(bytes: number) { +export default function dataConversion(bytes) { const i = Math.floor(Math.log(bytes) / Math.log(1024)); const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + if (bytes === 0) { + return '0 KB'; + } return `${(bytes / 1024 ** i).toFixed(2)} ${sizes[i]}`; } diff --git a/src/intervals/storage.ts b/src/intervals/storage.ts new file mode 100644 index 0000000..939edf4 --- /dev/null +++ b/src/intervals/storage.ts @@ -0,0 +1,25 @@ +/* eslint-disable no-await-in-loop */ +import fs from 'fs-extra'; +import { Client } from '..'; + +export default async function storage(client: Client) { + const main = async () => { + const accounts = await client.db.Account.find(); + for (const account of accounts) { + const res = await client.util.exec(`du -bs /home/${account.username}`); + let bytes = Number(res.split('/')[0].replace('\t', '')); + try { + await fs.access(`/var/mail/${account.username}`, fs.constants.F_OK); + const res2 = await client.util.exec(`du -bs /var/mail/${account.username}`); + bytes += Number(res2.split('/')[0].replace('\t', '')); + } catch { + bytes += 0; + } + await client.redis.set(`storage-${account.username}`, bytes); + } + }; + await main(); + setInterval(async () => { + await main(); + }, 900000); +}