mirror of
https://github.com/janishutz/MusicPlayerV2.git
synced 2025-11-25 04:54:23 +00:00
add login sdk
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
|||||||
*.secret.json
|
*.secret.json
|
||||||
apple_private_key.p8
|
apple_private_key.p8
|
||||||
musicplayerv2-server.zip
|
musicplayerv2-server.zip
|
||||||
|
dist
|
||||||
6
backend/config/sdk.config.testing.json
Normal file
6
backend/config/sdk.config.testing.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"token": "phafowegoväbwpb$weapvbpvfwcvfäawef39'ü0wtäqgpt5^ü62q'ẗ9wäa3g",
|
||||||
|
"name": "localhost:8082",
|
||||||
|
"client": "localhost:8081",
|
||||||
|
"backendURL": "http://localhost:8080"
|
||||||
|
}
|
||||||
49
backend/dist/app.js
vendored
49
backend/dist/app.js
vendored
@@ -1,49 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
const express_1 = __importDefault(require("express"));
|
|
||||||
const path_1 = __importDefault(require("path"));
|
|
||||||
const fs_1 = __importDefault(require("fs"));
|
|
||||||
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
||||||
const cors_1 = __importDefault(require("cors"));
|
|
||||||
if (typeof (__dirname) === 'undefined') {
|
|
||||||
__dirname = path_1.default.resolve(path_1.default.dirname(''));
|
|
||||||
}
|
|
||||||
const run = () => {
|
|
||||||
let app = (0, express_1.default)();
|
|
||||||
app.use((0, cors_1.default)({
|
|
||||||
credentials: true,
|
|
||||||
origin: true
|
|
||||||
}));
|
|
||||||
app.get('/', (request, response) => {
|
|
||||||
response.send('HELLO WORLD');
|
|
||||||
});
|
|
||||||
app.get('/getAppleMusicDevToken', (req, res) => {
|
|
||||||
// sign dev token
|
|
||||||
const privateKey = fs_1.default.readFileSync(path_1.default.join(__dirname + '/config/apple_private_key.p8')).toString();
|
|
||||||
// TODO: Remove secret
|
|
||||||
const config = JSON.parse('' + fs_1.default.readFileSync(path_1.default.join(__dirname + '/config/apple-music-api.config.secret.json')));
|
|
||||||
const now = new Date().getTime();
|
|
||||||
const tomorrow = now + 24 * 3600 * 1000;
|
|
||||||
const jwtToken = jsonwebtoken_1.default.sign({
|
|
||||||
'iss': config.teamID,
|
|
||||||
'iat': Math.floor(now / 1000),
|
|
||||||
'exp': Math.floor(tomorrow / 1000),
|
|
||||||
}, privateKey, {
|
|
||||||
algorithm: "ES256",
|
|
||||||
keyid: config.keyID
|
|
||||||
});
|
|
||||||
res.send(jwtToken);
|
|
||||||
});
|
|
||||||
app.use((request, response, next) => {
|
|
||||||
response.status(404).send('ERR_NOT_FOUND');
|
|
||||||
// response.sendFile( path.join( __dirname + '' ) )
|
|
||||||
});
|
|
||||||
const PORT = process.env.PORT || 8081;
|
|
||||||
app.listen(PORT);
|
|
||||||
};
|
|
||||||
exports.default = {
|
|
||||||
run
|
|
||||||
};
|
|
||||||
150
backend/package-lock.json
generated
150
backend/package-lock.json
generated
@@ -16,12 +16,30 @@
|
|||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"jsonwebtoken": "^9.0.2"
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"node-mysql": "^0.4.2",
|
||||||
|
"oauth-janishutz-client-server": "file:../../oauth/client/server/dist"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"../../oauth/client/server/dist": {
|
||||||
|
"name": "oauth-janishutz-client-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/body-parser": "^1.19.5",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/express-session": "^1.18.0",
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"express-session": "^1.18.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.5",
|
"version": "1.19.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||||
@@ -156,6 +174,20 @@
|
|||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/better-js-class": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/better-js-class/-/better-js-class-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-EMgtYym2RSQ75pUfOqKmCDL5EKur62Ec83aggzkJ942RM/mwskb6QDLaJJz93EwSK1dsfkPUFe+nlmxEbtBKDQ=="
|
||||||
|
},
|
||||||
|
"node_modules/bignumber.js": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||||
@@ -250,6 +282,12 @@
|
|||||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cors": {
|
"node_modules/cors": {
|
||||||
"version": "2.8.5",
|
"version": "2.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
@@ -263,6 +301,11 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cps": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cps/-/cps-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-kFE6vY9PBrN1sogxTxWa8ktnmfcKeR2I7+8tQgxqWndSgToerwITuprBs4FeQWEeMf+IGxo+ILixT/RK0wcIPQ=="
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
@@ -577,6 +620,12 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jsonwebtoken": {
|
"node_modules/jsonwebtoken": {
|
||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||||
@@ -731,6 +780,27 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/mysql": {
|
||||||
|
"version": "2.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
|
||||||
|
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": "9.0.0",
|
||||||
|
"readable-stream": "2.3.7",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"sqlstring": "2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mysql/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
@@ -740,6 +810,21 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-mysql": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-mysql/-/node-mysql-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-V/AVfW/d47Wsb2c8AfEu9Q8PiHUG7/3SbDJ03HFycMMrlKSXj5bNItnHgjSSA6N60/1JXmhoJYXJESSS5BYV9A==",
|
||||||
|
"dependencies": {
|
||||||
|
"better-js-class": "*",
|
||||||
|
"cps": "*",
|
||||||
|
"mysql": "*",
|
||||||
|
"underscore": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/oauth-janishutz-client-server": {
|
||||||
|
"resolved": "../../oauth/client/server/dist",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -785,6 +870,12 @@
|
|||||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -837,6 +928,27 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "2.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
|
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
@@ -961,6 +1073,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sqlstring": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/statuses": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
@@ -970,6 +1091,21 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
@@ -1006,6 +1142,12 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/underscore": {
|
||||||
|
"version": "1.13.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
|
||||||
|
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "5.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
@@ -1021,6 +1163,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"jsonwebtoken": "^9.0.2"
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"node-mysql": "^0.4.2",
|
||||||
|
"oauth-janishutz-client-server": "file:../../oauth/client/server/dist"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
backend/src/account.ts
Normal file
50
backend/src/account.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import db from './storage/db';
|
||||||
|
|
||||||
|
|
||||||
|
const createUser = ( uid: string, username: string, email: string ): Promise<boolean> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
db.writeDataSimple( 'users', 'uid', uid, { 'uid': uid, 'username': username, 'email': email } ).then( () => {
|
||||||
|
resolve( true );
|
||||||
|
} ).catch( err => {
|
||||||
|
reject( err );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveUserData = ( uid: string, data: object ): Promise<boolean> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
db.writeDataSimple( 'users', 'uid', uid, { 'data': data } ).then( () => {
|
||||||
|
resolve( true );
|
||||||
|
} ).catch( err => {
|
||||||
|
reject( err );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkUser = ( uid: string ): Promise<boolean> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
db.checkDataAvailability( 'users', 'uid', uid ).then( res => {
|
||||||
|
resolve( res );
|
||||||
|
} ).catch( err => {
|
||||||
|
reject( err );
|
||||||
|
} )
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const getUserData = ( uid: string ): Promise<object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
db.getDataSimple( 'users', 'uid', uid ).then( data => {
|
||||||
|
resolve( data );
|
||||||
|
} ).catch( err => {
|
||||||
|
reject( err );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createUser,
|
||||||
|
saveUserData,
|
||||||
|
checkUser,
|
||||||
|
getUserData
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import bodyParser from 'body-parser';
|
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
import account from './account';
|
||||||
|
import sdk from 'oauth-janishutz-client-server';
|
||||||
|
|
||||||
declare let __dirname: string | undefined
|
declare let __dirname: string | undefined
|
||||||
if ( typeof( __dirname ) === 'undefined' ) {
|
if ( typeof( __dirname ) === 'undefined' ) {
|
||||||
__dirname = path.resolve( path.dirname( '' ) );
|
__dirname = path.resolve( path.dirname( '' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Change config file, as well as in main.ts, index.html, oauth, if deploying there
|
||||||
|
const sdkConfig = JSON.parse( '' + fs.readFileSync( path.join( __dirname + '/config/sdk.config.testing.json' ) ) );
|
||||||
|
// const sdkConfig: AuthSDKConfig = JSON.parse( '' + fs.readFileSync( path.join( __dirname + '/config/sdk.config.secret.json' ) ) );
|
||||||
|
|
||||||
const run = () => {
|
const run = () => {
|
||||||
let app = express();
|
let app = express();
|
||||||
app.use( cors( {
|
app.use( cors( {
|
||||||
@@ -17,8 +22,29 @@ const run = () => {
|
|||||||
origin: true
|
origin: true
|
||||||
} ) );
|
} ) );
|
||||||
|
|
||||||
|
// Load id.janishutz.com SDK and allow signing in
|
||||||
|
sdk.routes( app, ( uid: string ) => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
account.checkUser( uid ).then( stat => {
|
||||||
|
resolve( stat );
|
||||||
|
} ).catch( e => {
|
||||||
|
reject( e );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
( uid: string, email: string, username: string ) => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
account.createUser( uid, username, email ).then( stat => {
|
||||||
|
resolve( stat );
|
||||||
|
} ).catch( e => {
|
||||||
|
reject( e );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}, sdkConfig );
|
||||||
|
|
||||||
|
|
||||||
app.get( '/', ( request, response ) => {
|
app.get( '/', ( request, response ) => {
|
||||||
response.send( 'HELLO WORLD' );
|
response.send( 'Please visit <a href="https://music.janishutz.com">https://music.janishutz.com</a> to use this service' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
330
backend/src/storage/db.ts
Normal file
330
backend/src/storage/db.ts
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
* libreevent - db.js
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 03/26/2023, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import * as sqlDB from './mysqldb.js';
|
||||||
|
|
||||||
|
declare let __dirname: string | undefined
|
||||||
|
if ( typeof( __dirname ) === 'undefined' ) {
|
||||||
|
__dirname = path.resolve( path.dirname( '' ) );
|
||||||
|
} else {
|
||||||
|
__dirname = __dirname + '/../';
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbRef = {
|
||||||
|
'user': 'jh_store_users',
|
||||||
|
'users': 'jh_store_users',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let dbh = new sqlDB.SQLDB();
|
||||||
|
dbh.connect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize database (create tables, etc)
|
||||||
|
* @returns {undefined}
|
||||||
|
*/
|
||||||
|
const initDB = (): undefined => {
|
||||||
|
( async() => {
|
||||||
|
console.log( '[ DB ] Setting up...' );
|
||||||
|
dbh.setupDB();
|
||||||
|
console.log( '[ DB ] Setting up complete!' );
|
||||||
|
} )();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve data from the database
|
||||||
|
* @param {string} db The name of the database
|
||||||
|
* @param {string} column The name of the column of the data-table in which to search for the searchQuery
|
||||||
|
* @param {string} searchQuery The query for the selected column
|
||||||
|
* @returns {Promise<object>} Returns a promise that resolves to an object containing the results.
|
||||||
|
*/
|
||||||
|
const getDataSimple = ( db: string, column: string, searchQuery: string ): Promise<object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
dbh.query( { 'command': 'getFilteredData', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( data => {
|
||||||
|
resolve( data );
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the SQL LeftJoin function to obtain data from DB.
|
||||||
|
* @param {string} db DB name to get data from
|
||||||
|
* @param {string} column The column in the DB in which to search for the searchQuery
|
||||||
|
* @param {string} searchQuery The data to look for in the selected column
|
||||||
|
* @param {string} secondTable The second table on which to perform the left join function
|
||||||
|
* @param {object} columns The columns to return, list of objects: { 'db': TABLE NAME, 'column': COLUMN NAME })
|
||||||
|
* @param {string} nameOfMatchingParam Which properties should be matched to get the data, e.g. order.user_id=users.id
|
||||||
|
* @returns {Promise<Object | Error>} Returns all records from the db and all matching data specified with the matchingParam from the secondTable.
|
||||||
|
*/
|
||||||
|
const getDataWithLeftJoinFunction = ( db: string, column: string, searchQuery: string, secondTable: string, columns: object, nameOfMatchingParam: string ): Promise<Object> => {
|
||||||
|
/*
|
||||||
|
LeftJoin (Select values in first table and return all corresponding values of second table):
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
- operation.columns (The columns of both tables to be selected, list of objects: { 'db': TABLE NAME, 'column': COLUMN NAME })
|
||||||
|
- operation.secondTable (The second table to perform Join operation with)
|
||||||
|
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
|
||||||
|
*/
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
let settings = {
|
||||||
|
'command': 'LeftJoin',
|
||||||
|
'property': column,
|
||||||
|
'searchQuery': searchQuery,
|
||||||
|
'selection': '',
|
||||||
|
'secondTable': dbRef[ secondTable ],
|
||||||
|
'matchingParam': dbRef[ db ] + '.' + nameOfMatchingParam + '=' + dbRef[ secondTable ] + '.' + nameOfMatchingParam,
|
||||||
|
}
|
||||||
|
for ( let el in columns ) {
|
||||||
|
settings.selection += dbRef[ columns[ el ].db ] + '.' + columns[ el ].column + ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.selection = settings.selection.slice( 0, settings.selection.length - 1 );
|
||||||
|
dbh.query( settings, dbRef[ db ] ).then( data => {
|
||||||
|
resolve( data );
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all data from the selected database
|
||||||
|
* @param {string} db The database of which all data should be retrieved
|
||||||
|
* @returns {Promise<object>} Returns an object containing all data
|
||||||
|
*/
|
||||||
|
const getData = ( db: string ): Promise<Object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
dbh.query( { 'command': 'getAllData' }, dbRef[ db ] ).then( data => {
|
||||||
|
resolve( data );
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the database
|
||||||
|
* @param {string} db The database in which the data should be written
|
||||||
|
* @param {string} column The column in which to search for the data
|
||||||
|
* @param {string} searchQuery The query with which to search
|
||||||
|
* @param {string} data The data to write. Also include the column & searchQuery parameters, if they also need to be added
|
||||||
|
* @returns {Promise<object>} Returns a promise that resolves to the interaction module return.
|
||||||
|
*/
|
||||||
|
const writeDataSimple = ( db: string, column: string, searchQuery: string, data: any ): Promise<Object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
dbh.query( { 'command': 'checkDataAvailability', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( res => {
|
||||||
|
if ( res.length > 0 ) {
|
||||||
|
dbh.query( { 'command': 'updateData', 'property': column, 'searchQuery': searchQuery, 'newValues': data }, dbRef[ db ] ).then( dat => {
|
||||||
|
resolve( dat );
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} else {
|
||||||
|
dbh.query( { 'command': 'addData', 'data': data }, dbRef[ db ] ).then( dat => {
|
||||||
|
resolve( dat );
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete data from the database
|
||||||
|
* @param {string} db The database from which the data should be deleted
|
||||||
|
* @param {string} column The column in which to search for the data
|
||||||
|
* @param {string} searchQuery The query with which to search
|
||||||
|
* @returns {Promise<object>} Returns a promise that resolves to the interaction module return.
|
||||||
|
*/
|
||||||
|
const deleteDataSimple = ( db: string, column: string, searchQuery: string ): Promise<object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
dbh.query( { 'command': 'deleteData', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( dat => {
|
||||||
|
resolve( dat );
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the data is available in the database.
|
||||||
|
* @param {string} db The database in which to check
|
||||||
|
* @param {string} column The column in which to search for the data
|
||||||
|
* @param {string} searchQuery The query with which to search
|
||||||
|
* @returns {Promise<boolean>} Returns a promise that resolves to a boolean (true = is available)
|
||||||
|
*/
|
||||||
|
const checkDataAvailability = ( db: string, column: string, searchQuery: string ): Promise<boolean> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
dbh.query( { 'command': 'checkDataAvailability', 'property': column, 'searchQuery': searchQuery }, dbRef[ db ] ).then( res => {
|
||||||
|
if ( res.length > 0 ) {
|
||||||
|
resolve( true );
|
||||||
|
} else {
|
||||||
|
resolve( false );
|
||||||
|
}
|
||||||
|
} ).catch( error => {
|
||||||
|
reject( error );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load multiple JSON files at once
|
||||||
|
* @param {Array<string>} files The files which to load
|
||||||
|
* @returns {Promise<object>} Returns the data from all files
|
||||||
|
*/
|
||||||
|
const getJSONDataBatch = async ( files: Array<string> ): Promise<object> => {
|
||||||
|
let allFiles = {};
|
||||||
|
for ( let file in files ) {
|
||||||
|
try {
|
||||||
|
allFiles[ files[ file ] ] = await getJSONData( files[ file ] );
|
||||||
|
} catch( err ) {
|
||||||
|
allFiles[ files[ file ] ] = 'ERROR: ' + err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all data from a JSON file
|
||||||
|
* @param {string} file The file to load (just file name, file must be in "/data/" folder, no file extension)
|
||||||
|
* @returns {Promise<object>} The data that was loaded
|
||||||
|
*/
|
||||||
|
const getJSONData = ( file: string ): Promise<object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
fs.readFile( path.join( __dirname + '/' + file + '.json' ), ( error, data ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
} else {
|
||||||
|
if ( data.byteLength > 0 ) {
|
||||||
|
resolve( JSON.parse( data.toString() ) ?? {} );
|
||||||
|
} else {
|
||||||
|
resolve( { } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load some data from a JSON file
|
||||||
|
* @param {string} file The file to load (just file name, file must be in "/data/" folder, no file extension)
|
||||||
|
* @param {string} identifier The identifier of the element which should be loaded
|
||||||
|
* @returns {Promise<object>} The data that was loaded
|
||||||
|
*/
|
||||||
|
const getJSONDataSimple = ( file: string, identifier: string ): Promise<object> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
fs.readFile( path.join( __dirname + '/' + file + '.json' ), ( error, data ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
} else {
|
||||||
|
if ( data.byteLength > 0 ) {
|
||||||
|
resolve( JSON.parse( data.toString() )[ identifier ] ?? {} );
|
||||||
|
} else {
|
||||||
|
resolve( { } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get JSON data, but synchronous (blocking)
|
||||||
|
* @param {string} file The file to be loaded (path relative to root)
|
||||||
|
* @returns {object} Returns the JSON file
|
||||||
|
*/
|
||||||
|
const getJSONDataSync = ( file: string ): Object => {
|
||||||
|
return JSON.parse( fs.readFileSync( path.join( __dirname + '/' + file ) ).toString() );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description
|
||||||
|
* @param {any} db:string
|
||||||
|
* @param {any} identifier:string
|
||||||
|
* @param {any} values:any
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
const writeJSONDataSimple = ( db: string, identifier: string, values: any ) => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
fs.readFile( path.join( __dirname + '/../../data/' + db + '.json' ), ( error, data ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
} else {
|
||||||
|
let dat = {};
|
||||||
|
if ( data.byteLength > 0 ) {
|
||||||
|
dat = JSON.parse( data.toString() ) ?? {};
|
||||||
|
}
|
||||||
|
dat[ identifier ] = values;
|
||||||
|
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( dat ), ( error ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
}
|
||||||
|
resolve( true );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to a JSON file
|
||||||
|
* @param {string} db The database to write into
|
||||||
|
* @param {object} data The data to write
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
const writeJSONData = ( db: string, data: object ): Promise<boolean> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( data ), ( error ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
} else {
|
||||||
|
resolve( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete data from a JSON file
|
||||||
|
* @param {string} db The file to delete from (just filename, has to be in "/data/" folder, no file extension)
|
||||||
|
* @param {string} identifier The identifier of the element to delete
|
||||||
|
* @returns {Promise<boolean>} Returns a promise that resolves to a boolean
|
||||||
|
*/
|
||||||
|
const deleteJSONDataSimple = ( db: string, identifier: string ): Promise<boolean> => {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
fs.readFile( path.join( __dirname + '/../../data/' + db + '.json' ), ( error, data ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
} else {
|
||||||
|
let dat = {};
|
||||||
|
if ( data.byteLength > 0 ) {
|
||||||
|
dat = JSON.parse( data.toString() ) ?? {};
|
||||||
|
}
|
||||||
|
delete dat[ identifier ];
|
||||||
|
fs.writeFile( path.join( __dirname + '/../../data/' + db + '.json' ), JSON.stringify( dat ), ( error ) => {
|
||||||
|
if ( error ) {
|
||||||
|
reject( 'Error occurred: Error trace: ' + error );
|
||||||
|
}
|
||||||
|
resolve( true );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { initDB, checkDataAvailability, deleteDataSimple, deleteJSONDataSimple, getData,
|
||||||
|
getDataSimple, getDataWithLeftJoinFunction, getJSONData, getJSONDataBatch, getJSONDataSimple,
|
||||||
|
getJSONDataSync, writeDataSimple, writeJSONData, writeJSONDataSimple
|
||||||
|
};
|
||||||
190
backend/src/storage/mysqldb.ts
Normal file
190
backend/src/storage/mysqldb.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* libreevent - mysqldb.js
|
||||||
|
*
|
||||||
|
* Created by Janis Hutz 07/12/2023, Licensed under the GPL V3 License
|
||||||
|
* https://janishutz.com, development@janishutz.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import mysql from 'mysql';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
declare let __dirname: string | undefined
|
||||||
|
if ( typeof( __dirname ) === 'undefined' ) {
|
||||||
|
__dirname = path.resolve( path.dirname( '' ) );
|
||||||
|
} else {
|
||||||
|
__dirname = __dirname + '/../';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the connection does not work for you, you will need to add your ip
|
||||||
|
// to the whitelist of the database
|
||||||
|
|
||||||
|
class SQLConfig {
|
||||||
|
command: string;
|
||||||
|
property?: string;
|
||||||
|
searchQuery?: string;
|
||||||
|
selection?: string;
|
||||||
|
query?: string;
|
||||||
|
newValues?: object;
|
||||||
|
secondTable?: string;
|
||||||
|
matchingParam?: string;
|
||||||
|
data?: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SQLDB {
|
||||||
|
sqlConnection: mysql.Connection;
|
||||||
|
isRecovering: boolean;
|
||||||
|
config: object;
|
||||||
|
constructor () {
|
||||||
|
this.config = JSON.parse( '' + fs.readFileSync( path.join( __dirname + '/config/db.config.secret.json' ) ) );
|
||||||
|
this.sqlConnection = mysql.createConnection( this.config );
|
||||||
|
this.isRecovering = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect () {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
const self = this;
|
||||||
|
if ( this.isRecovering ) {
|
||||||
|
console.log( '[ SQL ] Attempting to recover from critical error' );
|
||||||
|
this.sqlConnection = mysql.createConnection( this.config );
|
||||||
|
this.isRecovering = false;
|
||||||
|
}
|
||||||
|
this.sqlConnection.connect( ( err ) => {
|
||||||
|
if ( err ) {
|
||||||
|
console.error( '[ SQL ]: An error ocurred whilst connecting: ' + err.stack );
|
||||||
|
reject( err );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log( '[ SQL ] Connected to database successfully' );
|
||||||
|
self.sqlConnection.on( 'error', ( err ) => {
|
||||||
|
if ( err.code === 'ECONNRESET' ) {
|
||||||
|
console.error( '[ SQL ] Reconnecting to database, because connection was reset!' );
|
||||||
|
self.isRecovering = true;
|
||||||
|
setTimeout( () => {
|
||||||
|
self.disconnect();
|
||||||
|
self.connect();
|
||||||
|
}, 1000 );
|
||||||
|
} else {
|
||||||
|
console.error( err );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
resolve( 'connection' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect ( ) {
|
||||||
|
this.sqlConnection.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupDB () {
|
||||||
|
this.sqlConnection.query( 'SELECT @@default_storage_engine;', ( error, results ) => {
|
||||||
|
if ( error ) throw error;
|
||||||
|
if ( results[ 0 ][ '@@default_storage_engine' ] !== 'InnoDB' ) throw 'DB HAS TO USE InnoDB!';
|
||||||
|
} );
|
||||||
|
this.sqlConnection.query( 'CREATE TABLE jh_store_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, data VARCHAR( 55000 ), uid TINYTEXT, lang TINYTEXT, username TINYTEXT, stripe_user_id TINYTEXT, settings VARCHAR( 5000 ), PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', ( error ) => {
|
||||||
|
if ( error ) if ( error.code !== 'ER_TABLE_EXISTS_ERROR' ) throw error;
|
||||||
|
return 'DONE';
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
query ( operation: SQLConfig, table: string ): Promise<Array<Object>> {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
/*
|
||||||
|
Possible operation.command values (all need the table argument of the method call):
|
||||||
|
- getAllData: no additional instructions needed
|
||||||
|
|
||||||
|
- getFilteredData:
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
|
||||||
|
- InnerJoin (Select values that match in both tables):
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
- operation.selection (The columns of both tables to be selected, e.g. users.name, orders.id)
|
||||||
|
- operation.secondTable (The second table to perform Join operation with)
|
||||||
|
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
|
||||||
|
|
||||||
|
- LeftJoin (Select values in first table and return all corresponding values of second table):
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
- operation.selection (The columns of both tables to be selected, e.g. users.name, orders.id)
|
||||||
|
- operation.secondTable (The second table to perform Join operation with)
|
||||||
|
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
|
||||||
|
|
||||||
|
- RightJoin (Select values in second table and return all corresponding values of first table):
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
- operation.selection (The columns of both tables to be selected, e.g. users.name, orders.id)
|
||||||
|
- operation.secondTable (The second table to perform Join operation with)
|
||||||
|
- operation.matchingParam (Which properties should be matched to get the data, e.g. order.user_id=users.id)
|
||||||
|
|
||||||
|
- addData:
|
||||||
|
- operation.data (key-value pair with all data as values and column to insert into as key)
|
||||||
|
|
||||||
|
- deleteData:
|
||||||
|
- operation.property (the column to search for the value)
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
|
||||||
|
- updateData:
|
||||||
|
- operation.newValues (a object with keys being the column and value being the value to be inserted into that column, values are being
|
||||||
|
sanitised by the function)
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
|
||||||
|
- checkDataAvailability:
|
||||||
|
- operation.property (the column to search for the value),
|
||||||
|
- operation.searchQuery (the value to search for [will be sanitised by method])
|
||||||
|
|
||||||
|
- fullCustomCommand:
|
||||||
|
- operation.query (the SQL instruction to be executed) --> NOTE: This command will not be sanitised, so use only with proper sanitisation!
|
||||||
|
*/
|
||||||
|
let command = '';
|
||||||
|
if ( operation.command === 'getAllData' ) {
|
||||||
|
command = 'SELECT * FROM ' + table;
|
||||||
|
} else if ( operation.command === 'getFilteredData' || operation.command === 'checkDataAvailability' ) {
|
||||||
|
command = 'SELECT * FROM ' + table + ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
|
||||||
|
} else if ( operation.command === 'fullCustomCommand' ) {
|
||||||
|
command = operation.query;
|
||||||
|
} else if ( operation.command === 'addData' ) {
|
||||||
|
let keys = '';
|
||||||
|
let values = '';
|
||||||
|
for ( let key in operation.data ) {
|
||||||
|
keys += String( key ) + ', ';
|
||||||
|
values += this.sqlConnection.escape( String( operation.data[ key ] ) ) + ', ' ;
|
||||||
|
}
|
||||||
|
command = 'INSERT INTO ' + table + ' (' + keys.slice( 0, keys.length - 2 ) + ') VALUES (' + values.slice( 0, values.length - 2 ) + ');';
|
||||||
|
} else if ( operation.command === 'updateData' ) {
|
||||||
|
if ( !operation.property || !operation.searchQuery ) reject( 'Refusing to run destructive command: Missing Constraints' );
|
||||||
|
else {
|
||||||
|
command = 'UPDATE ' + table + ' SET ';
|
||||||
|
let updatedValues = '';
|
||||||
|
for ( let value in operation.newValues ) {
|
||||||
|
updatedValues += value + ' = ' + this.sqlConnection.escape( String( operation.newValues[ value ] ) ) + ', ';
|
||||||
|
}
|
||||||
|
command += updatedValues.slice( 0, updatedValues.length - 2 );
|
||||||
|
command += ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
|
||||||
|
}
|
||||||
|
} else if ( operation.command === 'deleteData' ) {
|
||||||
|
if ( !operation.property || !operation.searchQuery ) reject( 'Refusing to run destructive command: Missing Constraints' );
|
||||||
|
else {
|
||||||
|
command = 'DELETE FROM ' + table + ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
|
||||||
|
}
|
||||||
|
} else if ( operation.command === 'InnerJoin' ) {
|
||||||
|
command = 'SELECT ' + operation.selection + ' FROM ' + table + ' INNER JOIN ' + operation.secondTable + ' ON ' + operation.matchingParam + ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
|
||||||
|
} else if ( operation.command === 'LeftJoin' ) {
|
||||||
|
command = 'SELECT ' + operation.selection + ' FROM ' + table + ' LEFT JOIN ' + operation.secondTable + ' ON ' + operation.matchingParam + ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
|
||||||
|
} else if ( operation.command === 'RightJoin' ) {
|
||||||
|
command = 'SELECT ' + operation.selection + ' FROM ' + table + ' RIGHT JOIN ' + operation.secondTable + ' ON ' + operation.matchingParam + ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );
|
||||||
|
}
|
||||||
|
this.sqlConnection.query( command, ( error, results ) => {
|
||||||
|
if ( error ) reject( error );
|
||||||
|
resolve( results );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SQLConfig, SQLDB };
|
||||||
12
backend/src/storage/prepareDB.ts
Normal file
12
backend/src/storage/prepareDB.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import db from './db.js';
|
||||||
|
// import hash from '../security/hash.js';
|
||||||
|
|
||||||
|
db.initDB();
|
||||||
|
// setTimeout( () => {
|
||||||
|
// console.log( 'Setting up admin account' );
|
||||||
|
// hash.hashPassword( 'test' ).then( hash => {
|
||||||
|
// db.writeDataSimple( 'admin', 'email', 'info@janishutz.com', { email: 'info@janishutz.com', pass: hash, two_fa: 'enhanced' } );
|
||||||
|
// console.log( 'Complete!' );
|
||||||
|
// } );
|
||||||
|
// }, 5000 );
|
||||||
|
|
||||||
Reference in New Issue
Block a user