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 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) {
super(token, options);
this.commands = new Collection<Command>();
this.events = new Collection<Event>();
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() {

View File

@ -1,37 +1,65 @@
/* eslint-disable no-constant-condition */
import { promises as fs, constants } from 'fs';
import { Client } from '.';
import { promises as fs, accessSync, constants, writeFileSync } from 'fs';
import { promisify } from 'util';
import { gzip, gzipSync, unzip } from 'zlib';
type JSONData = [{key: string, value: any}?];
/**
* 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>
*/
export default class LocalStorage {
private client: Client;
protected storagePath: string;
private locked: boolean = false;
constructor(client: Client, storagePath = `${__dirname}/../../localstorage`) {
this.client = client;
this.storagePath = storagePath;
constructor(dbName: string) {
this.storagePath = `${__dirname}/../../localstorage/${dbName}.json.gz`;
this.init();
}
private async init() {
private init() {
try {
await fs.access(`${this.storagePath}/1.json`, constants.F_OK);
accessSync(this.storagePath, constants.F_OK);
} catch {
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.
* 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;
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;
const json: JSONData = JSON.parse(file);
const json: JSONData = JSON.parse(uncomp);
const result = json.filter((data) => data.key === key);
if (!result[0]) return null;
return result[0].value;
@ -67,9 +96,10 @@ export default class LocalStorage {
}
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;
const json: JSONData = JSON.parse(file);
const json: JSONData = JSON.parse(uncomp);
const result = json.filter((data) => data.key === key);
if (result.length < 1) return null;
return result;
@ -90,10 +120,12 @@ export default class LocalStorage {
}
this.locked = true;
const file = await fs.readFile(`${this.storagePath}/1.json`, { encoding: 'utf8' });
const json: JSONData = JSON.parse(file);
const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
const json: JSONData = JSON.parse(uncomp);
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;
}
@ -111,10 +143,12 @@ export default class LocalStorage {
}
this.locked = true;
const file = await fs.readFile(`${this.storagePath}/1.json`, { encoding: 'utf8' });
const json: JSONData = JSON.parse(file);
const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
const json: JSONData = JSON.parse(uncomp);
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;
}
}

View File

@ -134,7 +134,7 @@ export default class Moderation {
} else date = null;
const expiration = { date, processed };
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();
embed.setTitle(`Case ${logID} | Mute`);
@ -171,7 +171,7 @@ export default class Moderation {
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();
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.');
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.');
} catch {} // eslint-disable-line no-empty
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.');
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.');
} catch {} // eslint-disable-line no-empty
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) {
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) {
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);
break;
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);
}
break;