merge-requests/4/head
Matthew 2020-08-30 22:11:13 -04:00
parent 7d5b56d8cc
commit 8c12ad4086
No known key found for this signature in database
GPG Key ID: 210AF32ADE3B5C4B
5 changed files with 162 additions and 2 deletions

View File

@ -57,6 +57,8 @@ export default class Root extends Route {
try {
res.setHeader('Access-Control-Allow-Origin', '*');
const token = <any> jwt.verify(req.query.t.toString(), this.server.client.config.keyPair.privateKey);
const check = await this.server.storage.get<boolean>(token);
if (!check) return res.sendStatus(401);
const embed = new RichEmbed();
embed.setTitle('Referral Authorization');
embed.addField('Referred User', token.referredUserAndDiscrim, true);
@ -64,6 +66,7 @@ export default class Root extends Route {
embed.addField('Referral Code', token.referralCode, true);
const channel = <TextChannel> this.server.client.guilds.get('446067825673633794').channels.get('580950455581147146');
res.sendStatus(200);
await this.server.storage.set(token, true);
return channel.createMessage({ content: `<@${token.staffUserID}>`, embed });
} catch {
return res.sendStatus(401);

View File

@ -14,7 +14,6 @@
if (response.status === 200) return alert('Request authorized. You may now close this tab.');
if (response.status === 401) return alert('Authorization Token incorrect, try again.');
if (response.status >= 500) return alert('INTERNAL SERVER ERROR');
alert('Authentication Complete.');
} catch (err) {
alert(err);
}

154
src/class/LocalStorage.ts Normal file
View File

@ -0,0 +1,154 @@
/* eslint-disable no-constant-condition */
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
* - uses gzip compression to keep DB storage space utilization low
* @author Matthew <matthew@staff.libraryofcode.org>
*/
export default class LocalStorage {
protected readonly storagePath: string;
protected locked: boolean = false;
constructor(dbName: string, dir = `${__dirname}/../../localstorage`) {
this.storagePath = `${dir}/${dbName}.json.gz`;
this.init();
}
private init() {
try {
accessSync(this.storagePath, constants.F_OK);
} catch {
const setup = [];
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.
* ```ts
* await LocalStorage.get<type>('data-key');
* ```
* @param key The key for the data entry.
*/
public async get<T>(key: string): Promise<T> {
while (true) {
if (!this.locked) break;
}
this.locked = true;
const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
this.locked = false;
const json: JSONData = JSON.parse(uncomp);
const result = json.filter((data) => data.key === key);
if (!result[0]) return null;
return result[0].value;
}
/**
* Retrieves multiple data keys/values from the store.
* This function will return all of the values matching the key you provided exactly. Use `LocalStorage.get();` if possible.
* ```ts
* await LocalStorage.get<type>('data-key');
* @param key The key for the data entry.
*/
public async getMany<T>(key: string): Promise<{key: string, value: T}[]> {
while (true) {
if (!this.locked) break;
}
this.locked = true;
const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
this.locked = false;
const json: JSONData = JSON.parse(uncomp);
const result = json.filter((data) => data.key === key);
if (result.length < 1) return null;
return result;
}
/**
* Sets a key/value pair and creates a new data entry.
* @param key The key for the data entry.
* @param value The value for the data entry, can be anything that is valid JSON.
* @param options.override [DEPRECATED] By default, this function will error if the key you're trying to set already exists. Set this option to true to override that setting.
* ```ts
* await LocalStorage.set('data-key', 'test');
* ```
*/
public async set(key: string, value: any): Promise<void> {
while (true) {
if (!this.locked) break;
}
this.locked = true;
const file = await fs.readFile(this.storagePath);
const uncomp = await LocalStorage.decompress(file);
const json: JSONData = JSON.parse(uncomp);
json.push({ key, value });
const comp = await LocalStorage.compress(JSON.stringify(json));
await fs.writeFile(this.storagePath, comp);
this.locked = false;
}
/**
* Deletes the data for the specified key.
* **Warning:** This function will delete ALL matching entries.
* ```ts
* await LocalStorage.del('data-key');
* ```
* @param key The key for the data entry.
*/
public async del(key: string): Promise<void> {
while (true) {
if (!this.locked) break;
}
this.locked = true;
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);
const comp = await LocalStorage.compress(JSON.stringify(filtered));
await fs.writeFile(this.storagePath, comp);
this.locked = false;
}
}

View File

@ -3,7 +3,7 @@ import express from 'express';
import bodyParser from 'body-parser';
import helmet from 'helmet';
import fs from 'fs-extra';
import { Client, Collection, Route } from '.';
import { Client, Collection, LocalStorage, Route } from '.';
import { Security } from '../api';
@ -16,6 +16,8 @@ export default class Server {
public app: express.Express;
public storage: LocalStorage;
public options: { port: number }
constructor(client: Client, options?: { port: number }) {
@ -24,6 +26,7 @@ export default class Server {
this.client = client;
this.security = new Security(this.client);
this.app = express();
this.storage = new LocalStorage('usedra', '/opt/CloudServices/localstorage');
this.connect();
this.loadRoutes();
}

View File

@ -3,6 +3,7 @@ export { default as Client } from './Client';
export { default as Collection } from './Collection';
export { default as Command } from './Command';
export { default as Event } from './Event';
export { default as LocalStorage } from './LocalStorage';
export { default as RichEmbed } from './RichEmbed';
export { default as Route } from './Route';
export { default as Security } from './Security';