mirror of
https://github.com/Wolvan/poll.horse.git
synced 2024-11-21 20:47:59 +01:00
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.
This commit is contained in:
parent
df5b3e5c10
commit
0545d90c3f
8 changed files with 329 additions and 18 deletions
194
package-lock.json
generated
194
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"use strict";
|
||||
|
||||
export const MAX_POLL_OPTIONS = 255;
|
||||
export const MAX_POLL_OPTIONS = 20;
|
||||
export const MAX_CHARACTER_LENGTH = 300;
|
27
src/FlatFileStorage.ts
Normal file
27
src/FlatFileStorage.ts
Normal file
|
@ -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<Poll> {
|
||||
return this.#storage.getItem(key);
|
||||
}
|
||||
|
||||
setItem(key: string, value: Poll, options?: persist.DatumOptions): Promise<persist.WriteFileResult> {
|
||||
return this.#storage.setItem(key, value, options);
|
||||
}
|
||||
}
|
80
src/MySQLStorage.ts
Normal file
80
src/MySQLStorage.ts
Normal file
|
@ -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<this> {
|
||||
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<Poll|null> {
|
||||
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<void> {
|
||||
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()
|
||||
]);
|
||||
}
|
||||
}
|
7
src/Storage.ts
Normal file
7
src/Storage.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
export default abstract class Storage {
|
||||
abstract init(): Promise<this>;
|
||||
abstract getItem(key: string): Promise<any>;
|
||||
abstract setItem(key: string, value: any): Promise<any>;
|
||||
}
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
|||
|
||||
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()) ?
|
||||
|
|
24
src/main.ts
24
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<void> {
|
||||
await loadConfig([
|
||||
|
@ -13,6 +16,13 @@ async function main(): Promise<void> {
|
|||
["--no-backend", "Do not start the backend server"],
|
||||
["-d, --data-directory <path>", "Path to the data directory", "./data"],
|
||||
["-p, --port <port>", "Port to listen on", (port: any) => parseInt(port), 6969],
|
||||
["--use-mysql", "Use MySQL for storage"],
|
||||
["--mysql-host <host>", "MySQL host", "localhost"],
|
||||
["--mysql-port <port>", "MySQL port", (port: any) => parseInt(port), 3306],
|
||||
["--mysql-user <user>", "MySQL user", "root"],
|
||||
["--mysql-password <password>", "MySQL password", "root"],
|
||||
["--mysql-database <database>", "MySQL database", "polls"],
|
||||
["--mysql-ssl", "Use SSL for MySQL connection"],
|
||||
["--backend-base-url <url>", "Base URL for the backend server", null],
|
||||
], ".poll-horse-config");
|
||||
const opts = program.opts();
|
||||
|
@ -23,12 +33,24 @@ async function main(): Promise<void> {
|
|||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue