changes to localstorage: +gzip compression

merge-requests/15/head
Matthew 2020-07-09 19:09:46 -04:00
parent 5544354727
commit 947140a4c8
No known key found for this signature in database
GPG Key ID: 210AF32ADE3B5C4B
7 changed files with 63 additions and 29 deletions

View File

@ -17,14 +17,14 @@ export default class Client extends eris.Client {
public serverManagement: ServerManagement; public serverManagement: ServerManagement;
public db: { Member: mongoose.Model<MemberInterface>, Moderation: mongoose.Model<ModerationInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Rank: mongoose.Model<RankInterface>, Redirect: mongoose.Model<RedirectInterface>, local: LocalStorage }; public db: { Member: mongoose.Model<MemberInterface>, Moderation: mongoose.Model<ModerationInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Rank: mongoose.Model<RankInterface>, Redirect: mongoose.Model<RedirectInterface>, local: { muted: LocalStorage } };
constructor(token: string, options?: eris.ClientOptions) { constructor(token: string, options?: eris.ClientOptions) {
super(token, options); super(token, options);
this.commands = new Collection<Command>(); this.commands = new Collection<Command>();
this.events = new Collection<Event>(); this.events = new Collection<Event>();
this.intervals = new Collection<NodeJS.Timeout>(); this.intervals = new Collection<NodeJS.Timeout>();
this.db = { Member, Moderation, PagerNumber, Rank, Redirect, local: new LocalStorage(this) }; this.db = { Member, Moderation, PagerNumber, Rank, Redirect, local: { muted: new LocalStorage('muted') } };
} }
public async loadDatabase() { public async loadDatabase() {

View File

@ -1,37 +1,65 @@
/* eslint-disable no-constant-condition */ /* eslint-disable no-constant-condition */
import { promises as fs, constants } from 'fs'; import { promises as fs, accessSync, constants, writeFileSync } from 'fs';
import { Client } from '.'; import { promisify } from 'util';
import { gzip, gzipSync, unzip } from 'zlib';
type JSONData = [{key: string, value: any}?]; type JSONData = [{key: string, value: any}?];
/** /**
* Persistant local JSON-based storage. * Persistant local JSON-based storage.
* Auto-locking system to prevent corrupted data. * - auto-locking system to prevent corrupted data
* - uses gzip compression to keep DB storage space utilization low
* @author Matthew <matthew@staff.libraryofcode.org> * @author Matthew <matthew@staff.libraryofcode.org>
*/ */
export default class LocalStorage { export default class LocalStorage {
private client: Client;
protected storagePath: string; protected storagePath: string;
private locked: boolean = false; private locked: boolean = false;
constructor(client: Client, storagePath = `${__dirname}/../../localstorage`) { constructor(dbName: string) {
this.client = client; this.storagePath = `${__dirname}/../../localstorage/${dbName}.json.gz`;
this.storagePath = storagePath;
this.init(); this.init();
} }
private async init() { private init() {
try { try {
await fs.access(`${this.storagePath}/1.json`, constants.F_OK); accessSync(this.storagePath, constants.F_OK);
} catch { } catch {
const setup = []; const setup = [];
await fs.writeFile(`${this.storagePath}/1.json`, JSON.stringify(setup), { encoding: 'utf8' }); const data = gzipSync(JSON.stringify(setup));
writeFileSync(this.storagePath, data);
} }
} }
/**
* Compresses data using gzip.
* @param data The data to be compressed.
* ```ts
* await LocalStorage.compress('hello!');
* ```
*/
static async compress(data: string): Promise<Buffer> {
const func = promisify(gzip);
const comp = <Buffer> await func(data);
return comp;
}
/**
* Decompresses data using gzip.
* @param data The data to be decompressed.
* ```ts
* const compressed = await LocalStorage.compress('data');
* const decompressed = await LocalStorage.decompress(compressed);
* console.log(decompressed); // logs 'data';
* ```
*/
static async decompress(data: Buffer): Promise<string> {
const func = promisify(unzip);
const uncomp = <Buffer> await func(data);
return uncomp.toString();
}
/** /**
* Retrieves one data from the store. * Retrieves one data from the store.
* If the store has multiple entries for the same key, this function will only return the first entry. * If the store has multiple entries for the same key, this function will only return the first entry.
@ -46,9 +74,10 @@ export default class LocalStorage {
} }
this.locked = true; this.locked = true;
const file = await fs.readFile(`${this.storagePath}/1.json`, { encoding: 'utf8' }); const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
this.locked = false; this.locked = false;
const json: JSONData = JSON.parse(file); const json: JSONData = JSON.parse(uncomp);
const result = json.filter((data) => data.key === key); const result = json.filter((data) => data.key === key);
if (!result[0]) return null; if (!result[0]) return null;
return result[0].value; return result[0].value;
@ -67,9 +96,10 @@ export default class LocalStorage {
} }
this.locked = true; this.locked = true;
const file = await fs.readFile(`${this.storagePath}/1.json`, { encoding: 'utf8' }); const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
this.locked = false; this.locked = false;
const json: JSONData = JSON.parse(file); const json: JSONData = JSON.parse(uncomp);
const result = json.filter((data) => data.key === key); const result = json.filter((data) => data.key === key);
if (result.length < 1) return null; if (result.length < 1) return null;
return result; return result;
@ -90,10 +120,12 @@ export default class LocalStorage {
} }
this.locked = true; this.locked = true;
const file = await fs.readFile(`${this.storagePath}/1.json`, { encoding: 'utf8' }); const file = await fs.readFile(this.storagePath);
const json: JSONData = JSON.parse(file); const uncomp = await LocalStorage.decompress(file);
const json: JSONData = JSON.parse(uncomp);
json.push({ key, value }); json.push({ key, value });
await fs.writeFile(`${this.storagePath}/1.json`, JSON.stringify(json), { encoding: 'utf8' }); const comp = await LocalStorage.compress(JSON.stringify(json));
await fs.writeFile(this.storagePath, comp);
this.locked = false; this.locked = false;
} }
@ -111,10 +143,12 @@ export default class LocalStorage {
} }
this.locked = true; this.locked = true;
const file = await fs.readFile(`${this.storagePath}/1.json`, { encoding: 'utf8' }); const file = await fs.readFile(this.storagePath);
const json: JSONData = JSON.parse(file); const uncomp = await LocalStorage.decompress(file);
const json: JSONData = JSON.parse(uncomp);
const filtered = json.filter((data) => data.key !== key); const filtered = json.filter((data) => data.key !== key);
await fs.writeFile(`${this.storagePath}/1.json`, JSON.stringify(filtered), { encoding: 'utf8' }); const comp = await LocalStorage.compress(JSON.stringify(filtered));
await fs.writeFile(this.storagePath, comp);
this.locked = false; this.locked = false;
} }
} }

View File

@ -134,7 +134,7 @@ export default class Moderation {
} else date = null; } else date = null;
const expiration = { date, processed }; const expiration = { date, processed };
mod.expiration = expiration; mod.expiration = expiration;
await this.client.db.local.set(`muted-${member.id}`, true); await this.client.db.local.muted.set(`muted-${member.id}`, true);
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle(`Case ${logID} | Mute`); embed.setTitle(`Case ${logID} | Mute`);
@ -171,7 +171,7 @@ export default class Moderation {
date: new Date(), date: new Date(),
}); });
await this.client.db.local.del(`muted-${member.id}`); await this.client.db.local.muted.del(`muted-${member.id}`);
const embed = new RichEmbed(); const embed = new RichEmbed();
embed.setTitle(`Case ${logID} | Unmute`); embed.setTitle(`Case ${logID} | Unmute`);

View File

@ -20,7 +20,7 @@ export default class Mute extends Command {
if (!member) return this.error(message.channel, 'Cannot find user.'); if (!member) return this.error(message.channel, 'Cannot find user.');
try { try {
const res1 = await this.client.db.local.get<boolean>(`muted-${member.id}`); const res1 = await this.client.db.local.muted.get<boolean>(`muted-${member.id}`);
if (res1 || this.mainGuild.members.get(member.id).roles.includes('478373942638149643')) return this.error(message.channel, 'This user is already muted.'); if (res1 || this.mainGuild.members.get(member.id).roles.includes('478373942638149643')) return this.error(message.channel, 'This user is already muted.');
} catch {} // eslint-disable-line no-empty } catch {} // eslint-disable-line no-empty
if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.'); if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.');

View File

@ -19,7 +19,7 @@ export default class Unmute extends Command {
if (!member) return this.error(message.channel, 'Cannot find user.'); if (!member) return this.error(message.channel, 'Cannot find user.');
try { try {
const res1 = await this.client.db.local.get<boolean>(`muted-${member.id}`); const res1 = await this.client.db.local.muted.get<boolean>(`muted-${member.id}`);
if (!res1 || !this.mainGuild.members.get(member.id).roles.includes('478373942638149643')) return this.error(message.channel, 'This user is already unmuted.'); if (!res1 || !this.mainGuild.members.get(member.id).roles.includes('478373942638149643')) return this.error(message.channel, 'This user is already unmuted.');
} catch {} // eslint-disable-line no-empty } catch {} // eslint-disable-line no-empty
if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.'); if (member && !this.client.util.moderation.checkPermissions(member, message.member)) return this.error(message.channel, 'Permission Denied.');

View File

@ -11,7 +11,7 @@ export default class GuildMemberAdd extends Event {
public async run(_, member: Member) { public async run(_, member: Member) {
try { try {
const search = await this.client.db.local.get<boolean>(`muted-${member.user.id}`); const search = await this.client.db.local.muted.get<boolean>(`muted-${member.user.id}`);
if (search === true) { if (search === true) {
member.addRole('478373942638149643'); member.addRole('478373942638149643');
} }

View File

@ -18,7 +18,7 @@ export default function checkLock(client: Client): NodeJS.Timeout {
await client.util.moderation.unban(moderation.userID, system); await client.util.moderation.unban(moderation.userID, system);
break; break;
case 2: case 2:
if (await client.db.local.get<boolean>(`muted-${moderation.userID}`) === true) { if (await client.db.local.muted.get<boolean>(`muted-${moderation.userID}`) === true) {
await client.util.moderation.unmute(moderation.userID, system); await client.util.moderation.unmute(moderation.userID, system);
} }
break; break;