From 0545d90c3fbd1cfba20e7d3c36e46596ec815838 Mon Sep 17 00:00:00 2001 From: Wolvan Date: Sat, 8 Jan 2022 14:32:25 +0100 Subject: [PATCH] Implement different backend types Including to the previous flatfile storage, a new storage for MySQL has been added to store polls in a database. The amount of possible Poll Options has also been reduced to 20. --- package-lock.json | 194 +++++++++++++++++++++++++++++++++++++++-- package.json | 1 + src/Config.ts | 2 +- src/FlatFileStorage.ts | 27 ++++++ src/MySQLStorage.ts | 80 +++++++++++++++++ src/Storage.ts | 7 ++ src/backend.ts | 12 +-- src/main.ts | 24 ++++- 8 files changed, 329 insertions(+), 18 deletions(-) create mode 100644 src/FlatFileStorage.ts create mode 100644 src/MySQLStorage.ts create mode 100644 src/Storage.ts diff --git a/package-lock.json b/package-lock.json index 43d127c..293fc62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "compression": "^1.7.4", "cookie-parser": "^1.4.6", "express": "^4.17.2", + "mysql2": "^2.3.3", "node-fetch": "^2.6.6", "node-persist": "^3.1.0" }, @@ -1090,6 +1091,14 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1680,6 +1689,14 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1974,6 +1991,11 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -2087,11 +2109,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2301,6 +2327,60 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "dependencies": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "dependencies": { + "lru-cache": "^4.1.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/named-placeholders/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, "node_modules/nanoid": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", @@ -2548,6 +2628,11 @@ "node": ">= 0.10" } }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -2812,6 +2897,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -2889,6 +2979,14 @@ "source-map": "^0.6.0" } }, + "node_modules/sqlstring": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz", + "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -3375,8 +3473,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "16.2.0", @@ -4260,6 +4357,11 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -4726,6 +4828,14 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4938,6 +5048,11 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -5022,11 +5137,15 @@ "is-unicode-supported": "^0.1.0" } }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -5179,6 +5298,55 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "requires": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "requires": { + "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, "nanoid": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", @@ -5351,6 +5519,11 @@ "ipaddr.js": "1.9.1" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -5522,6 +5695,11 @@ } } }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -5584,6 +5762,11 @@ "source-map": "^0.6.0" } }, + "sqlstring": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz", + "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg==" + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -5918,8 +6101,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { "version": "16.2.0", diff --git a/package.json b/package.json index 4d8fa0d..e7b7bcd 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "compression": "^1.7.4", "cookie-parser": "^1.4.6", "express": "^4.17.2", + "mysql2": "^2.3.3", "node-fetch": "^2.6.6", "node-persist": "^3.1.0" } diff --git a/src/Config.ts b/src/Config.ts index aceafac..2070724 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -1,4 +1,4 @@ "use strict"; -export const MAX_POLL_OPTIONS = 255; +export const MAX_POLL_OPTIONS = 20; export const MAX_CHARACTER_LENGTH = 300; \ No newline at end of file diff --git a/src/FlatFileStorage.ts b/src/FlatFileStorage.ts new file mode 100644 index 0000000..dc70cb8 --- /dev/null +++ b/src/FlatFileStorage.ts @@ -0,0 +1,27 @@ +"use strict"; + +import Storage from "./Storage"; +import persist from "node-persist"; +import { BackendPoll as Poll } from "./Poll"; + +export default class FlatFileStorage extends Storage { + #storage: persist.LocalStorage; + constructor(options: persist.InitOptions) { + super(); + console.debug("Initiating FlatFileStorage."); + this.#storage = persist.create(options); + } + + async init() { + await this.#storage.init(); + return this; + } + + getItem(key: string): Promise { + return this.#storage.getItem(key); + } + + setItem(key: string, value: Poll, options?: persist.DatumOptions): Promise { + return this.#storage.setItem(key, value, options); + } +} \ No newline at end of file diff --git a/src/MySQLStorage.ts b/src/MySQLStorage.ts new file mode 100644 index 0000000..4eebccb --- /dev/null +++ b/src/MySQLStorage.ts @@ -0,0 +1,80 @@ +"use strict"; + +import Storage from "./Storage"; +import mysql from "mysql2"; +import { MAX_CHARACTER_LENGTH } from "./Config"; +import { BackendPoll as Poll } from "./Poll"; + +export default class MySQLStorage extends Storage { + #db: mysql.Connection; + constructor(options: mysql.ConnectionOptions) { + super(); + console.debug("Initiating MySQLStorage."); + this.#db = mysql.createConnection(options); + } + + async init(): Promise { + await this.#db.promise().connect(); + await this.#db.promise().query(` + CREATE TABLE IF NOT EXISTS polls ( + id INT AUTO_INCREMENT PRIMARY KEY, + id_str VARCHAR(32) NOT NULL UNIQUE, + title VARCHAR(${MAX_CHARACTER_LENGTH}) NOT NULL DEFAULT '', + dupe_check_mode ENUM('none', 'ip', 'cookie') NOT NULL DEFAULT 'ip', + multi_select TINYINT(1) NOT NULL DEFAULT 0, + captcha TINYINT(1) NOT NULL DEFAULT 0, + creation_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + dupe_data VARCHAR(8000) DEFAULT '[]', + options VARCHAR(32000) NOT NULL DEFAULT '{}', + deleted_at DATETIME DEFAULT NULL, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=INNODB; + `); + return this; + } + + async getItem(key: string): Promise { + const [rows] = await this.#db.promise().execute("SELECT * FROM polls WHERE id_str = ? AND deleted_at IS NULL;", [key]); + if (!rows || !Array.isArray(rows) || !rows.length) return null; + const row = rows[0] as { + id_str: string, + title: string, + dupe_check_mode: "none" | "ip" | "cookie", + multi_select: number, + captcha: number, + creation_time: string, + dupe_data: string, + options: string + }; + return { + id: row.id_str, + title: row.title, + dupeCheckMode: row.dupe_check_mode, + multiSelect: !!row.multi_select, + captcha: !!row.captcha, + creationTime: new Date(row.creation_time), + dupeData: JSON.parse(row.dupe_data), + options: JSON.parse(row.options) + }; + } + + async setItem(key: string, value: Poll): Promise { + await this.#db.promise().execute(` + INSERT INTO polls + (id_str, title, dupe_check_mode, multi_select, captcha, dupe_data, options, creation_time) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + dupe_data = VALUES(dupe_data), + options = VALUES(options); + `, [ + key, + value.title, + value.dupeCheckMode, + value.multiSelect, + value.captcha, + JSON.stringify(value.dupeData), + JSON.stringify(value.options), + value.creationTime.toISOString() + ]); + } +} \ No newline at end of file diff --git a/src/Storage.ts b/src/Storage.ts new file mode 100644 index 0000000..6c52018 --- /dev/null +++ b/src/Storage.ts @@ -0,0 +1,7 @@ +"use strict"; + +export default abstract class Storage { + abstract init(): Promise; + abstract getItem(key: string): Promise; + abstract setItem(key: string, value: any): Promise; +} \ No newline at end of file diff --git a/src/backend.ts b/src/backend.ts index 3766fe0..43c5979 100644 --- a/src/backend.ts +++ b/src/backend.ts @@ -1,11 +1,9 @@ "use strict"; import { CookieOptions, Router } from "express"; -import persist from "node-persist"; -import { program } from "commander"; -import { resolve } from "path"; import { BackendPoll as Poll, DupeCheckMode } from "./Poll"; import { MAX_POLL_OPTIONS, MAX_CHARACTER_LENGTH } from "./Config"; +import Storage from "./Storage"; function randomString(length = 10, charset = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789") { let result = ""; @@ -15,12 +13,7 @@ function randomString(length = 10, charset = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKL return result; } -export default async function init(router: Router): Promise { - const polls = await persist.create({ - dir: resolve(process.cwd(), program.opts().dataDirectory) - }); - await polls.init(); - +export default async function init(router: Router, polls: Storage): Promise { router.get("/poll/:id", async (req, res) => { try { const id = req.params.id; @@ -75,7 +68,6 @@ export default async function init(router: Router): Promise { let id = randomString(8); while (await polls.getItem(id)) id = randomString(6); - await polls.setItem(id, {}); const dupeCheckMode = ( ["none", "ip", "cookie"].includes((pollData.dupeCheckMode || "").toLowerCase()) ? diff --git a/src/main.ts b/src/main.ts index ada02b9..0ef09d3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,9 @@ import express from "express"; import compression from "compression"; import cookiepaser from "cookie-parser"; import { resolve } from "path"; +import FlatFileStorage from "./FlatFileStorage"; +import Storage from "./Storage"; +import MySQLStorage from "./MySQLStorage"; async function main(): Promise { await loadConfig([ @@ -13,6 +16,13 @@ async function main(): Promise { ["--no-backend", "Do not start the backend server"], ["-d, --data-directory ", "Path to the data directory", "./data"], ["-p, --port ", "Port to listen on", (port: any) => parseInt(port), 6969], + ["--use-mysql", "Use MySQL for storage"], + ["--mysql-host ", "MySQL host", "localhost"], + ["--mysql-port ", "MySQL port", (port: any) => parseInt(port), 3306], + ["--mysql-user ", "MySQL user", "root"], + ["--mysql-password ", "MySQL password", "root"], + ["--mysql-database ", "MySQL database", "polls"], + ["--mysql-ssl", "Use SSL for MySQL connection"], ["--backend-base-url ", "Base URL for the backend server", null], ], ".poll-horse-config"); const opts = program.opts(); @@ -23,12 +33,24 @@ async function main(): Promise { app.use(compression()); app.use(cookiepaser()); + const storage: Storage = (opts.useMysql) ? + new MySQLStorage({ + host: opts.mysqlHost, + port: opts.mysqlPort, + user: opts.mysqlUser, + password: opts.mysqlPassword, + database: opts.mysqlDatabase, + ssl: opts.mysqlSsl + }) : + new FlatFileStorage({ dir: resolve(process.cwd(), program.opts().dataDirectory) }); + await storage.init(); + if (opts.backend) { console.log(`Mounting backend`); const backendRouter = express.Router(); const backend = await import("./backend"); - await backend.default(backendRouter); + await backend.default(backendRouter, storage); app.use("/_backend/", backendRouter); }