From e4dbf4ef7b7762156fcc0a3b5ba88bf249c9ba15 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao <629990+naresh97@users.noreply.github.com> Date: Mon, 2 Aug 2021 17:43:02 +0200 Subject: [PATCH 01/18] implemented --- src/app.js | 2 ++ src/db/db.js | 3 ++ src/routes/CovidRoute.js | 61 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/routes/CovidRoute.js diff --git a/src/app.js b/src/app.js index aacc8fc..ff5be0f 100644 --- a/src/app.js +++ b/src/app.js @@ -8,6 +8,7 @@ const { CodeRoute } = require("./routes/CodeRoute"); const { VerifyRoute } = require("./routes/VerifyRoute"); const { corsOpts, sessionOpts } = require("./session"); const { TelegramWebhookRoute } = require("./routes/TelegramWebhookRoute"); +const { CovidRoute } = require("./routes/CovidRoute"); console.log(`Node Environment: ${process.env.NODE_ENV}`); @@ -21,6 +22,7 @@ app.post(`/${process.env.TELEGRAM_SECRET}`, TelegramWebhookRoute); app.post("/login", LoginRoute); app.get("/code", CodeRoute); app.post("/verify", VerifyRoute); +app.post("/covid", CovidRoute); const port = process.env.PORT || 8080; app.listen(port, () => { diff --git a/src/db/db.js b/src/db/db.js index 88f3469..4a6048e 100644 --- a/src/db/db.js +++ b/src/db/db.js @@ -63,6 +63,9 @@ const User = sequelize.define("User", { isInfected: { type: DataTypes.BOOLEAN, }, + infectionDate: { + type: DataTypes.DATE, + } }); Contact.sync(); diff --git a/src/routes/CovidRoute.js b/src/routes/CovidRoute.js new file mode 100644 index 0000000..b36cf5d --- /dev/null +++ b/src/routes/CovidRoute.js @@ -0,0 +1,61 @@ +const { User } = require("../db/db"); + +function CovidRoute(req, res){ + if(!req.session.user){ + res.status(401).send("Not logged in"); + return; + } + + console.log(`SetPositive: ${req.body.setPositive}`); + + if(req.body.setPositive){ + setUserCovidPositive(req.session.user, true, response=>{ + res.send({covidPositive: response}); + }); + }else{ + getUserCovidPositivity(req.session.user, (success, positivity)=>{ + res.status(success ? 200 : 400).send({covidPositive: positivity}); + }); + } +} + +function getUserCovidPositivity(telegramID, callback){ + User.findOne({ + where: {telegram: telegramID}, + }) + .then(user=>{ + if(user){ + const infectionDuration = user.infectionDate - Date.now(); + if(infectionDuration > 60 * 60 * 24 * 14){ + setUserCovidPositive(telegramID, false, res=>{ + callback(res, res ? false : null); + }); + }else{ + callback(true, user.isInfected); + } + }else{ + callback(false, null); + } + }) + .catch(()=>{ + callback(false, null); + }) +} + +function setUserCovidPositive(telegramID, infectionState, callback){ + User.findOne({ + where: {telegram: telegramID}, + }) + .then(user=>{ + if(user){ + user.isInfected = infectionState; + user.infectionDate = Date.now(); + user.save().then(()=>callback(true)).catch(()=>callback(false)); + }else{ + callback(false); + } + }) + .catch(()=>callback(false)); +} + +exports.CovidRoute = CovidRoute; \ No newline at end of file From a4a2aa04aa41b9d2cb78245cb3faa9796f02724a Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao <629990+naresh97@users.noreply.github.com> Date: Mon, 2 Aug 2021 17:56:49 +0200 Subject: [PATCH 02/18] added tsconfig and rename all files --- src/{app.js => app.ts} | 0 src/db/{db.js => db.ts} | 0 src/db/{utils.js => utils.ts} | 0 src/routes/{CodeRoute.js => CodeRoute.ts} | 0 src/routes/{LoginRoute.js => LoginRoute.ts} | 0 src/routes/{TelegramWebhookRoute.js => TelegramWebhookRoute.ts} | 0 src/routes/{VerifyRoute.js => VerifyRoute.ts} | 0 src/{session.js => session.ts} | 0 src/{telegram.js => telegram.ts} | 0 src/{utils.js => utils.ts} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename src/{app.js => app.ts} (100%) rename src/db/{db.js => db.ts} (100%) rename src/db/{utils.js => utils.ts} (100%) rename src/routes/{CodeRoute.js => CodeRoute.ts} (100%) rename src/routes/{LoginRoute.js => LoginRoute.ts} (100%) rename src/routes/{TelegramWebhookRoute.js => TelegramWebhookRoute.ts} (100%) rename src/routes/{VerifyRoute.js => VerifyRoute.ts} (100%) rename src/{session.js => session.ts} (100%) rename src/{telegram.js => telegram.ts} (100%) rename src/{utils.js => utils.ts} (100%) diff --git a/src/app.js b/src/app.ts similarity index 100% rename from src/app.js rename to src/app.ts diff --git a/src/db/db.js b/src/db/db.ts similarity index 100% rename from src/db/db.js rename to src/db/db.ts diff --git a/src/db/utils.js b/src/db/utils.ts similarity index 100% rename from src/db/utils.js rename to src/db/utils.ts diff --git a/src/routes/CodeRoute.js b/src/routes/CodeRoute.ts similarity index 100% rename from src/routes/CodeRoute.js rename to src/routes/CodeRoute.ts diff --git a/src/routes/LoginRoute.js b/src/routes/LoginRoute.ts similarity index 100% rename from src/routes/LoginRoute.js rename to src/routes/LoginRoute.ts diff --git a/src/routes/TelegramWebhookRoute.js b/src/routes/TelegramWebhookRoute.ts similarity index 100% rename from src/routes/TelegramWebhookRoute.js rename to src/routes/TelegramWebhookRoute.ts diff --git a/src/routes/VerifyRoute.js b/src/routes/VerifyRoute.ts similarity index 100% rename from src/routes/VerifyRoute.js rename to src/routes/VerifyRoute.ts diff --git a/src/session.js b/src/session.ts similarity index 100% rename from src/session.js rename to src/session.ts diff --git a/src/telegram.js b/src/telegram.ts similarity index 100% rename from src/telegram.js rename to src/telegram.ts diff --git a/src/utils.js b/src/utils.ts similarity index 100% rename from src/utils.js rename to src/utils.ts From bed5c91567c7325d3c56e68a5089fb3b14655b49 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao <629990+naresh97@users.noreply.github.com> Date: Mon, 2 Aug 2021 17:57:19 +0200 Subject: [PATCH 03/18] added tsconfig and rename all files --- package-lock.json | 13 +++++++++++++ package.json | 1 + tsconfig.json | 4 ++++ 3 files changed, 18 insertions(+) create mode 100644 tsconfig.json diff --git a/package-lock.json b/package-lock.json index 4ffc3c8..0c0e72d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "sqlite3": "^5.0.2" }, "devDependencies": { + "@tsconfig/node14": "^1.0.1", "eslint": "^7.31.0", "prettier": "2.3.2" } @@ -218,6 +219,12 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, "node_modules/@types/node": { "version": "16.4.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.1.tgz", @@ -4600,6 +4607,12 @@ "tar": "^6.1.0" } }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, "@types/node": { "version": "16.4.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.1.tgz", diff --git a/package.json b/package.json index bf2939b..af1368e 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "sqlite3": "^5.0.2" }, "devDependencies": { + "@tsconfig/node14": "^1.0.1", "eslint": "^7.31.0", "prettier": "2.3.2" } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..45a951a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@tsconfig/node14/tsconfig.json", + "include": ["./src/**/*"], +} \ No newline at end of file From e881a7b782626b3ce2ae295fd5bc0b295564ad0a Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Tue, 3 Aug 2021 01:53:23 +0200 Subject: [PATCH 04/18] first build --- .vscode/settings.json | 1 + built/app.js | 25 ++++ built/db/db.js | 75 ++++++++++ built/db/utils.js | 47 ++++++ built/routes/CodeRoute.js | 48 ++++++ built/routes/LoginRoute.js | 73 +++++++++ built/routes/TelegramWebhookRoute.js | 84 +++++++++++ built/routes/VerifyRoute.js | 44 ++++++ built/session.js | 16 ++ built/telegram.js | 40 +++++ built/utils.js | 7 + package-lock.json | 211 +++++++++++++++++++++++++++ package.json | 5 + src/app.ts | 17 +-- src/db/db.ts | 75 ++++++---- src/db/utils.ts | 44 +++--- src/routes/CodeRoute.ts | 54 ++++--- src/routes/LoginRoute.ts | 51 +++++-- src/routes/TelegramWebhookRoute.ts | 159 +++++++++++--------- src/routes/VerifyRoute.ts | 31 ++-- src/session.ts | 12 +- src/telegram.ts | 26 ++-- src/utils.ts | 6 +- tsconfig.json | 5 +- 24 files changed, 967 insertions(+), 189 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 built/app.js create mode 100644 built/db/db.js create mode 100644 built/db/utils.js create mode 100644 built/routes/CodeRoute.js create mode 100644 built/routes/LoginRoute.js create mode 100644 built/routes/TelegramWebhookRoute.js create mode 100644 built/routes/VerifyRoute.js create mode 100644 built/session.js create mode 100644 built/telegram.js create mode 100644 built/utils.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} diff --git a/built/app.js b/built/app.js new file mode 100644 index 0000000..715dc67 --- /dev/null +++ b/built/app.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const express = require("express"); +const session = require("express-session"); +const cors = require("cors"); +const session_1 = require("./session"); +const TelegramWebhookRoute_1 = require("./routes/TelegramWebhookRoute"); +const LoginRoute_1 = require("./routes/LoginRoute"); +const CodeRoute_1 = require("./routes/CodeRoute"); +const VerifyRoute_1 = require("./routes/VerifyRoute"); +require("dotenv-flow").config(); +console.log(`Node Environment: ${process.env.NODE_ENV}`); +const app = express(); +app.set("trust proxy", 1); +app.use(session(session_1.sessionOpts)); +app.use(cors(session_1.corsOpts)); +app.use(express.json()); +app.post(`/${process.env.TELEGRAM_SECRET}`, TelegramWebhookRoute_1.TelegramWebhookRoute); +app.post("/login", LoginRoute_1.LoginRoute); +app.get("/code", CodeRoute_1.CodeRoute); +app.post("/verify", VerifyRoute_1.VerifyRoute); +const port = process.env.PORT || 8080; +app.listen(port, () => { + console.log(`Listening on port ${port}`); +}); diff --git a/built/db/db.js b/built/db/db.js new file mode 100644 index 0000000..c58ea90 --- /dev/null +++ b/built/db/db.js @@ -0,0 +1,75 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.User = exports.Contact = exports.store = exports.storeDB = exports.sequelize = void 0; +const connect_session_sequelize_1 = __importDefault(require("connect-session-sequelize")); +const express_session_1 = __importDefault(require("express-session")); +const sequelize_1 = require("sequelize"); +const SequelizeStore = connect_session_sequelize_1.default(express_session_1.default.Store); +const isProduction = process.env.NODE_ENV == "production"; +exports.sequelize = (() => { + if (isProduction) { + return new sequelize_1.Sequelize(process.env.DB_DATA_NAME || "DATABASE", process.env.DB_USER || "USERNAME", process.env.DB_PASS || "PASSWORD", { + host: process.env.DB_PATH || "localhost", + dialect: "postgres", + }); + } + else { + return new sequelize_1.Sequelize("sqlite::memory:"); + } +})(); +exports.storeDB = (() => { + if (isProduction) { + return new sequelize_1.Sequelize(process.env.DB_STORE_NAME || "DATABASE", process.env.DB_USER || "USERNAME", process.env.DB_PASS || "PASSWORD", { + host: process.env.DB_PATH, + dialect: "postgres", + }); + } + else { + return new sequelize_1.Sequelize("sqlite::memory:"); + } +})(); +exports.store = new SequelizeStore({ + db: exports.storeDB, +}); +exports.Contact = exports.sequelize.define("Contact", { + user: { + type: sequelize_1.DataTypes.INTEGER, + allowNull: false, + }, + with: { + type: sequelize_1.DataTypes.INTEGER, + allowNull: false, + }, +}); +exports.User = exports.sequelize.define("User", { + id: { + type: sequelize_1.DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + telegram: { + type: sequelize_1.DataTypes.INTEGER, + allowNull: false, + unique: true, + }, + verification: { + type: sequelize_1.DataTypes.STRING, + }, + isInfected: { + type: sequelize_1.DataTypes.BOOLEAN, + }, +}); +exports.Contact.sync(); +exports.User.sync().then(() => { + if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { + exports.User.create({ + telegram: 12345, + }).catch(() => { + console.log("Couldn't create admin account. Probably exists."); + }); + } +}); +exports.store.sync(); diff --git a/built/db/utils.js b/built/db/utils.js new file mode 100644 index 0000000..28ee01d --- /dev/null +++ b/built/db/utils.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createUser = exports.addContact = void 0; +const telegram_1 = require("../telegram"); +const db_1 = require("./db"); +function addContact(userATelegram, userBRowID, done) { + db_1.User.findOne({ where: { telegram: userATelegram } }).then((userA) => { + db_1.User.findOne({ where: { id: userBRowID } }).then((userB) => { + if (!!userA || !!userB) { + done(false, "Could not find user."); + return; + } + db_1.Contact.create({ user: userA.id, with: userBRowID }) + .then(() => { + console.log(`Registering contact between ${userA.id} and ${userBRowID}`); + telegram_1.sendTelegramMessage(userB.telegram, "Someone scanned your QR code. You will be notified if they are tested positive with Covid. If you are tested positive, please tell this bot /COVIDPOSITIVE"); + done(true, "Successfully added contact"); + }) + .catch((e) => { + done(false, e); + }); + }); + }); +} +exports.addContact = addContact; +function createUser(telegram, callback) { + db_1.User.create({ + telegram: telegram, + }) + .then((user) => { + if (!user) { + callback(false, "Could not create user"); + } + else { + callback(true, "Success"); + } + }) + .catch((reason) => { + if (reason.name == "SequelizeUniqueConstraintError") { + callback(false, "User already exists"); + } + else { + callback(false, "Unknown error"); + } + }); +} +exports.createUser = createUser; diff --git a/built/routes/CodeRoute.js b/built/routes/CodeRoute.js new file mode 100644 index 0000000..133c1e0 --- /dev/null +++ b/built/routes/CodeRoute.js @@ -0,0 +1,48 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CodeRoute = void 0; +const db_1 = require("../db/db"); +const bcrypt_1 = __importDefault(require("bcrypt")); +const qrcode_1 = __importDefault(require("qrcode")); +function CodeRoute(req, res) { + if (!req.session.user) { + res.status(401).send("Not logged in"); + return; + } + createQRCode(req.session.user, (err, url) => { + res.status(url ? 200 : 401).send({ error: err, data: url }); + }); +} +exports.CodeRoute = CodeRoute; +function createQRCode(telegram, callback) { + db_1.User.findOne({ + where: { + telegram: telegram, + }, + }) + .then((user) => { + user && + refreshVerification(user, (result) => { + const verifyURL = `${process.env.WEBSITE_URL}/#/verify/${encodeURIComponent(result.verification)}`; + qrcode_1.default.toDataURL(verifyURL, { width: 300, height: 300 }, (error, url) => { + callback(error.message, url); + }); + }); + }) + .catch((error) => { + callback(error); + }); +} +function refreshVerification(user, callback) { + let newVerification = bcrypt_1.default + .hashSync(`${new Date().getTime()}-${user.telegram}`, 5) + .replace(/[^a-zA-Z0-9]+/g, ""); + newVerification = newVerification.substr(0, newVerification.length / 2); + user.verification = newVerification; + user.save().then((result) => { + callback(result); + }); +} diff --git a/built/routes/LoginRoute.js b/built/routes/LoginRoute.js new file mode 100644 index 0000000..35e8694 --- /dev/null +++ b/built/routes/LoginRoute.js @@ -0,0 +1,73 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoginRoute = void 0; +const db_1 = require("../db/db"); +const crypto_1 = __importDefault(require("crypto")); +const utils_1 = require("../db/utils"); +function LoginRoute(req, res) { + const telegramResponse = req.body.telegramResponse; + authUser(telegramResponse, (authObject) => { + if (authObject) { + const verified = req.session.verified; + const verifiedBy = req.session.verifiedBy; + req.session.regenerate(() => { + req.session.user = telegramResponse.id; + if (verified) { + utils_1.addContact(telegramResponse.id, verifiedBy, (contactSuccess) => { + res.send({ + authorized: authObject.authorized, + contactSuccess: contactSuccess, + }); + }); + } + else { + res.send(authObject); + } + }); + } + else { + res.status(401).send(authObject); + } + }); +} +exports.LoginRoute = LoginRoute; +function authUser(telegramResponse, callback) { + let dataCheckArray = []; + for (const [key, value] of Object.entries(telegramResponse)) { + if (key == "hash") + continue; + dataCheckArray.push(`${key}=${value}`); + } + dataCheckArray.sort(); + const dataCheckString = dataCheckArray.join("\n"); + const secretKey = crypto_1.default + .createHash("sha256") + .update(process.env.TELEGRAM_TOKEN) + .digest(); + const confirmationHash = crypto_1.default + .createHmac("sha256", secretKey) + .update(dataCheckString) + .digest("hex"); + const authorized = confirmationHash == telegramResponse.hash; + if (!authorized) { + callback({ authorized: false }); + } + db_1.User.findOne({ + where: { + telegram: telegramResponse.id, + }, + }).then((user) => { + if (!user) { + utils_1.createUser(telegramResponse.id, (success) => { + callback({ authorized: success }); + }); + } + else { + callback({ authorized: true }); + } + }); +} +exports.LoginRoute = LoginRoute; diff --git a/built/routes/TelegramWebhookRoute.js b/built/routes/TelegramWebhookRoute.js new file mode 100644 index 0000000..8054f69 --- /dev/null +++ b/built/routes/TelegramWebhookRoute.js @@ -0,0 +1,84 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TelegramWebhookRoute = void 0; +const types_1 = require("sequelize/types"); +const db_1 = require("../db/db"); +const telegram_1 = require("../telegram"); +function TelegramWebhookRoute(req, res) { + try { + const messageText = req.body.message.text; + const telegramID = req.body.message.from.id; + if (messageText.toLowerCase() == "/covidpositive") { + userInfected(telegramID, (result) => { + if (result.saved) { + telegram_1.sendTelegramMessage(telegramID, "Thanks for informing us. We will notify the people you were in contact with!"); + informContacts(telegramID); + } + else { + telegram_1.sendTelegramMessage(telegramID, "Sorry, something went wrong."); + } + }); + } + } + catch (e) { + console.log("Could not get Telegram Message"); + } + res.send(); +} +exports.TelegramWebhookRoute = TelegramWebhookRoute; +function informContacts(telegramID) { + db_1.User.findOne({ + where: { + telegram: telegramID, + }, + }).then((user) => { + if (user) { + const userRowID = user.id; + db_1.Contact.findAll({ + where: { + [types_1.Op.or]: [{ user: userRowID }, { with: userRowID }], + }, + }).then((result) => { + result.forEach((contact) => { + const otherPersonID = contact.user == userRowID ? contact.with : contact.user; + db_1.User.findOne({ + where: { + id: otherPersonID, + }, + }).then((otherPerson) => { + otherPerson && + telegram_1.sendTelegramMessage(otherPerson.telegram, "You're infected."); + }); + }); + }); + } + }); +} +function userInfected(telegramID, callback) { + db_1.User.findOne({ + where: { + telegram: telegramID, + }, + }) + .then((user) => { + if (!user) { + callback({ saved: false }); + } + else { + user.isInfected = true; + user + .save() + .then((result) => { + if (result) { + callback({ saved: true }); + } + }) + .catch((err) => { + callback({ saved: false }); + }); + } + }) + .catch((err) => { + callback({ saved: false }); + }); +} diff --git a/built/routes/VerifyRoute.js b/built/routes/VerifyRoute.js new file mode 100644 index 0000000..b60b088 --- /dev/null +++ b/built/routes/VerifyRoute.js @@ -0,0 +1,44 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VerifyRoute = void 0; +const db_1 = require("../db/db"); +const utils_1 = require("../db/utils"); +function VerifyRoute(req, res) { + checkVerification(req.body.id, (success, msg, withUserID) => { + if (success) { + req.session.verified = success; + req.session.verifiedBy = withUserID; + if (req.session.user) { + // If Logged In + utils_1.addContact(req.session.user, withUserID, (success, msg) => { + res + .status(success ? 200 : 400) + .send({ success: success, message: msg, loggedIn: true }); + }); + } + else { + // If Not Logged In + res.send({ success: success, message: msg, loggedIn: false }); + } + } + else { + res.status(400).send({ success: success, message: msg }); + } + }); +} +exports.VerifyRoute = VerifyRoute; +function checkVerification(verification, callback) { + db_1.User.findOne({ + where: { + verification: decodeURIComponent(verification), + }, + }).then((user) => { + if (user) { + callback(true, "User verified", user.id); + } + else { + callback(false, "No such verification"); + } + }); +} +exports.VerifyRoute = VerifyRoute; diff --git a/built/session.js b/built/session.js new file mode 100644 index 0000000..7b19ef2 --- /dev/null +++ b/built/session.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.corsOpts = exports.sessionOpts = void 0; +const db_1 = require("./db/db"); +exports.sessionOpts = { + secret: process.env.SERVER_SESSION_SECRET, + resave: false, + saveUninitialized: false, + cookie: { + secure: true, + sameSite: "none", + maxAge: Number(process.env.SESSION_LENGTH), + }, + store: db_1.store, +}; +exports.corsOpts = { credentials: true, origin: true, secure: true }; diff --git a/built/telegram.js b/built/telegram.js new file mode 100644 index 0000000..9b1014c --- /dev/null +++ b/built/telegram.js @@ -0,0 +1,40 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.sendTelegramMessage = exports.setTelegramWebHook = void 0; +const axios_1 = __importDefault(require("axios")); +function setTelegramWebHook(callback = () => { }) { + const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/setWebhook`; + axios_1.default + .post(url, { + url: `${process.env.SERVER_API_URL}/${process.env.TELEGRAM_SECRET}`, + allowed_updates: [], + drop_pending_updates: true, + }) + .then((res) => { + callback(!!res); + }) + .catch((err) => { + callback(!!err); + }); +} +exports.setTelegramWebHook = setTelegramWebHook; +function sendTelegramMessage(telegramID, message, callback = () => { }) { + const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/sendMessage`; + axios_1.default + .post(url, { + chat_id: telegramID, + text: message, + }) + .then((res) => { + callback(!!res); + }) + .catch((err) => { + console.error("Problem sending Telegram message."); + callback(!!err); + }); +} +exports.sendTelegramMessage = sendTelegramMessage; +setTelegramWebHook(); diff --git a/built/utils.js b/built/utils.js new file mode 100644 index 0000000..ce4af57 --- /dev/null +++ b/built/utils.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getCookieExpiry = void 0; +function getCookieExpiry() { + return new Date(Date.now() + Number(process.env.COOKIE_EXPIRY_DURATION)); +} +exports.getCookieExpiry = getCookieExpiry; diff --git a/package-lock.json b/package-lock.json index 0c0e72d..6f14058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,11 @@ }, "devDependencies": { "@tsconfig/node14": "^1.0.1", + "@types/bcrypt": "^5.0.0", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.13", + "@types/express-session": "^1.17.4", + "@types/qrcode": "^1.4.1", "eslint": "^7.31.0", "prettier": "2.3.2" } @@ -225,11 +230,114 @@ "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", "dev": true }, + "node_modules/@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", + "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/express-session": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz", + "integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "node_modules/@types/node": { "version": "16.4.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.1.tgz", "integrity": "sha512-UW7cbLqf/Wu5XH2RKKY1cHwUNLicIDRLMraYKz+HHAerJ0ZffUEk+fMnd8qU2JaS6cAy0r8tsaf7yqHASf/Y0Q==" }, + "node_modules/@types/qrcode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.4.1.tgz", + "integrity": "sha512-vxMyr7JM7tYPxu8vUE83NiosWX5DZieCyYeJRoOIg0pAkyofCBzknJ2ycUZkPGDFis2RS8GN/BeJLnRnAPxeCA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4613,11 +4721,114 @@ "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", "dev": true }, + "@types/bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", + "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/express-session": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz", + "integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "@types/node": { "version": "16.4.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.1.tgz", "integrity": "sha512-UW7cbLqf/Wu5XH2RKKY1cHwUNLicIDRLMraYKz+HHAerJ0ZffUEk+fMnd8qU2JaS6cAy0r8tsaf7yqHASf/Y0Q==" }, + "@types/qrcode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.4.1.tgz", + "integrity": "sha512-vxMyr7JM7tYPxu8vUE83NiosWX5DZieCyYeJRoOIg0pAkyofCBzknJ2ycUZkPGDFis2RS8GN/BeJLnRnAPxeCA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", diff --git a/package.json b/package.json index af1368e..75e5089 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,11 @@ }, "devDependencies": { "@tsconfig/node14": "^1.0.1", + "@types/bcrypt": "^5.0.0", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.13", + "@types/express-session": "^1.17.4", + "@types/qrcode": "^1.4.1", "eslint": "^7.31.0", "prettier": "2.3.2" } diff --git a/src/app.ts b/src/app.ts index aacc8fc..54370c6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,14 +1,13 @@ -const express = require("express"); -const session = require("express-session"); -const cors = require("cors"); +import express = require("express"); +import session = require("express-session"); +import cors = require("cors"); +import { corsOpts, sessionOpts } from "./session"; +import { TelegramWebhookRoute } from "./routes/TelegramWebhookRoute"; +import { LoginRoute } from "./routes/LoginRoute"; +import { CodeRoute } from "./routes/CodeRoute"; +import { VerifyRoute } from "./routes/VerifyRoute"; require("dotenv-flow").config(); -const { LoginRoute } = require("./routes/LoginRoute"); -const { CodeRoute } = require("./routes/CodeRoute"); -const { VerifyRoute } = require("./routes/VerifyRoute"); -const { corsOpts, sessionOpts } = require("./session"); -const { TelegramWebhookRoute } = require("./routes/TelegramWebhookRoute"); - console.log(`Node Environment: ${process.env.NODE_ENV}`); const app = express(); diff --git a/src/db/db.ts b/src/db/db.ts index 88f3469..ed18cec 100644 --- a/src/db/db.ts +++ b/src/db/db.ts @@ -1,18 +1,20 @@ -const session = require("express-session"); -const { Sequelize, DataTypes } = require("sequelize"); -var SequelizeStore = require("connect-session-sequelize")(session.Store); +import ConnectSessionSequelize from "connect-session-sequelize"; +import session from "express-session"; +import { DataTypes, Model, Optional, Sequelize } from "sequelize"; -const isProduction = process.env.NODE_ENV == "production"; +const SequelizeStore = ConnectSessionSequelize(session.Store); -const sequelize = (() => { +const isProduction: boolean = process.env.NODE_ENV == "production"; + +export const sequelize: Sequelize = (() => { if (isProduction) { return new Sequelize( - process.env.DB_DATA_NAME, - process.env.DB_USER, - process.env.DB_PASS, + process.env.DB_DATA_NAME || "DATABASE", + process.env.DB_USER || "USERNAME", + process.env.DB_PASS || "PASSWORD", { - host: process.env.DB_PATH, - dialect: process.env.DB_DATA_DIALECT, + host: process.env.DB_PATH || "localhost", + dialect: "postgres", } ); } else { @@ -20,15 +22,15 @@ const sequelize = (() => { } })(); -const storeDB = (() => { +export const storeDB: Sequelize = (() => { if (isProduction) { return new Sequelize( - process.env.DB_STORE_NAME, - process.env.DB_USER, - process.env.DB_PASS, + process.env.DB_STORE_NAME || "DATABASE", + process.env.DB_USER || "USERNAME", + process.env.DB_PASS || "PASSWORD", { host: process.env.DB_PATH, - dialect: process.env.DB_DATA_DIALECT, + dialect: "postgres", } ); } else { @@ -36,11 +38,22 @@ const storeDB = (() => { } })(); -const store = new SequelizeStore({ +export const store = new SequelizeStore({ db: storeDB, }); -const Contact = sequelize.define("Contact", { +export type UserRowID = number; +export type TelegramID = number; +export type VerificationString = string; + +interface ContactAttributes { + user: UserRowID; + with: UserRowID; +} +export interface ContactInterface + extends Model, + ContactAttributes {} +export const Contact = sequelize.define("Contact", { user: { type: DataTypes.INTEGER, allowNull: false, @@ -51,7 +64,25 @@ const Contact = sequelize.define("Contact", { }, }); -const User = sequelize.define("User", { +interface UserAttributes { + id: UserRowID; + telegram: TelegramID; + verification: VerificationString; + isInfected: boolean; +} +interface UserCreationAttributes { + telegram: TelegramID; +} +export interface UserInstance + extends Model, + UserAttributes {} + +export const User = sequelize.define("User", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, telegram: { type: DataTypes.INTEGER, allowNull: false, @@ -70,7 +101,7 @@ Contact.sync(); User.sync().then(() => { if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { User.create({ - telegram: process.env.ADMIN_USERNAME, + telegram: 12345, }).catch(() => { console.log("Couldn't create admin account. Probably exists."); }); @@ -78,9 +109,3 @@ User.sync().then(() => { }); store.sync(); - -exports.User = User; -exports.Contact = Contact; -exports.sequelize = sequelize; -exports.storeDB = storeDB; -exports.store = store; diff --git a/src/db/utils.ts b/src/db/utils.ts index 591db8c..67b21c8 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -1,18 +1,26 @@ -const { sendTelegramMessage } = require("../telegram"); -const { User, Contact } = require("./db"); +import { sendTelegramMessage } from "../telegram"; +import { User, Contact, TelegramID, UserRowID } from "./db"; -function addContact(telegram, withUserID, done) { - User.findOne({ where: { telegram: telegram } }).then((user) => { - User.findOne({ where: { id: withUserID } }).then((withUser) => { - Contact.create({ user: user.id, with: withUserID }) +export function addContact( + userATelegram: TelegramID, + userBRowID: UserRowID, + done: (success: boolean, message: string) => void +): void { + User.findOne({ where: { telegram: userATelegram } }).then((userA) => { + User.findOne({ where: { id: userBRowID } }).then((userB) => { + if (!!userA || !!userB) { + done(false, "Could not find user."); + return; + } + + Contact.create({ user: userA!.id, with: userBRowID }) .then(() => { console.log( - `Registering contact between ${user.id} and ${withUserID}` + `Registering contact between ${userA!.id} and ${userBRowID}` ); sendTelegramMessage( - withUser.telegram, - "Someone scanned your QR code. You will be notified if they are tested positive with Covid. If you are tested positive, please tell this bot /COVIDPOSITIVE", - () => {} + userB!.telegram, + "Someone scanned your QR code. You will be notified if they are tested positive with Covid. If you are tested positive, please tell this bot /COVIDPOSITIVE" ); done(true, "Successfully added contact"); }) @@ -23,25 +31,25 @@ function addContact(telegram, withUserID, done) { }); } -function createUser(telegram, done) { +export function createUser( + telegram: TelegramID, + callback: (success: boolean, message: string) => void +): void { User.create({ telegram: telegram, }) .then((user) => { if (!user) { - done(false, "Could not create user"); + callback(false, "Could not create user"); } else { - done(true, "Success"); + callback(true, "Success"); } }) .catch((reason) => { if (reason.name == "SequelizeUniqueConstraintError") { - done(false, "User already exists"); + callback(false, "User already exists"); } else { - done(false, "Unknown error"); + callback(false, "Unknown error"); } }); } - -exports.addContact = addContact; -exports.createUser = createUser; diff --git a/src/routes/CodeRoute.ts b/src/routes/CodeRoute.ts index 78372e9..9f435b4 100644 --- a/src/routes/CodeRoute.ts +++ b/src/routes/CodeRoute.ts @@ -1,8 +1,15 @@ -const bcrypt = require("bcrypt"); -const QRCode = require("qrcode"); -const { User } = require("../db/db"); +import { Request, Response } from "express"; +import { TelegramID, User, UserInstance } from "../db/db"; +import bcrypt from "bcrypt"; +import QRCode, { QRCodeToDataURLOptions } from "qrcode"; -function CodeRoute(req, res) { +declare module "express-session" { + interface Session { + user: TelegramID; + } +} + +export function CodeRoute(req: Request, res: Response) { if (!req.session.user) { res.status(401).send("Not logged in"); return; @@ -12,36 +19,45 @@ function CodeRoute(req, res) { }); } -function createQRCode(telegram, done) { +function createQRCode( + telegram: TelegramID, + callback: (errorMessage: string, url?: string) => void +): void { User.findOne({ where: { telegram: telegram, }, }) .then((user) => { - refreshVerification(user, (result) => { - const verifyURL = `${ - process.env.WEBSITE_URL - }/#/verify/${encodeURIComponent(result.verification)}`; - QRCode.toDataURL(verifyURL, { width: 300, height: 300 }, (err, url) => { - done(err, url); + user && + refreshVerification(user, (result) => { + const verifyURL = `${ + process.env.WEBSITE_URL + }/#/verify/${encodeURIComponent(result.verification)}`; + QRCode.toDataURL( + verifyURL, + { width: 300, height: 300 } as QRCodeToDataURLOptions, + (error, url) => { + callback(error.message, url); + } + ); }); - }); }) - .catch((err) => { - done(err); + .catch((error) => { + callback(error); }); } -function refreshVerification(user, done) { +function refreshVerification( + user: UserInstance, + callback: (success: UserInstance) => void +): void { let newVerification = bcrypt - .hashSync(`${new Date().getTime()}-${user.hash}`, 5) + .hashSync(`${new Date().getTime()}-${user.telegram}`, 5) .replace(/[^a-zA-Z0-9]+/g, ""); newVerification = newVerification.substr(0, newVerification.length / 2); user.verification = newVerification; user.save().then((result) => { - done(result); + callback(result); }); } - -exports.CodeRoute = CodeRoute; diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 7163367..cac172c 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -1,11 +1,30 @@ -const crypto = require("crypto"); -const { User } = require("../db/db"); -const { addContact, createUser } = require("../db/utils"); +import { Request, Response } from "express"; +import { TelegramID, User, UserRowID } from "../db/db"; +import crypto from "crypto"; +import { addContact, createUser } from "../db/utils"; -function LoginRoute(req, res) { +declare module "express-session" { + interface Session { + verified: boolean; + verifiedBy: UserRowID; + } +} + +type TelegramLoginResponse = { + id: TelegramID; + hash: string; +}; + +interface LoginRequest extends Request { + body: { + telegramResponse: TelegramLoginResponse; + }; +} + +export function LoginRoute(req: LoginRequest, res: Response) { const telegramResponse = req.body.telegramResponse; - authUser(telegramResponse, (success, msg) => { - if (success) { + authUser(telegramResponse, (authObject) => { + if (authObject) { const verified = req.session.verified; const verifiedBy = req.session.verifiedBy; req.session.regenerate(() => { @@ -13,22 +32,24 @@ function LoginRoute(req, res) { if (verified) { addContact(telegramResponse.id, verifiedBy, (contactSuccess) => { res.send({ - authorized: success, - message: msg, + authorized: authObject.authorized, contactSuccess: contactSuccess, }); }); } else { - res.send({ authorized: success, message: msg }); + res.send(authObject); } }); } else { - res.status(401).send({ authorized: success, message: msg }); + res.status(401).send(authObject); } }); } -function authUser(telegramResponse, done) { +function authUser( + telegramResponse: TelegramLoginResponse, + callback: (callbackObject: { authorized: boolean }) => void +): void { let dataCheckArray = []; for (const [key, value] of Object.entries(telegramResponse)) { @@ -40,7 +61,7 @@ function authUser(telegramResponse, done) { const secretKey = crypto .createHash("sha256") - .update(process.env.TELEGRAM_TOKEN) + .update(process.env.TELEGRAM_TOKEN!) .digest(); const confirmationHash = crypto .createHmac("sha256", secretKey) @@ -50,7 +71,7 @@ function authUser(telegramResponse, done) { const authorized = confirmationHash == telegramResponse.hash; if (!authorized) { - done({ authorized: false }); + callback({ authorized: false }); } User.findOne({ @@ -60,10 +81,10 @@ function authUser(telegramResponse, done) { }).then((user) => { if (!user) { createUser(telegramResponse.id, (success) => { - done({ authorized: success }); + callback({ authorized: success }); }); } else { - done({ authorized: true }); + callback({ authorized: true }); } }); } diff --git a/src/routes/TelegramWebhookRoute.ts b/src/routes/TelegramWebhookRoute.ts index 9c1e795..e337ab3 100644 --- a/src/routes/TelegramWebhookRoute.ts +++ b/src/routes/TelegramWebhookRoute.ts @@ -1,82 +1,103 @@ -const { Op } = require("sequelize"); -const { User, Contact } = require("../db/db"); -const { sendTelegramMessage } = require("../telegram"); +import { Request, Response } from "express"; +import { Op } from "sequelize/types"; +import { Contact, TelegramID, User } from "../db/db"; +import { sendTelegramMessage } from "../telegram"; -function TelegramWebhookRoute(req, res) { +interface TelegramWebhookRequest extends Request { + body: { + message: { + text: string; + from: { + id: TelegramID; + }; + }; + }; +} - try{ - const messageText = req.body.message.text; - const telegramID = req.body.message.from.id; - if (messageText.toLowerCase() == "/covidpositive") { - userInfected(telegramID, (result) => { - if(result.saved){ - sendTelegramMessage(telegramID, "Thanks for informing us. We will notify the people you were in contact with!", ()=>{}); - informContacts(telegramID, ()=>{}); - }else{ - sendTelegramMessage(telegramID, "Sorry, something went wrong.", ()=>{}); - } - }); +export function TelegramWebhookRoute( + req: TelegramWebhookRequest, + res: Response +) { + try { + const messageText = req.body.message.text; + const telegramID = req.body.message.from.id; + if (messageText.toLowerCase() == "/covidpositive") { + userInfected(telegramID, (result) => { + if (result.saved) { + sendTelegramMessage( + telegramID, + "Thanks for informing us. We will notify the people you were in contact with!" + ); + informContacts(telegramID); + } else { + sendTelegramMessage(telegramID, "Sorry, something went wrong."); } + }); } - catch(e){ - console.log("Could not get Telegram Message"); - } - + } catch (e) { + console.log("Could not get Telegram Message"); + } - res.send(); + res.send(); } -function informContacts(telegramID, doneCallback){ - User.findOne({ +function informContacts(telegramID: TelegramID) { + User.findOne({ + where: { + telegram: telegramID, + }, + }).then((user) => { + if (user) { + const userRowID = user.id; + Contact.findAll({ where: { - telegram: telegramID, - } - }).then(user => { - if(user){ - const userRowID = user.id; - Contact.findAll({ - where: { - [Op.or]: [{user: userRowID}, {with: userRowID}], - } - }) - .then(result => { - result.forEach(contact => { - const otherPersonID = contact.user == userRowID ? contact.with : contact.user; - User.findOne({ - where: { - id: otherPersonID, - } - }).then(otherPerson => { - sendTelegramMessage(otherPerson.telegram, "You're infected.", ()=>{}); - }); - }); - }); - } - }); - + [Op.or]: [{ user: userRowID }, { with: userRowID }], + }, + }).then((result) => { + result.forEach((contact) => { + const otherPersonID = + contact.user == userRowID ? contact.with : contact.user; + User.findOne({ + where: { + id: otherPersonID, + }, + }).then((otherPerson) => { + otherPerson && + sendTelegramMessage(otherPerson.telegram, "You're infected."); + }); + }); + }); + } + }); } -function userInfected(telegramID, doneCallback) { - User.findOne({ - where: { - telegram: telegramID, - }, - }).then((user) => { - if (!user) { - done({saved: false}); - } else { - user.isInfected = true; - user.save().then(result => { - if(result){ - - doneCallback({saved: true}); - } - }).catch(err=>{doneCallback({saved: false})}); - } +function userInfected( + telegramID: TelegramID, + callback: (callbackObject: { saved: boolean }) => void +): void { + User.findOne({ + where: { + telegram: telegramID, + }, + }) + .then((user) => { + if (!user) { + callback({ saved: false }); + } else { + user.isInfected = true; + user + .save() + .then((result) => { + if (result) { + callback({ saved: true }); + } + }) + .catch((err) => { + callback({ saved: false }); + }); + } }) - .catch(err=>{ - doneCallback({saved: false}); + .catch((err) => { + callback({ saved: false }); }); } - -exports.TelegramWebhookRoute = TelegramWebhookRoute; \ No newline at end of file diff --git a/src/routes/VerifyRoute.ts b/src/routes/VerifyRoute.ts index 4f39cfb..43295f3 100644 --- a/src/routes/VerifyRoute.ts +++ b/src/routes/VerifyRoute.ts @@ -1,15 +1,21 @@ -const { User } = require("../db/db"); -const { addContact } = require("../db/utils"); +import { Request, Response } from "express"; +import { User, UserRowID, VerificationString } from "../db/db"; +import { addContact } from "../db/utils"; -function VerifyRoute(req, res) { - checkVerification(req.body.id, (success, msg, withUserID) => { - req.session.verified = success; - req.session.verifiedBy = withUserID; +interface VerifyRequest extends Request { + body: { + id: VerificationString; + }; +} +export function VerifyRoute(req: VerifyRequest, res: Response) { + checkVerification(req.body.id, (success, msg, withUserID) => { if (success) { + req.session.verified = success; + req.session.verifiedBy = withUserID!; if (req.session.user) { // If Logged In - addContact(req.session.user, withUserID, (success, msg) => { + addContact(req.session.user, withUserID!, (success, msg) => { res .status(success ? 200 : 400) .send({ success: success, message: msg, loggedIn: true }); @@ -24,16 +30,19 @@ function VerifyRoute(req, res) { }); } -function checkVerification(id, done) { +function checkVerification( + verification: VerificationString, + callback: (success: boolean, msg: string, userID?: UserRowID) => void +): void { User.findOne({ where: { - verification: decodeURIComponent(id), + verification: decodeURIComponent(verification), }, }).then((user) => { if (user) { - done(true, "User verified", user.id); + callback(true, "User verified", user.id); } else { - done(false, "No such verification"); + callback(false, "No such verification"); } }); } diff --git a/src/session.ts b/src/session.ts index 4812e45..dd1739c 100644 --- a/src/session.ts +++ b/src/session.ts @@ -1,7 +1,8 @@ -const { store } = require("./db/db"); +import { SessionOptions } from "express-session"; +import { store } from "./db/db"; -const sessionOpts = { - secret: process.env.SERVER_SESSION_SECRET, +export const sessionOpts: SessionOptions = { + secret: process.env.SERVER_SESSION_SECRET!, resave: false, saveUninitialized: false, cookie: { @@ -12,7 +13,4 @@ const sessionOpts = { store: store, }; -const corsOpts = { credentials: true, origin: true, secure: true }; - -exports.sessionOpts = sessionOpts; -exports.corsOpts = corsOpts; +export const corsOpts = { credentials: true, origin: true, secure: true }; diff --git a/src/telegram.ts b/src/telegram.ts index 73c601c..1bac0b3 100644 --- a/src/telegram.ts +++ b/src/telegram.ts @@ -1,6 +1,9 @@ -const { default: axios } = require("axios"); +import axios from "axios"; +import { TelegramID } from "./db/db"; -function setTelegramWebHook(done) { +export function setTelegramWebHook( + callback: (success: boolean) => void = () => {} +): void { const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/setWebhook`; axios .post(url, { @@ -9,14 +12,18 @@ function setTelegramWebHook(done) { drop_pending_updates: true, }) .then((res) => { - done(res); + callback(!!res); }) .catch((err) => { - done(err); + callback(!!err); }); } -function sendTelegramMessage(telegramID, message, done) { +export function sendTelegramMessage( + telegramID: TelegramID, + message: string, + callback: (success: boolean) => void = () => {} +): void { const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/sendMessage`; axios .post(url, { @@ -24,15 +31,12 @@ function sendTelegramMessage(telegramID, message, done) { text: message, }) .then((res) => { - done(res); + callback(!!res); }) .catch((err) => { console.error("Problem sending Telegram message."); - done(err); + callback(!!err); }); } -setTelegramWebHook(() => {}); - -exports.sendTelegramMessage = sendTelegramMessage; -exports.setTelegramWebHook = setTelegramWebHook; +setTelegramWebHook(); diff --git a/src/utils.ts b/src/utils.ts index ea1a9a1..6f3cbe4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,3 @@ -function getCookieExpiry() { - return new Date(Date.now() + process.env.COOKIE_EXPIRY_DURATION); +export function getCookieExpiry(): Date { + return new Date(Date.now() + Number(process.env.COOKIE_EXPIRY_DURATION)); } - -exports.getCookieExpiry = getCookieExpiry; diff --git a/tsconfig.json b/tsconfig.json index 45a951a..3cdd8cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "@tsconfig/node14/tsconfig.json", "include": ["./src/**/*"], -} \ No newline at end of file + "compilerOptions": { + "outDir": "./built" + } +} From 958800aef3b3c504008552754dafc76834f25aa9 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Tue, 3 Aug 2021 02:13:57 +0200 Subject: [PATCH 05/18] remoev built files, updated gitignore --- .gitignore | 2 + built/app.js | 25 --------- built/db/db.js | 75 ------------------------- built/db/utils.js | 47 ---------------- built/routes/CodeRoute.js | 48 ---------------- built/routes/LoginRoute.js | 73 ------------------------ built/routes/TelegramWebhookRoute.js | 84 ---------------------------- built/routes/VerifyRoute.js | 44 --------------- built/session.js | 16 ------ built/telegram.js | 40 ------------- built/utils.js | 7 --- src/routes/TelegramWebhookRoute.ts | 2 +- 12 files changed, 3 insertions(+), 460 deletions(-) delete mode 100644 built/app.js delete mode 100644 built/db/db.js delete mode 100644 built/db/utils.js delete mode 100644 built/routes/CodeRoute.js delete mode 100644 built/routes/LoginRoute.js delete mode 100644 built/routes/TelegramWebhookRoute.js delete mode 100644 built/routes/VerifyRoute.js delete mode 100644 built/session.js delete mode 100644 built/telegram.js delete mode 100644 built/utils.js diff --git a/.gitignore b/.gitignore index be303de..52ebddf 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,5 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +built/**/* \ No newline at end of file diff --git a/built/app.js b/built/app.js deleted file mode 100644 index 715dc67..0000000 --- a/built/app.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const express = require("express"); -const session = require("express-session"); -const cors = require("cors"); -const session_1 = require("./session"); -const TelegramWebhookRoute_1 = require("./routes/TelegramWebhookRoute"); -const LoginRoute_1 = require("./routes/LoginRoute"); -const CodeRoute_1 = require("./routes/CodeRoute"); -const VerifyRoute_1 = require("./routes/VerifyRoute"); -require("dotenv-flow").config(); -console.log(`Node Environment: ${process.env.NODE_ENV}`); -const app = express(); -app.set("trust proxy", 1); -app.use(session(session_1.sessionOpts)); -app.use(cors(session_1.corsOpts)); -app.use(express.json()); -app.post(`/${process.env.TELEGRAM_SECRET}`, TelegramWebhookRoute_1.TelegramWebhookRoute); -app.post("/login", LoginRoute_1.LoginRoute); -app.get("/code", CodeRoute_1.CodeRoute); -app.post("/verify", VerifyRoute_1.VerifyRoute); -const port = process.env.PORT || 8080; -app.listen(port, () => { - console.log(`Listening on port ${port}`); -}); diff --git a/built/db/db.js b/built/db/db.js deleted file mode 100644 index c58ea90..0000000 --- a/built/db/db.js +++ /dev/null @@ -1,75 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.User = exports.Contact = exports.store = exports.storeDB = exports.sequelize = void 0; -const connect_session_sequelize_1 = __importDefault(require("connect-session-sequelize")); -const express_session_1 = __importDefault(require("express-session")); -const sequelize_1 = require("sequelize"); -const SequelizeStore = connect_session_sequelize_1.default(express_session_1.default.Store); -const isProduction = process.env.NODE_ENV == "production"; -exports.sequelize = (() => { - if (isProduction) { - return new sequelize_1.Sequelize(process.env.DB_DATA_NAME || "DATABASE", process.env.DB_USER || "USERNAME", process.env.DB_PASS || "PASSWORD", { - host: process.env.DB_PATH || "localhost", - dialect: "postgres", - }); - } - else { - return new sequelize_1.Sequelize("sqlite::memory:"); - } -})(); -exports.storeDB = (() => { - if (isProduction) { - return new sequelize_1.Sequelize(process.env.DB_STORE_NAME || "DATABASE", process.env.DB_USER || "USERNAME", process.env.DB_PASS || "PASSWORD", { - host: process.env.DB_PATH, - dialect: "postgres", - }); - } - else { - return new sequelize_1.Sequelize("sqlite::memory:"); - } -})(); -exports.store = new SequelizeStore({ - db: exports.storeDB, -}); -exports.Contact = exports.sequelize.define("Contact", { - user: { - type: sequelize_1.DataTypes.INTEGER, - allowNull: false, - }, - with: { - type: sequelize_1.DataTypes.INTEGER, - allowNull: false, - }, -}); -exports.User = exports.sequelize.define("User", { - id: { - type: sequelize_1.DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - telegram: { - type: sequelize_1.DataTypes.INTEGER, - allowNull: false, - unique: true, - }, - verification: { - type: sequelize_1.DataTypes.STRING, - }, - isInfected: { - type: sequelize_1.DataTypes.BOOLEAN, - }, -}); -exports.Contact.sync(); -exports.User.sync().then(() => { - if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { - exports.User.create({ - telegram: 12345, - }).catch(() => { - console.log("Couldn't create admin account. Probably exists."); - }); - } -}); -exports.store.sync(); diff --git a/built/db/utils.js b/built/db/utils.js deleted file mode 100644 index 28ee01d..0000000 --- a/built/db/utils.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createUser = exports.addContact = void 0; -const telegram_1 = require("../telegram"); -const db_1 = require("./db"); -function addContact(userATelegram, userBRowID, done) { - db_1.User.findOne({ where: { telegram: userATelegram } }).then((userA) => { - db_1.User.findOne({ where: { id: userBRowID } }).then((userB) => { - if (!!userA || !!userB) { - done(false, "Could not find user."); - return; - } - db_1.Contact.create({ user: userA.id, with: userBRowID }) - .then(() => { - console.log(`Registering contact between ${userA.id} and ${userBRowID}`); - telegram_1.sendTelegramMessage(userB.telegram, "Someone scanned your QR code. You will be notified if they are tested positive with Covid. If you are tested positive, please tell this bot /COVIDPOSITIVE"); - done(true, "Successfully added contact"); - }) - .catch((e) => { - done(false, e); - }); - }); - }); -} -exports.addContact = addContact; -function createUser(telegram, callback) { - db_1.User.create({ - telegram: telegram, - }) - .then((user) => { - if (!user) { - callback(false, "Could not create user"); - } - else { - callback(true, "Success"); - } - }) - .catch((reason) => { - if (reason.name == "SequelizeUniqueConstraintError") { - callback(false, "User already exists"); - } - else { - callback(false, "Unknown error"); - } - }); -} -exports.createUser = createUser; diff --git a/built/routes/CodeRoute.js b/built/routes/CodeRoute.js deleted file mode 100644 index 133c1e0..0000000 --- a/built/routes/CodeRoute.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CodeRoute = void 0; -const db_1 = require("../db/db"); -const bcrypt_1 = __importDefault(require("bcrypt")); -const qrcode_1 = __importDefault(require("qrcode")); -function CodeRoute(req, res) { - if (!req.session.user) { - res.status(401).send("Not logged in"); - return; - } - createQRCode(req.session.user, (err, url) => { - res.status(url ? 200 : 401).send({ error: err, data: url }); - }); -} -exports.CodeRoute = CodeRoute; -function createQRCode(telegram, callback) { - db_1.User.findOne({ - where: { - telegram: telegram, - }, - }) - .then((user) => { - user && - refreshVerification(user, (result) => { - const verifyURL = `${process.env.WEBSITE_URL}/#/verify/${encodeURIComponent(result.verification)}`; - qrcode_1.default.toDataURL(verifyURL, { width: 300, height: 300 }, (error, url) => { - callback(error.message, url); - }); - }); - }) - .catch((error) => { - callback(error); - }); -} -function refreshVerification(user, callback) { - let newVerification = bcrypt_1.default - .hashSync(`${new Date().getTime()}-${user.telegram}`, 5) - .replace(/[^a-zA-Z0-9]+/g, ""); - newVerification = newVerification.substr(0, newVerification.length / 2); - user.verification = newVerification; - user.save().then((result) => { - callback(result); - }); -} diff --git a/built/routes/LoginRoute.js b/built/routes/LoginRoute.js deleted file mode 100644 index 35e8694..0000000 --- a/built/routes/LoginRoute.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LoginRoute = void 0; -const db_1 = require("../db/db"); -const crypto_1 = __importDefault(require("crypto")); -const utils_1 = require("../db/utils"); -function LoginRoute(req, res) { - const telegramResponse = req.body.telegramResponse; - authUser(telegramResponse, (authObject) => { - if (authObject) { - const verified = req.session.verified; - const verifiedBy = req.session.verifiedBy; - req.session.regenerate(() => { - req.session.user = telegramResponse.id; - if (verified) { - utils_1.addContact(telegramResponse.id, verifiedBy, (contactSuccess) => { - res.send({ - authorized: authObject.authorized, - contactSuccess: contactSuccess, - }); - }); - } - else { - res.send(authObject); - } - }); - } - else { - res.status(401).send(authObject); - } - }); -} -exports.LoginRoute = LoginRoute; -function authUser(telegramResponse, callback) { - let dataCheckArray = []; - for (const [key, value] of Object.entries(telegramResponse)) { - if (key == "hash") - continue; - dataCheckArray.push(`${key}=${value}`); - } - dataCheckArray.sort(); - const dataCheckString = dataCheckArray.join("\n"); - const secretKey = crypto_1.default - .createHash("sha256") - .update(process.env.TELEGRAM_TOKEN) - .digest(); - const confirmationHash = crypto_1.default - .createHmac("sha256", secretKey) - .update(dataCheckString) - .digest("hex"); - const authorized = confirmationHash == telegramResponse.hash; - if (!authorized) { - callback({ authorized: false }); - } - db_1.User.findOne({ - where: { - telegram: telegramResponse.id, - }, - }).then((user) => { - if (!user) { - utils_1.createUser(telegramResponse.id, (success) => { - callback({ authorized: success }); - }); - } - else { - callback({ authorized: true }); - } - }); -} -exports.LoginRoute = LoginRoute; diff --git a/built/routes/TelegramWebhookRoute.js b/built/routes/TelegramWebhookRoute.js deleted file mode 100644 index 8054f69..0000000 --- a/built/routes/TelegramWebhookRoute.js +++ /dev/null @@ -1,84 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TelegramWebhookRoute = void 0; -const types_1 = require("sequelize/types"); -const db_1 = require("../db/db"); -const telegram_1 = require("../telegram"); -function TelegramWebhookRoute(req, res) { - try { - const messageText = req.body.message.text; - const telegramID = req.body.message.from.id; - if (messageText.toLowerCase() == "/covidpositive") { - userInfected(telegramID, (result) => { - if (result.saved) { - telegram_1.sendTelegramMessage(telegramID, "Thanks for informing us. We will notify the people you were in contact with!"); - informContacts(telegramID); - } - else { - telegram_1.sendTelegramMessage(telegramID, "Sorry, something went wrong."); - } - }); - } - } - catch (e) { - console.log("Could not get Telegram Message"); - } - res.send(); -} -exports.TelegramWebhookRoute = TelegramWebhookRoute; -function informContacts(telegramID) { - db_1.User.findOne({ - where: { - telegram: telegramID, - }, - }).then((user) => { - if (user) { - const userRowID = user.id; - db_1.Contact.findAll({ - where: { - [types_1.Op.or]: [{ user: userRowID }, { with: userRowID }], - }, - }).then((result) => { - result.forEach((contact) => { - const otherPersonID = contact.user == userRowID ? contact.with : contact.user; - db_1.User.findOne({ - where: { - id: otherPersonID, - }, - }).then((otherPerson) => { - otherPerson && - telegram_1.sendTelegramMessage(otherPerson.telegram, "You're infected."); - }); - }); - }); - } - }); -} -function userInfected(telegramID, callback) { - db_1.User.findOne({ - where: { - telegram: telegramID, - }, - }) - .then((user) => { - if (!user) { - callback({ saved: false }); - } - else { - user.isInfected = true; - user - .save() - .then((result) => { - if (result) { - callback({ saved: true }); - } - }) - .catch((err) => { - callback({ saved: false }); - }); - } - }) - .catch((err) => { - callback({ saved: false }); - }); -} diff --git a/built/routes/VerifyRoute.js b/built/routes/VerifyRoute.js deleted file mode 100644 index b60b088..0000000 --- a/built/routes/VerifyRoute.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VerifyRoute = void 0; -const db_1 = require("../db/db"); -const utils_1 = require("../db/utils"); -function VerifyRoute(req, res) { - checkVerification(req.body.id, (success, msg, withUserID) => { - if (success) { - req.session.verified = success; - req.session.verifiedBy = withUserID; - if (req.session.user) { - // If Logged In - utils_1.addContact(req.session.user, withUserID, (success, msg) => { - res - .status(success ? 200 : 400) - .send({ success: success, message: msg, loggedIn: true }); - }); - } - else { - // If Not Logged In - res.send({ success: success, message: msg, loggedIn: false }); - } - } - else { - res.status(400).send({ success: success, message: msg }); - } - }); -} -exports.VerifyRoute = VerifyRoute; -function checkVerification(verification, callback) { - db_1.User.findOne({ - where: { - verification: decodeURIComponent(verification), - }, - }).then((user) => { - if (user) { - callback(true, "User verified", user.id); - } - else { - callback(false, "No such verification"); - } - }); -} -exports.VerifyRoute = VerifyRoute; diff --git a/built/session.js b/built/session.js deleted file mode 100644 index 7b19ef2..0000000 --- a/built/session.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.corsOpts = exports.sessionOpts = void 0; -const db_1 = require("./db/db"); -exports.sessionOpts = { - secret: process.env.SERVER_SESSION_SECRET, - resave: false, - saveUninitialized: false, - cookie: { - secure: true, - sameSite: "none", - maxAge: Number(process.env.SESSION_LENGTH), - }, - store: db_1.store, -}; -exports.corsOpts = { credentials: true, origin: true, secure: true }; diff --git a/built/telegram.js b/built/telegram.js deleted file mode 100644 index 9b1014c..0000000 --- a/built/telegram.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.sendTelegramMessage = exports.setTelegramWebHook = void 0; -const axios_1 = __importDefault(require("axios")); -function setTelegramWebHook(callback = () => { }) { - const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/setWebhook`; - axios_1.default - .post(url, { - url: `${process.env.SERVER_API_URL}/${process.env.TELEGRAM_SECRET}`, - allowed_updates: [], - drop_pending_updates: true, - }) - .then((res) => { - callback(!!res); - }) - .catch((err) => { - callback(!!err); - }); -} -exports.setTelegramWebHook = setTelegramWebHook; -function sendTelegramMessage(telegramID, message, callback = () => { }) { - const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/sendMessage`; - axios_1.default - .post(url, { - chat_id: telegramID, - text: message, - }) - .then((res) => { - callback(!!res); - }) - .catch((err) => { - console.error("Problem sending Telegram message."); - callback(!!err); - }); -} -exports.sendTelegramMessage = sendTelegramMessage; -setTelegramWebHook(); diff --git a/built/utils.js b/built/utils.js deleted file mode 100644 index ce4af57..0000000 --- a/built/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getCookieExpiry = void 0; -function getCookieExpiry() { - return new Date(Date.now() + Number(process.env.COOKIE_EXPIRY_DURATION)); -} -exports.getCookieExpiry = getCookieExpiry; diff --git a/src/routes/TelegramWebhookRoute.ts b/src/routes/TelegramWebhookRoute.ts index f33c9e7..983b9ee 100644 --- a/src/routes/TelegramWebhookRoute.ts +++ b/src/routes/TelegramWebhookRoute.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { Op } from "sequelize/types"; +import { Op } from "sequelize"; import { Contact, TelegramID, User } from "../db/db"; import { strings_en } from "../strings"; import { sendTelegramMessage } from "../telegram"; From 99a7e994caa69486e3a2ea9a11e0aae0927e7673 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Tue, 3 Aug 2021 03:03:16 +0200 Subject: [PATCH 06/18] dotenv flow fix --- package-lock.json | 13 +++++++++++++ package.json | 1 + src/app.ts | 10 ++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f14058..99d31f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@tsconfig/node14": "^1.0.1", "@types/bcrypt": "^5.0.0", "@types/cors": "^2.8.12", + "@types/dotenv-flow": "^3.1.1", "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", "@types/qrcode": "^1.4.1", @@ -264,6 +265,12 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "node_modules/@types/dotenv-flow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/dotenv-flow/-/dotenv-flow-3.1.1.tgz", + "integrity": "sha512-khxgP+KkHPL72SP0Wqn1gB6EHj6yk79OBGJEKW64XL13RbyDGTkRbbA47VICOLRrvgKOpZeun2uMsgO7pAsExQ==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -4755,6 +4762,12 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "@types/dotenv-flow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/dotenv-flow/-/dotenv-flow-3.1.1.tgz", + "integrity": "sha512-khxgP+KkHPL72SP0Wqn1gB6EHj6yk79OBGJEKW64XL13RbyDGTkRbbA47VICOLRrvgKOpZeun2uMsgO7pAsExQ==", + "dev": true + }, "@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", diff --git a/package.json b/package.json index 75e5089..591a81a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@tsconfig/node14": "^1.0.1", "@types/bcrypt": "^5.0.0", "@types/cors": "^2.8.12", + "@types/dotenv-flow": "^3.1.1", "@types/express": "^4.17.13", "@types/express-session": "^1.17.4", "@types/qrcode": "^1.4.1", diff --git a/src/app.ts b/src/app.ts index 54370c6..4c0cb26 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,12 +1,14 @@ -import express = require("express"); -import session = require("express-session"); -import cors = require("cors"); +import * as dotenvFlow from "dotenv-flow"; +dotenvFlow.config(); + +import express from "express"; +import session from "express-session"; +import cors from "cors"; import { corsOpts, sessionOpts } from "./session"; import { TelegramWebhookRoute } from "./routes/TelegramWebhookRoute"; import { LoginRoute } from "./routes/LoginRoute"; import { CodeRoute } from "./routes/CodeRoute"; import { VerifyRoute } from "./routes/VerifyRoute"; -require("dotenv-flow").config(); console.log(`Node Environment: ${process.env.NODE_ENV}`); From c3b115386c5c3390bd94004f48a16e4f8b5bf65d Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao <629990+naresh97@users.noreply.github.com> Date: Tue, 3 Aug 2021 03:33:23 +0200 Subject: [PATCH 07/18] small bugfix and cleanup --- src/routes/LoginRoute.ts | 2 -- src/strings.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index b28dc37..f0329db 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -93,5 +93,3 @@ function authUser( } }); } - -exports.LoginRoute = LoginRoute; diff --git a/src/strings.ts b/src/strings.ts index 9509dc8..6c608b7 100644 --- a/src/strings.ts +++ b/src/strings.ts @@ -15,5 +15,3 @@ local COVID19 guidelines.", be notified if they report being tested positive with COVID19. If \ you are tested positive, please tell me /COVIDPOSITIVE", }; - -exports.strings_en = strings_en; From 06a3f97ee7ead2f2797d216c969f85f2b57c3ddb Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao <629990+naresh97@users.noreply.github.com> Date: Tue, 3 Aug 2021 03:33:44 +0200 Subject: [PATCH 08/18] bugfix & cleanup --- .eslintrc.js | 12 ------------ src/db/utils.ts | 2 +- src/routes/CodeRoute.ts | 4 ++-- src/routes/VerifyRoute.ts | 4 +--- 4 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 9ae7956..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - env: { - node: true, - commonjs: true, - es2021: true, - }, - extends: "eslint:recommended", - parserOptions: { - ecmaVersion: 12, - }, - rules: {}, -}; diff --git a/src/db/utils.ts b/src/db/utils.ts index 6e5f3d3..552463d 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -9,7 +9,7 @@ export function addContact( ): void { User.findOne({ where: { telegram: userATelegram } }).then((userA) => { User.findOne({ where: { id: userBRowID } }).then((userB) => { - if (!!userA || !!userB) { + if (!userA || !userB) { done(false, "Could not find user."); return; } diff --git a/src/routes/CodeRoute.ts b/src/routes/CodeRoute.ts index 9f435b4..aee5b49 100644 --- a/src/routes/CodeRoute.ts +++ b/src/routes/CodeRoute.ts @@ -21,7 +21,7 @@ export function CodeRoute(req: Request, res: Response) { function createQRCode( telegram: TelegramID, - callback: (errorMessage: string, url?: string) => void + callback: (errorMessage: string | Error, url?: string) => void ): void { User.findOne({ where: { @@ -38,7 +38,7 @@ function createQRCode( verifyURL, { width: 300, height: 300 } as QRCodeToDataURLOptions, (error, url) => { - callback(error.message, url); + callback(error, url); } ); }); diff --git a/src/routes/VerifyRoute.ts b/src/routes/VerifyRoute.ts index 43295f3..8a3b47b 100644 --- a/src/routes/VerifyRoute.ts +++ b/src/routes/VerifyRoute.ts @@ -45,6 +45,4 @@ function checkVerification( callback(false, "No such verification"); } }); -} - -exports.VerifyRoute = VerifyRoute; +} \ No newline at end of file From 8d05734ee93e472a2e52ad0f9c12231411e2356f Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Tue, 3 Aug 2021 04:09:32 +0200 Subject: [PATCH 09/18] split up models into separate files, other cleanup --- src/db/db.ts | 69 +----------------------------- src/db/models/Contact.ts | 23 ++++++++++ src/db/models/User.ts | 45 +++++++++++++++++++ src/db/utils.ts | 9 ++-- src/routes/CodeRoute.ts | 9 +--- src/routes/LoginRoute.ts | 10 +---- src/routes/TelegramWebhookRoute.ts | 39 +++++++++-------- src/routes/VerifyRoute.ts | 5 ++- src/telegram.ts | 2 +- src/types.ts | 11 +++++ 10 files changed, 114 insertions(+), 108 deletions(-) create mode 100644 src/db/models/Contact.ts create mode 100644 src/db/models/User.ts create mode 100644 src/types.ts diff --git a/src/db/db.ts b/src/db/db.ts index ed18cec..180856b 100644 --- a/src/db/db.ts +++ b/src/db/db.ts @@ -1,6 +1,6 @@ import ConnectSessionSequelize from "connect-session-sequelize"; import session from "express-session"; -import { DataTypes, Model, Optional, Sequelize } from "sequelize"; +import { Sequelize } from "sequelize"; const SequelizeStore = ConnectSessionSequelize(session.Store); @@ -41,71 +41,4 @@ export const storeDB: Sequelize = (() => { export const store = new SequelizeStore({ db: storeDB, }); - -export type UserRowID = number; -export type TelegramID = number; -export type VerificationString = string; - -interface ContactAttributes { - user: UserRowID; - with: UserRowID; -} -export interface ContactInterface - extends Model, - ContactAttributes {} -export const Contact = sequelize.define("Contact", { - user: { - type: DataTypes.INTEGER, - allowNull: false, - }, - with: { - type: DataTypes.INTEGER, - allowNull: false, - }, -}); - -interface UserAttributes { - id: UserRowID; - telegram: TelegramID; - verification: VerificationString; - isInfected: boolean; -} -interface UserCreationAttributes { - telegram: TelegramID; -} -export interface UserInstance - extends Model, - UserAttributes {} - -export const User = sequelize.define("User", { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, - telegram: { - type: DataTypes.INTEGER, - allowNull: false, - unique: true, - }, - verification: { - type: DataTypes.STRING, - }, - isInfected: { - type: DataTypes.BOOLEAN, - }, -}); - -Contact.sync(); - -User.sync().then(() => { - if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { - User.create({ - telegram: 12345, - }).catch(() => { - console.log("Couldn't create admin account. Probably exists."); - }); - } -}); - store.sync(); diff --git a/src/db/models/Contact.ts b/src/db/models/Contact.ts new file mode 100644 index 0000000..c009cca --- /dev/null +++ b/src/db/models/Contact.ts @@ -0,0 +1,23 @@ +import { DataTypes, Model } from "sequelize"; +import { UserRowID } from "../../types"; +import { sequelize } from "../db"; + +interface ContactAttributes { + user: UserRowID; + with: UserRowID; +} +export interface ContactInterface + extends Model, + ContactAttributes {} +export const Contact = sequelize.define("Contact", { + user: { + type: DataTypes.INTEGER, + allowNull: false, + }, + with: { + type: DataTypes.INTEGER, + allowNull: false, + }, +}); + +Contact.sync(); diff --git a/src/db/models/User.ts b/src/db/models/User.ts new file mode 100644 index 0000000..b16b2b3 --- /dev/null +++ b/src/db/models/User.ts @@ -0,0 +1,45 @@ +import { DataTypes, Model } from "sequelize"; +import { TelegramID, UserRowID, VerificationString } from "../../types"; +import { sequelize } from "../db"; + +interface UserAttributes { + id: UserRowID; + telegram: TelegramID; + verification: VerificationString; + isInfected: boolean; +} +interface UserCreationAttributes { + telegram: TelegramID; +} +export interface UserInstance + extends Model, + UserAttributes {} + +export const User = sequelize.define("User", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + telegram: { + type: DataTypes.INTEGER, + allowNull: false, + unique: true, + }, + verification: { + type: DataTypes.STRING, + }, + isInfected: { + type: DataTypes.BOOLEAN, + }, +}); + +User.sync().then(() => { + if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { + User.create({ + telegram: 12345, + }).catch(() => { + console.log("Couldn't create admin account. Probably exists."); + }); + } +}); diff --git a/src/db/utils.ts b/src/db/utils.ts index 552463d..e69e054 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -1,6 +1,8 @@ import { strings_en } from "../strings"; import { sendTelegramMessage } from "../telegram"; -import { User, Contact, TelegramID, UserRowID } from "./db"; +import { TelegramID, UserRowID } from "../types"; +import { Contact } from "./models/Contact"; +import { User } from "./models/User"; export function addContact( userATelegram: TelegramID, @@ -19,10 +21,7 @@ export function addContact( console.log( `Registering contact between ${userA!.id} and ${userBRowID}` ); - sendTelegramMessage( - userB!.telegram, - strings_en.telegram_qr_scanned, - ); + sendTelegramMessage(userB!.telegram, strings_en.telegram_qr_scanned); done(true, "Successfully added contact"); }) .catch((e) => { diff --git a/src/routes/CodeRoute.ts b/src/routes/CodeRoute.ts index aee5b49..ee491b6 100644 --- a/src/routes/CodeRoute.ts +++ b/src/routes/CodeRoute.ts @@ -1,13 +1,8 @@ import { Request, Response } from "express"; -import { TelegramID, User, UserInstance } from "../db/db"; import bcrypt from "bcrypt"; import QRCode, { QRCodeToDataURLOptions } from "qrcode"; - -declare module "express-session" { - interface Session { - user: TelegramID; - } -} +import { TelegramID } from "../types"; +import { User, UserInstance } from "../db/models/User"; export function CodeRoute(req: Request, res: Response) { if (!req.session.user) { diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index f0329db..c40075a 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -1,14 +1,8 @@ import { Request, Response } from "express"; -import { TelegramID, User, UserRowID } from "../db/db"; import crypto from "crypto"; import { addContact, createUser } from "../db/utils"; - -declare module "express-session" { - interface Session { - verified: boolean; - verifiedBy: UserRowID; - } -} +import { TelegramID, UserRowID } from "../types"; +import { User } from "../db/models/User"; type TelegramLoginResponse = { id: TelegramID; diff --git a/src/routes/TelegramWebhookRoute.ts b/src/routes/TelegramWebhookRoute.ts index 983b9ee..3bd7cab 100644 --- a/src/routes/TelegramWebhookRoute.ts +++ b/src/routes/TelegramWebhookRoute.ts @@ -1,8 +1,10 @@ import { Request, Response } from "express"; import { Op } from "sequelize"; -import { Contact, TelegramID, User } from "../db/db"; +import { Contact } from "../db/models/Contact"; +import { User } from "../db/models/User"; import { strings_en } from "../strings"; import { sendTelegramMessage } from "../telegram"; +import { TelegramID } from "../types"; interface TelegramWebhookRequest extends Request { body: { @@ -27,22 +29,22 @@ export function TelegramWebhookRoute( "Thanks for using OurSejahtera! Let's stay safer together <3" ); } else { - const messageText = req.body.message.text; - const telegramID = req.body.message.from.id; - if (messageText.toLowerCase() == "/covidpositive") { - userInfected(telegramID, (result) => { - if (result.saved) { - sendTelegramMessage( - telegramID, - strings_en.telegram_inform_positive, - ); - informContacts(telegramID); - } else { - sendTelegramMessage(telegramID, "Sorry, something went wrong."); - } - }); + const messageText = req.body.message.text; + const telegramID = req.body.message.from.id; + if (messageText.toLowerCase() == "/covidpositive") { + userInfected(telegramID, (result) => { + if (result.saved) { + sendTelegramMessage( + telegramID, + strings_en.telegram_inform_positive + ); + informContacts(telegramID); + } else { + sendTelegramMessage(telegramID, "Sorry, something went wrong."); + } + }); + } } - } } catch (e) { console.log("Could not get Telegram Message"); } @@ -72,7 +74,10 @@ function informContacts(telegramID: TelegramID) { }, }).then((otherPerson) => { otherPerson && - sendTelegramMessage(otherPerson.telegram, strings_en.telegram_inform_infect); + sendTelegramMessage( + otherPerson.telegram, + strings_en.telegram_inform_infect + ); }); }); }); diff --git a/src/routes/VerifyRoute.ts b/src/routes/VerifyRoute.ts index 8a3b47b..92e93e5 100644 --- a/src/routes/VerifyRoute.ts +++ b/src/routes/VerifyRoute.ts @@ -1,6 +1,7 @@ import { Request, Response } from "express"; -import { User, UserRowID, VerificationString } from "../db/db"; +import { User } from "../db/models/User"; import { addContact } from "../db/utils"; +import { UserRowID, VerificationString } from "../types"; interface VerifyRequest extends Request { body: { @@ -45,4 +46,4 @@ function checkVerification( callback(false, "No such verification"); } }); -} \ No newline at end of file +} diff --git a/src/telegram.ts b/src/telegram.ts index 1bac0b3..7d631ab 100644 --- a/src/telegram.ts +++ b/src/telegram.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { TelegramID } from "./db/db"; +import { TelegramID } from "./types"; export function setTelegramWebHook( callback: (success: boolean) => void = () => {} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..060b9a3 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,11 @@ +export type UserRowID = number; +export type TelegramID = number; +export type VerificationString = string; + +declare module "express-session" { + interface Session { + verified: boolean; + verifiedBy: UserRowID; + user: TelegramID; + } +} From 84cc73e2f9d5655da50d0d3f05bfec983912a492 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Wed, 4 Aug 2021 00:04:40 +0200 Subject: [PATCH 10/18] extracted functions into helpers, use some nominal types --- .gitignore | 2 +- package.json | 3 +- src/db/models/Contact.helper.ts | 0 src/db/models/User.helper.ts | 53 +++++++++++++++++++++++ src/db/models/User.ts | 2 +- src/db/utils.ts | 21 ++++----- src/routes/CodeRoute.ts | 52 +++++++++++------------ src/routes/LoginRoute.ts | 46 ++++++++++---------- src/routes/TelegramWebhookRoute.ts | 68 +++++++++++------------------- src/routes/VerifyRoute.ts | 61 ++++++++++++--------------- src/types.ts | 15 ++++--- tsconfig.json | 2 +- 12 files changed, 177 insertions(+), 148 deletions(-) create mode 100644 src/db/models/Contact.helper.ts create mode 100644 src/db/models/User.helper.ts diff --git a/.gitignore b/.gitignore index 52ebddf..b6347cf 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,4 @@ dist .yarn/install-state.gz .pnp.* -built/**/* \ No newline at end of file +dist/**/* \ No newline at end of file diff --git a/package.json b/package.json index 591a81a..761aaa6 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "src/app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "node src/app.js" + "start": "tsc && NODE_ENV=production node dist/app.js", + "dev": "NODE_ENV=development tsc && node dist/app.js" }, "author": "", "license": "ISC", diff --git a/src/db/models/Contact.helper.ts b/src/db/models/Contact.helper.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/db/models/User.helper.ts b/src/db/models/User.helper.ts new file mode 100644 index 0000000..7fbeabc --- /dev/null +++ b/src/db/models/User.helper.ts @@ -0,0 +1,53 @@ +import { TelegramID, UserRowID, VerificationString } from "../../types"; +import { User, UserInstance } from "./User"; + +export function getUserByTelegramID( + telegramID: TelegramID, + callback: (user?: UserInstance, message?: string) => void +): void { + User.findOne({ + where: { + telegram: telegramID, + }, + }) + .then((result) => { + callback(!!result ? result : undefined); + }) + .catch(() => { + callback(undefined); + }); +} + +export function getUserByRowID( + rowID: UserRowID, + callback: (user?: UserInstance, message?: string) => void +): void { + User.findOne({ + where: { + id: rowID, + }, + }) + .then((result) => { + callback(!!result ? result : undefined); + }) + .catch(() => { + callback(undefined); + }); +} + +export function getUserByVerification( + verification: VerificationString, + callback: (user?: UserInstance, message?: string) => void +): void { + User.findOne({ + where: { + verification: verification, + }, + }) + .then((result) => { + callback(!!result ? result : undefined); + }) + .catch(() => { + callback(undefined); + }); +} diff --git a/src/db/models/User.ts b/src/db/models/User.ts index b16b2b3..00a14fd 100644 --- a/src/db/models/User.ts +++ b/src/db/models/User.ts @@ -37,7 +37,7 @@ export const User = sequelize.define("User", { User.sync().then(() => { if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { User.create({ - telegram: 12345, + telegram: 12345 as TelegramID, }).catch(() => { console.log("Couldn't create admin account. Probably exists."); }); diff --git a/src/db/utils.ts b/src/db/utils.ts index e69e054..53f92f0 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -3,29 +3,30 @@ import { sendTelegramMessage } from "../telegram"; import { TelegramID, UserRowID } from "../types"; import { Contact } from "./models/Contact"; import { User } from "./models/User"; +import { getUserByRowID, getUserByTelegramID } from "./models/User.helper"; export function addContact( userATelegram: TelegramID, - userBRowID: UserRowID, - done: (success: boolean, message: string) => void + userBTelegram: TelegramID, + callback: (success: boolean, message?: string) => void ): void { - User.findOne({ where: { telegram: userATelegram } }).then((userA) => { - User.findOne({ where: { id: userBRowID } }).then((userB) => { + getUserByTelegramID(userATelegram, (userA) => { + getUserByTelegramID(userBTelegram, (userB) => { if (!userA || !userB) { - done(false, "Could not find user."); + callback(false, "Could not find user."); return; } - Contact.create({ user: userA!.id, with: userBRowID }) + Contact.create({ user: userA.id, with: userB.id }) .then(() => { console.log( - `Registering contact between ${userA!.id} and ${userBRowID}` + `Registering contact between ${userA.id} and ${userB.id}` ); - sendTelegramMessage(userB!.telegram, strings_en.telegram_qr_scanned); - done(true, "Successfully added contact"); + sendTelegramMessage(userB.telegram, strings_en.telegram_qr_scanned); + callback(true, "Successfully added contact"); }) .catch((e) => { - done(false, e); + callback(false, e); }); }); }); diff --git a/src/routes/CodeRoute.ts b/src/routes/CodeRoute.ts index ee491b6..acbf941 100644 --- a/src/routes/CodeRoute.ts +++ b/src/routes/CodeRoute.ts @@ -1,15 +1,16 @@ import { Request, Response } from "express"; import bcrypt from "bcrypt"; import QRCode, { QRCodeToDataURLOptions } from "qrcode"; -import { TelegramID } from "../types"; +import { TelegramID, VerificationString } from "../types"; import { User, UserInstance } from "../db/models/User"; +import { getUserByTelegramID } from "../db/models/User.helper"; export function CodeRoute(req: Request, res: Response) { - if (!req.session.user) { + if (!req.session.userTelegramID) { res.status(401).send("Not logged in"); return; } - createQRCode(req.session.user, (err, url) => { + createQRCode(req.session.userTelegramID, (err, url) => { res.status(url ? 200 : 401).send({ error: err, data: url }); }); } @@ -18,29 +19,21 @@ function createQRCode( telegram: TelegramID, callback: (errorMessage: string | Error, url?: string) => void ): void { - User.findOne({ - where: { - telegram: telegram, - }, - }) - .then((user) => { - user && - refreshVerification(user, (result) => { - const verifyURL = `${ - process.env.WEBSITE_URL - }/#/verify/${encodeURIComponent(result.verification)}`; - QRCode.toDataURL( - verifyURL, - { width: 300, height: 300 } as QRCodeToDataURLOptions, - (error, url) => { - callback(error, url); - } - ); - }); - }) - .catch((error) => { - callback(error); - }); + getUserByTelegramID(telegram, (user) => { + !!user && + refreshVerification(user, (result) => { + const verifyURL = `${ + process.env.WEBSITE_URL + }/#/verify/${encodeURIComponent(result.verification)}`; + QRCode.toDataURL( + verifyURL, + { width: 300, height: 300 } as QRCodeToDataURLOptions, + (error, url) => { + callback(error, url); + } + ); + }); + }); } function refreshVerification( @@ -49,8 +42,11 @@ function refreshVerification( ): void { let newVerification = bcrypt .hashSync(`${new Date().getTime()}-${user.telegram}`, 5) - .replace(/[^a-zA-Z0-9]+/g, ""); - newVerification = newVerification.substr(0, newVerification.length / 2); + .replace(/[^a-zA-Z0-9]+/g, "") as VerificationString; + newVerification = newVerification.substr( + 0, + newVerification.length / 2 + ) as VerificationString; user.verification = newVerification; user.save().then((result) => { callback(result); diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index c40075a..1548b0d 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -3,6 +3,7 @@ import crypto from "crypto"; import { addContact, createUser } from "../db/utils"; import { TelegramID, UserRowID } from "../types"; import { User } from "../db/models/User"; +import { getUserByTelegramID } from "../db/models/User.helper"; type TelegramLoginResponse = { id: TelegramID; @@ -17,37 +18,37 @@ interface LoginRequest extends Request { export function LoginRoute(req: LoginRequest, res: Response) { const telegramResponse = req.body.telegramResponse; - authUser(telegramResponse, (authObject) => { - if (authObject) { + authUser(telegramResponse, (authorized) => { + if (authorized) { // User is already logged in - if (req.session.user == telegramResponse.id) { - res.send(authObject); + if (req.session.userTelegramID == telegramResponse.id) { + res.send(authorized); return; } - const verified = req.session.verified; - const verifiedBy = req.session.verifiedBy; + const verified = req.session.isVerified; + const verifiedBy = req.session.verifiedByTelegramID; req.session.regenerate(() => { - req.session.user = telegramResponse.id; + req.session.userTelegramID = telegramResponse.id; if (verified) { - addContact(telegramResponse.id, verifiedBy, (contactSuccess) => { + addContact(telegramResponse.id, verifiedBy, (success) => { res.send({ - authorized: authObject.authorized, - contactSuccess: contactSuccess, + authorized: authorized, + contactSuccess: success, }); }); } else { - res.send(authObject); + res.send(authorized); } }); } else { - res.status(401).send(authObject); + res.status(401).send(authorized); } }); } function authUser( telegramResponse: TelegramLoginResponse, - callback: (callbackObject: { authorized: boolean }) => void + callback: (authorized: boolean, message?: string) => void ): void { let dataCheckArray = []; @@ -70,20 +71,17 @@ function authUser( const authorized = confirmationHash == telegramResponse.hash; if (!authorized) { - callback({ authorized: false }); + callback(false); + return; } - User.findOne({ - where: { - telegram: telegramResponse.id, - }, - }).then((user) => { - if (!user) { - createUser(telegramResponse.id, (success) => { - callback({ authorized: success }); - }); + getUserByTelegramID(telegramResponse.id, (user) => { + if (!!user) { + callback(true); } else { - callback({ authorized: true }); + createUser(telegramResponse.id, (success, message) => { + callback(success, message); + }); } }); } diff --git a/src/routes/TelegramWebhookRoute.ts b/src/routes/TelegramWebhookRoute.ts index 3bd7cab..353af6a 100644 --- a/src/routes/TelegramWebhookRoute.ts +++ b/src/routes/TelegramWebhookRoute.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import { Op } from "sequelize"; import { Contact } from "../db/models/Contact"; import { User } from "../db/models/User"; +import { getUserByRowID, getUserByTelegramID } from "../db/models/User.helper"; import { strings_en } from "../strings"; import { sendTelegramMessage } from "../telegram"; import { TelegramID } from "../types"; @@ -32,8 +33,8 @@ export function TelegramWebhookRoute( const messageText = req.body.message.text; const telegramID = req.body.message.from.id; if (messageText.toLowerCase() == "/covidpositive") { - userInfected(telegramID, (result) => { - if (result.saved) { + userInfected(telegramID, (success) => { + if (success) { sendTelegramMessage( telegramID, strings_en.telegram_inform_positive @@ -53,29 +54,20 @@ export function TelegramWebhookRoute( } function informContacts(telegramID: TelegramID) { - User.findOne({ - where: { - telegram: telegramID, - }, - }).then((user) => { + getUserByTelegramID(telegramID, (user) => { if (user) { - const userRowID = user.id; Contact.findAll({ where: { - [Op.or]: [{ user: userRowID }, { with: userRowID }], + [Op.or]: [{ user: user.id }, { with: user.id }], }, }).then((result) => { result.forEach((contact) => { const otherPersonID = - contact.user == userRowID ? contact.with : contact.user; - User.findOne({ - where: { - id: otherPersonID, - }, - }).then((otherPerson) => { - otherPerson && + contact.user == user.id ? contact.with : contact.user; + getUserByRowID(otherPersonID, (otherUser) => { + otherUser && sendTelegramMessage( - otherPerson.telegram, + otherUser.telegram, strings_en.telegram_inform_infect ); }); @@ -87,31 +79,21 @@ function informContacts(telegramID: TelegramID) { function userInfected( telegramID: TelegramID, - callback: (callbackObject: { saved: boolean }) => void + callback: (success: boolean) => void ): void { - User.findOne({ - where: { - telegram: telegramID, - }, - }) - .then((user) => { - if (!user) { - callback({ saved: false }); - } else { - user.isInfected = true; - user - .save() - .then((result) => { - if (result) { - callback({ saved: true }); - } - }) - .catch((err) => { - callback({ saved: false }); - }); - } - }) - .catch((err) => { - callback({ saved: false }); - }); + getUserByTelegramID(telegramID, (user) => { + if (!!user) { + user.isInfected = true; + user + .save() + .then((result) => { + callback(!!result); + }) + .catch(() => { + callback(false); + }); + } else { + callback(false); + } + }); } diff --git a/src/routes/VerifyRoute.ts b/src/routes/VerifyRoute.ts index 92e93e5..0c05992 100644 --- a/src/routes/VerifyRoute.ts +++ b/src/routes/VerifyRoute.ts @@ -1,5 +1,6 @@ import { Request, Response } from "express"; import { User } from "../db/models/User"; +import { getUserByVerification } from "../db/models/User.helper"; import { addContact } from "../db/utils"; import { UserRowID, VerificationString } from "../types"; @@ -10,40 +11,34 @@ interface VerifyRequest extends Request { } export function VerifyRoute(req: VerifyRequest, res: Response) { - checkVerification(req.body.id, (success, msg, withUserID) => { - if (success) { - req.session.verified = success; - req.session.verifiedBy = withUserID!; - if (req.session.user) { - // If Logged In - addContact(req.session.user, withUserID!, (success, msg) => { - res - .status(success ? 200 : 400) - .send({ success: success, message: msg, loggedIn: true }); - }); + getUserByVerification( + decodeURIComponent(req.body.id) as VerificationString, + (verifiedByUser, message) => { + if (!!verifiedByUser) { + req.session.isVerified = !!verifiedByUser; + req.session.verifiedByTelegramID = verifiedByUser.telegram; + if (req.session.userTelegramID) { + // If Logged In + addContact( + req.session.userTelegramID, + verifiedByUser.telegram, + (success, message) => { + res + .status(success ? 200 : 400) + .send({ success: success, message: message, loggedIn: true }); + } + ); + } else { + // If Not Logged In + res.send({ + success: !!verifiedByUser, + message: message, + loggedIn: false, + }); + } } else { - // If Not Logged In - res.send({ success: success, message: msg, loggedIn: false }); + res.status(400).send({ success: !!verifiedByUser, message: message }); } - } else { - res.status(400).send({ success: success, message: msg }); } - }); -} - -function checkVerification( - verification: VerificationString, - callback: (success: boolean, msg: string, userID?: UserRowID) => void -): void { - User.findOne({ - where: { - verification: decodeURIComponent(verification), - }, - }).then((user) => { - if (user) { - callback(true, "User verified", user.id); - } else { - callback(false, "No such verification"); - } - }); + ); } diff --git a/src/types.ts b/src/types.ts index 060b9a3..a2f39b6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,11 +1,14 @@ -export type UserRowID = number; -export type TelegramID = number; -export type VerificationString = string; +/* + * Branding allows to use nominal typing and avoid errors + */ +export type UserRowID = number & { __userRowIDBrand: any }; +export type TelegramID = number & { __telegramIDBrand: any }; +export type VerificationString = string & { __verificationStringBrand: any }; declare module "express-session" { interface Session { - verified: boolean; - verifiedBy: UserRowID; - user: TelegramID; + isVerified: boolean; + verifiedByTelegramID: TelegramID; + userTelegramID: TelegramID; } } diff --git a/tsconfig.json b/tsconfig.json index 3cdd8cc..ce20dfc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,6 @@ "extends": "@tsconfig/node14/tsconfig.json", "include": ["./src/**/*"], "compilerOptions": { - "outDir": "./built" + "outDir": "./dist" } } From 42b87ca4075e74a7e8495e02b18facbe5470b1f1 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Wed, 4 Aug 2021 00:08:05 +0200 Subject: [PATCH 11/18] bugfix --- src/routes/LoginRoute.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 1548b0d..18f64ff 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -22,7 +22,7 @@ export function LoginRoute(req: LoginRequest, res: Response) { if (authorized) { // User is already logged in if (req.session.userTelegramID == telegramResponse.id) { - res.send(authorized); + res.send({authorized: authorized}); return; } const verified = req.session.isVerified; @@ -37,7 +37,7 @@ export function LoginRoute(req: LoginRequest, res: Response) { }); }); } else { - res.send(authorized); + res.send({authorized: authorized}); } }); } else { From 62019f64ad39b200f83e2e20ef237bc7bc7d9f57 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 17:25:13 +0200 Subject: [PATCH 12/18] converted covidroute to ts --- src/app.ts | 1 + src/db/models/User.helper.ts | 27 ++++++++++++++++ src/routes/CovidRoute.js | 61 ------------------------------------ src/routes/CovidRoute.ts | 26 +++++++++++++++ 4 files changed, 54 insertions(+), 61 deletions(-) delete mode 100644 src/routes/CovidRoute.js create mode 100644 src/routes/CovidRoute.ts diff --git a/src/app.ts b/src/app.ts index bae4aa5..f0d4dc0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -9,6 +9,7 @@ import { TelegramWebhookRoute } from "./routes/TelegramWebhookRoute"; import { LoginRoute } from "./routes/LoginRoute"; import { CodeRoute } from "./routes/CodeRoute"; import { VerifyRoute } from "./routes/VerifyRoute"; +import { CovidRoute } from "./routes/CovidRoute"; console.log(`Node Environment: ${process.env.NODE_ENV}`); diff --git a/src/db/models/User.helper.ts b/src/db/models/User.helper.ts index 7fbeabc..342522d 100644 --- a/src/db/models/User.helper.ts +++ b/src/db/models/User.helper.ts @@ -51,3 +51,30 @@ export function getUserByVerification( callback(undefined); }); } + +export function getUserCovidPositivity(telegramID: TelegramID, callback: (isInfected?: boolean) => void): void { + getUserByTelegramID(telegramID, user => { + if (!!user) { + const infectionDuration = +user.infectionDate - Date.now(); + if (infectionDuration > 60 * 60 * 24 * 14) { + setUserCovidPositivity(telegramID, false, success => { + callback(success ? false : undefined); + }); + } else { + callback(user.isInfected); + } + } else { + callback(); + } + }); +} + +export function setUserCovidPositivity(telegramID: TelegramID, infectionState: boolean, callback: (success: boolean) => void): void { + getUserByTelegramID(telegramID, user => { + if (!!user) { + user.isInfected = infectionState; + user.infectionDate = new Date(); + user.save().then(() => callback(true)).catch(() => callback(false)); + } else { callback(false) } + }); +} diff --git a/src/routes/CovidRoute.js b/src/routes/CovidRoute.js deleted file mode 100644 index b36cf5d..0000000 --- a/src/routes/CovidRoute.js +++ /dev/null @@ -1,61 +0,0 @@ -const { User } = require("../db/db"); - -function CovidRoute(req, res){ - if(!req.session.user){ - res.status(401).send("Not logged in"); - return; - } - - console.log(`SetPositive: ${req.body.setPositive}`); - - if(req.body.setPositive){ - setUserCovidPositive(req.session.user, true, response=>{ - res.send({covidPositive: response}); - }); - }else{ - getUserCovidPositivity(req.session.user, (success, positivity)=>{ - res.status(success ? 200 : 400).send({covidPositive: positivity}); - }); - } -} - -function getUserCovidPositivity(telegramID, callback){ - User.findOne({ - where: {telegram: telegramID}, - }) - .then(user=>{ - if(user){ - const infectionDuration = user.infectionDate - Date.now(); - if(infectionDuration > 60 * 60 * 24 * 14){ - setUserCovidPositive(telegramID, false, res=>{ - callback(res, res ? false : null); - }); - }else{ - callback(true, user.isInfected); - } - }else{ - callback(false, null); - } - }) - .catch(()=>{ - callback(false, null); - }) -} - -function setUserCovidPositive(telegramID, infectionState, callback){ - User.findOne({ - where: {telegram: telegramID}, - }) - .then(user=>{ - if(user){ - user.isInfected = infectionState; - user.infectionDate = Date.now(); - user.save().then(()=>callback(true)).catch(()=>callback(false)); - }else{ - callback(false); - } - }) - .catch(()=>callback(false)); -} - -exports.CovidRoute = CovidRoute; \ No newline at end of file diff --git a/src/routes/CovidRoute.ts b/src/routes/CovidRoute.ts new file mode 100644 index 0000000..82c7026 --- /dev/null +++ b/src/routes/CovidRoute.ts @@ -0,0 +1,26 @@ +import { Request, Response } from "express"; +import { getUserCovidPositivity, setUserCovidPositivity } from "../db/models/User.helper"; + +interface CovidRouteRequest extends Request { + body:{ + setPositive: boolean; + } +} + +export function CovidRoute(req: CovidRouteRequest, res:Response){ + if(!req.session.userTelegramID){ + res.status(401).send("Not logged in"); + return; + } + + if(req.body.setPositive){ + setUserCovidPositivity(req.session.userTelegramID, true, success=>{ + res.send({covidPositive: true}); + }); + }else{ + getUserCovidPositivity(req.session.userTelegramID, isInfected=>{ + res.status(isInfected ? 200 : 400).send({covidPositive: isInfected}); + }); + } +} + From 4aec65d3f103e7a156a75f20e92c62b06ecb779a Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 18:12:39 +0200 Subject: [PATCH 13/18] quickfix default val --- src/db/models/User.ts | 1 + src/routes/CovidRoute.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/models/User.ts b/src/db/models/User.ts index c3ad9e6..36900b5 100644 --- a/src/db/models/User.ts +++ b/src/db/models/User.ts @@ -32,6 +32,7 @@ export const User = sequelize.define("User", { }, isInfected: { type: DataTypes.BOOLEAN, + defaultValue: false, }, infectionDate: { type: DataTypes.DATE, diff --git a/src/routes/CovidRoute.ts b/src/routes/CovidRoute.ts index 82c7026..e2470e0 100644 --- a/src/routes/CovidRoute.ts +++ b/src/routes/CovidRoute.ts @@ -19,7 +19,7 @@ export function CovidRoute(req: CovidRouteRequest, res:Response){ }); }else{ getUserCovidPositivity(req.session.userTelegramID, isInfected=>{ - res.status(isInfected ? 200 : 400).send({covidPositive: isInfected}); + res.send({covidPositive: isInfected}); }); } } From f25dcfa8587e51f3098dd75f095347393a411119 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 19:28:16 +0200 Subject: [PATCH 14/18] implemented promises and removed callbacks --- src/db/models/User.helper.ts | 98 ++++++++++++------------------ src/db/utils.ts | 66 ++++++-------------- src/routes/CodeRoute.ts | 55 ++++++++--------- src/routes/CovidRoute.ts | 45 ++++++++------ src/routes/LoginRoute.ts | 54 ++++++++-------- src/routes/TelegramWebhookRoute.ts | 87 ++++++++++---------------- src/routes/VerifyRoute.ts | 50 +++++++-------- src/telegram.ts | 44 ++++---------- 8 files changed, 205 insertions(+), 294 deletions(-) diff --git a/src/db/models/User.helper.ts b/src/db/models/User.helper.ts index 342522d..6e4625b 100644 --- a/src/db/models/User.helper.ts +++ b/src/db/models/User.helper.ts @@ -1,80 +1,60 @@ import { TelegramID, UserRowID, VerificationString } from "../../types"; import { User, UserInstance } from "./User"; -export function getUserByTelegramID( - telegramID: TelegramID, - callback: (user?: UserInstance, message?: string) => void -): void { - User.findOne({ +export async function getUserByTelegramID( + telegramID: TelegramID +): Promise { + const user = await User.findOne({ where: { telegram: telegramID, }, - }) - .then((result) => { - callback(!!result ? result : undefined); - }) - .catch(() => { - callback(undefined); - }); + }); + return user; } -export function getUserByRowID( - rowID: UserRowID, - callback: (user?: UserInstance, message?: string) => void -): void { - User.findOne({ +export async function getUserByRowID( + rowID: UserRowID +): Promise { + const user = await User.findOne({ where: { id: rowID, }, - }) - .then((result) => { - callback(!!result ? result : undefined); - }) - .catch(() => { - callback(undefined); - }); + }); + return user; } -export function getUserByVerification( - verification: VerificationString, - callback: (user?: UserInstance, message?: string) => void -): void { - User.findOne({ +export async function getUserByVerification( + verification: VerificationString +): Promise { + const user = await User.findOne({ where: { verification: verification, }, - }) - .then((result) => { - callback(!!result ? result : undefined); - }) - .catch(() => { - callback(undefined); - }); + }); + return user; } -export function getUserCovidPositivity(telegramID: TelegramID, callback: (isInfected?: boolean) => void): void { - getUserByTelegramID(telegramID, user => { - if (!!user) { - const infectionDuration = +user.infectionDate - Date.now(); - if (infectionDuration > 60 * 60 * 24 * 14) { - setUserCovidPositivity(telegramID, false, success => { - callback(success ? false : undefined); - }); - } else { - callback(user.isInfected); - } - } else { - callback(); - } - }); +export async function getUserCovidPositivity( + telegramID: TelegramID +): Promise { + const user = await getUserByTelegramID(telegramID); + if (!user) throw new Error("User not found"); + const infectionDuration = +user.infectionDate - Date.now(); + if (infectionDuration > 60 * 60 * 24 * 14) { + await setUserCovidPositivity(telegramID, false); + return false; + } else { + return user.isInfected; + } } -export function setUserCovidPositivity(telegramID: TelegramID, infectionState: boolean, callback: (success: boolean) => void): void { - getUserByTelegramID(telegramID, user => { - if (!!user) { - user.isInfected = infectionState; - user.infectionDate = new Date(); - user.save().then(() => callback(true)).catch(() => callback(false)); - } else { callback(false) } - }); +export async function setUserCovidPositivity( + telegramID: TelegramID, + infectionState: boolean +): Promise { + const user = await getUserByTelegramID(telegramID); + if (!user) throw new Error("User not found"); + user.isInfected = infectionState; + user.infectionDate = new Date(); + if (!(await user.save())) throw new Error("Could not save user state"); } diff --git a/src/db/utils.ts b/src/db/utils.ts index 53f92f0..3379e82 100644 --- a/src/db/utils.ts +++ b/src/db/utils.ts @@ -1,56 +1,30 @@ import { strings_en } from "../strings"; import { sendTelegramMessage } from "../telegram"; -import { TelegramID, UserRowID } from "../types"; +import { TelegramID } from "../types"; import { Contact } from "./models/Contact"; -import { User } from "./models/User"; -import { getUserByRowID, getUserByTelegramID } from "./models/User.helper"; +import { User, UserInstance } from "./models/User"; +import { getUserByTelegramID } from "./models/User.helper"; -export function addContact( +export async function addContact( userATelegram: TelegramID, - userBTelegram: TelegramID, - callback: (success: boolean, message?: string) => void -): void { - getUserByTelegramID(userATelegram, (userA) => { - getUserByTelegramID(userBTelegram, (userB) => { - if (!userA || !userB) { - callback(false, "Could not find user."); - return; - } + userBTelegram: TelegramID +): Promise { + const userA = await getUserByTelegramID(userATelegram); + const userB = await getUserByTelegramID(userBTelegram); - Contact.create({ user: userA.id, with: userB.id }) - .then(() => { - console.log( - `Registering contact between ${userA.id} and ${userB.id}` - ); - sendTelegramMessage(userB.telegram, strings_en.telegram_qr_scanned); - callback(true, "Successfully added contact"); - }) - .catch((e) => { - callback(false, e); - }); - }); - }); + if (!userA || !userB) { + throw new Error("Could not found users"); + } + + await Contact.create({ user: userA.id, with: userB.id }); + await sendTelegramMessage(userB.telegram, strings_en.telegram_qr_scanned); } -export function createUser( - telegram: TelegramID, - callback: (success: boolean, message: string) => void -): void { - User.create({ +export async function createUser( + telegram: TelegramID +): Promise { + const user = await User.create({ telegram: telegram, - }) - .then((user) => { - if (!user) { - callback(false, "Could not create user"); - } else { - callback(true, "Success"); - } - }) - .catch((reason) => { - if (reason.name == "SequelizeUniqueConstraintError") { - callback(false, "User already exists"); - } else { - callback(false, "Unknown error"); - } - }); + }); + return user; } diff --git a/src/routes/CodeRoute.ts b/src/routes/CodeRoute.ts index acbf941..c06babb 100644 --- a/src/routes/CodeRoute.ts +++ b/src/routes/CodeRoute.ts @@ -5,41 +5,37 @@ import { TelegramID, VerificationString } from "../types"; import { User, UserInstance } from "../db/models/User"; import { getUserByTelegramID } from "../db/models/User.helper"; -export function CodeRoute(req: Request, res: Response) { +export async function CodeRoute(req: Request, res: Response) { if (!req.session.userTelegramID) { res.status(401).send("Not logged in"); return; } - createQRCode(req.session.userTelegramID, (err, url) => { - res.status(url ? 200 : 401).send({ error: err, data: url }); - }); + try { + const url = await createQRCode(req.session.userTelegramID); + res.send({ data: url }); + } catch (error) { + res + .status(500) + .send({ error: error instanceof Error ? error.message : "Error" }); + } } -function createQRCode( - telegram: TelegramID, - callback: (errorMessage: string | Error, url?: string) => void -): void { - getUserByTelegramID(telegram, (user) => { - !!user && - refreshVerification(user, (result) => { - const verifyURL = `${ - process.env.WEBSITE_URL - }/#/verify/${encodeURIComponent(result.verification)}`; - QRCode.toDataURL( - verifyURL, - { width: 300, height: 300 } as QRCodeToDataURLOptions, - (error, url) => { - callback(error, url); - } - ); - }); - }); +async function createQRCode(telegram: TelegramID): Promise { + const user = await getUserByTelegramID(telegram); + if (!user) throw new Error("User not found"); + const newVerification = await refreshVerification(user); + const verifyURL = `${process.env.WEBSITE_URL}/#/verify/${encodeURIComponent( + newVerification + )}`; + return await QRCode.toDataURL(verifyURL, { + width: 300, + height: 300, + } as QRCodeToDataURLOptions); } -function refreshVerification( - user: UserInstance, - callback: (success: UserInstance) => void -): void { +async function refreshVerification( + user: UserInstance +): Promise { let newVerification = bcrypt .hashSync(`${new Date().getTime()}-${user.telegram}`, 5) .replace(/[^a-zA-Z0-9]+/g, "") as VerificationString; @@ -48,7 +44,6 @@ function refreshVerification( newVerification.length / 2 ) as VerificationString; user.verification = newVerification; - user.save().then((result) => { - callback(result); - }); + await user.save(); + return newVerification; } diff --git a/src/routes/CovidRoute.ts b/src/routes/CovidRoute.ts index e2470e0..d2c9584 100644 --- a/src/routes/CovidRoute.ts +++ b/src/routes/CovidRoute.ts @@ -1,26 +1,33 @@ import { Request, Response } from "express"; -import { getUserCovidPositivity, setUserCovidPositivity } from "../db/models/User.helper"; +import { + getUserCovidPositivity, + setUserCovidPositivity, +} from "../db/models/User.helper"; interface CovidRouteRequest extends Request { - body:{ - setPositive: boolean; - } + body: { + setPositive: boolean; + }; } -export function CovidRoute(req: CovidRouteRequest, res:Response){ - if(!req.session.userTelegramID){ - res.status(401).send("Not logged in"); - return; - } - - if(req.body.setPositive){ - setUserCovidPositivity(req.session.userTelegramID, true, success=>{ - res.send({covidPositive: true}); - }); - }else{ - getUserCovidPositivity(req.session.userTelegramID, isInfected=>{ - res.send({covidPositive: isInfected}); - }); +export async function CovidRoute(req: CovidRouteRequest, res: Response) { + if (!req.session.userTelegramID) { + res.status(401).send("Not logged in"); + return; + } + try { + if (req.body.setPositive) { + await setUserCovidPositivity(req.session.userTelegramID, true); + res.send({ covidPositive: true }); + } else { + const isInfected = await getUserCovidPositivity( + req.session.userTelegramID + ); + res.send({ covidPositive: isInfected }); } + } catch (error) { + res + .send(500) + .send({ error: error instanceof Error ? error.message : "Error" }); + } } - diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 18f64ff..c940910 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -16,40 +16,44 @@ interface LoginRequest extends Request { }; } -export function LoginRoute(req: LoginRequest, res: Response) { +export async function LoginRoute(req: LoginRequest, res: Response) { const telegramResponse = req.body.telegramResponse; - authUser(telegramResponse, (authorized) => { + try { + const authorized = await authUser(telegramResponse); if (authorized) { // User is already logged in if (req.session.userTelegramID == telegramResponse.id) { - res.send({authorized: authorized}); + res.send({ authorized: authorized }); return; } + // User not logged in const verified = req.session.isVerified; const verifiedBy = req.session.verifiedByTelegramID; - req.session.regenerate(() => { + req.session.regenerate(async () => { req.session.userTelegramID = telegramResponse.id; if (verified) { - addContact(telegramResponse.id, verifiedBy, (success) => { - res.send({ - authorized: authorized, - contactSuccess: success, - }); + await addContact(telegramResponse.id, verifiedBy); + res.send({ + authorized: true, + contactSuccess: true, }); } else { - res.send({authorized: authorized}); + res.send({ authorized: authorized }); } }); } else { - res.status(401).send(authorized); + res.status(401).send({ error: "Unauthorized" }); } - }); + } catch (error) { + res + .status(500) + .send({ error: error instanceof Error ? error.message : "Error" }); + } } -function authUser( - telegramResponse: TelegramLoginResponse, - callback: (authorized: boolean, message?: string) => void -): void { +async function authUser( + telegramResponse: TelegramLoginResponse +): Promise { let dataCheckArray = []; for (const [key, value] of Object.entries(telegramResponse)) { @@ -71,17 +75,13 @@ function authUser( const authorized = confirmationHash == telegramResponse.hash; if (!authorized) { - callback(false); - return; + return false; } - getUserByTelegramID(telegramResponse.id, (user) => { - if (!!user) { - callback(true); - } else { - createUser(telegramResponse.id, (success, message) => { - callback(success, message); - }); - } - }); + const user = await getUserByTelegramID(telegramResponse.id); + if (!!user) { + return true; + } else { + return !!(await createUser(telegramResponse.id)); + } } diff --git a/src/routes/TelegramWebhookRoute.ts b/src/routes/TelegramWebhookRoute.ts index 353af6a..3c1c3be 100644 --- a/src/routes/TelegramWebhookRoute.ts +++ b/src/routes/TelegramWebhookRoute.ts @@ -19,13 +19,13 @@ interface TelegramWebhookRequest extends Request { }; } -export function TelegramWebhookRoute( +export async function TelegramWebhookRoute( req: TelegramWebhookRequest, res: Response ) { try { if (req.body.message.connected_website) { - sendTelegramMessage( + await sendTelegramMessage( req.body.message.from.id, "Thanks for using OurSejahtera! Let's stay safer together <3" ); @@ -33,67 +33,46 @@ export function TelegramWebhookRoute( const messageText = req.body.message.text; const telegramID = req.body.message.from.id; if (messageText.toLowerCase() == "/covidpositive") { - userInfected(telegramID, (success) => { - if (success) { - sendTelegramMessage( - telegramID, - strings_en.telegram_inform_positive - ); - informContacts(telegramID); - } else { - sendTelegramMessage(telegramID, "Sorry, something went wrong."); - } - }); + await userInfected(telegramID); + await sendTelegramMessage( + telegramID, + strings_en.telegram_inform_positive + ); + await informContacts(telegramID); } } } catch (e) { - console.log("Could not get Telegram Message"); + console.log( + e instanceof Error ? e.message : "Could not get Telegram Message" + ); } res.send(); } -function informContacts(telegramID: TelegramID) { - getUserByTelegramID(telegramID, (user) => { - if (user) { - Contact.findAll({ - where: { - [Op.or]: [{ user: user.id }, { with: user.id }], - }, - }).then((result) => { - result.forEach((contact) => { - const otherPersonID = - contact.user == user.id ? contact.with : contact.user; - getUserByRowID(otherPersonID, (otherUser) => { - otherUser && - sendTelegramMessage( - otherUser.telegram, - strings_en.telegram_inform_infect - ); - }); - }); - }); - } +async function informContacts(telegramID: TelegramID): Promise { + const user = await getUserByTelegramID(telegramID); + if (!user) throw new Error("User not found"); + const contacts = await Contact.findAll({ + where: { + [Op.or]: [{ user: user.id }, { with: user.id }], + }, }); -} -function userInfected( - telegramID: TelegramID, - callback: (success: boolean) => void -): void { - getUserByTelegramID(telegramID, (user) => { - if (!!user) { - user.isInfected = true; - user - .save() - .then((result) => { - callback(!!result); - }) - .catch(() => { - callback(false); - }); - } else { - callback(false); - } + contacts.forEach(async (contact) => { + const otherPersonID = contact.user == user.id ? contact.with : contact.user; + const otherUser = await getUserByRowID(otherPersonID); + if (!otherUser) throw new Error("Other user does not exist"); + await sendTelegramMessage( + otherUser.telegram, + strings_en.telegram_inform_infect + ); }); } + +async function userInfected(telegramID: TelegramID): Promise { + const user = await getUserByTelegramID(telegramID); + if (!user) throw new Error("User not found"); + user.isInfected = true; + await user.save(); +} diff --git a/src/routes/VerifyRoute.ts b/src/routes/VerifyRoute.ts index 0c05992..0197ab1 100644 --- a/src/routes/VerifyRoute.ts +++ b/src/routes/VerifyRoute.ts @@ -10,35 +10,29 @@ interface VerifyRequest extends Request { }; } -export function VerifyRoute(req: VerifyRequest, res: Response) { - getUserByVerification( - decodeURIComponent(req.body.id) as VerificationString, - (verifiedByUser, message) => { - if (!!verifiedByUser) { - req.session.isVerified = !!verifiedByUser; - req.session.verifiedByTelegramID = verifiedByUser.telegram; - if (req.session.userTelegramID) { - // If Logged In - addContact( - req.session.userTelegramID, - verifiedByUser.telegram, - (success, message) => { - res - .status(success ? 200 : 400) - .send({ success: success, message: message, loggedIn: true }); - } - ); - } else { - // If Not Logged In - res.send({ - success: !!verifiedByUser, - message: message, - loggedIn: false, - }); - } +export async function VerifyRoute(req: VerifyRequest, res: Response) { + const verifiedByUser = await getUserByVerification( + decodeURIComponent(req.body.id) as VerificationString + ); + try{ + if (!!verifiedByUser) { + req.session.isVerified = !!verifiedByUser; + req.session.verifiedByTelegramID = verifiedByUser.telegram; + if (req.session.userTelegramID) { + // If Logged In + await addContact(req.session.userTelegramID, verifiedByUser.telegram); + res.send({ success: true, loggedIn: true }); } else { - res.status(400).send({ success: !!verifiedByUser, message: message }); + // If Not Logged In + res.send({ + success: false, + loggedIn: false, + }); } + } else { + res.status(400).send({ success: false }); } - ); + }catch(e){ + res.status(500).send({error: e instanceof Error ? e.message : "Error"}); + } } diff --git a/src/telegram.ts b/src/telegram.ts index 7d631ab..0c2515e 100644 --- a/src/telegram.ts +++ b/src/telegram.ts @@ -1,42 +1,24 @@ import axios from "axios"; import { TelegramID } from "./types"; -export function setTelegramWebHook( - callback: (success: boolean) => void = () => {} -): void { +export async function setTelegramWebHook(): Promise { const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/setWebhook`; - axios - .post(url, { - url: `${process.env.SERVER_API_URL}/${process.env.TELEGRAM_SECRET}`, - allowed_updates: [], - drop_pending_updates: true, - }) - .then((res) => { - callback(!!res); - }) - .catch((err) => { - callback(!!err); - }); + await axios.post(url, { + url: `${process.env.SERVER_API_URL}/${process.env.TELEGRAM_SECRET}`, + allowed_updates: [], + drop_pending_updates: true, + }); } -export function sendTelegramMessage( +export async function sendTelegramMessage( telegramID: TelegramID, - message: string, - callback: (success: boolean) => void = () => {} -): void { + message: string +): Promise { const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/sendMessage`; - axios - .post(url, { - chat_id: telegramID, - text: message, - }) - .then((res) => { - callback(!!res); - }) - .catch((err) => { - console.error("Problem sending Telegram message."); - callback(!!err); - }); + const response = await axios.post(url, { + chat_id: telegramID, + text: message, + }); } setTelegramWebHook(); From ebf1128930556f7799fd82289291b8868196bb52 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 19:36:55 +0200 Subject: [PATCH 15/18] added error messaging --- src/telegram.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/telegram.ts b/src/telegram.ts index 0c2515e..9cca20a 100644 --- a/src/telegram.ts +++ b/src/telegram.ts @@ -21,4 +21,10 @@ export async function sendTelegramMessage( }); } -setTelegramWebHook(); + +setTelegramWebHook() +.catch(error=>{ + console.error("Error setting Telegram Webhook"); + error instanceof Error && console.error(error.message); +}); + From 0ff3f46e8eb640d570370b740fad9d538cfcb681 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 21:05:35 +0200 Subject: [PATCH 16/18] fix package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 643413d..dcf6d2d 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "src/app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "tsc && NODE_ENV=production node dist/app.js", - "dev": "NODE_ENV=development tsc && node dist/app.js" + "start": "node dist/app.js", + "dev": "(NODE_ENV=development; tsc && node dist/app.js)" }, "author": "", "license": "ISC", From a7ae495e37223ee60e8ad7e297a36bb91d8ce793 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 21:14:51 +0200 Subject: [PATCH 17/18] date issue fix --- src/db/models/User.helper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/models/User.helper.ts b/src/db/models/User.helper.ts index 342522d..e90bcdd 100644 --- a/src/db/models/User.helper.ts +++ b/src/db/models/User.helper.ts @@ -55,8 +55,8 @@ export function getUserByVerification( export function getUserCovidPositivity(telegramID: TelegramID, callback: (isInfected?: boolean) => void): void { getUserByTelegramID(telegramID, user => { if (!!user) { - const infectionDuration = +user.infectionDate - Date.now(); - if (infectionDuration > 60 * 60 * 24 * 14) { + const infectionDuration = Date.now() - +user.infectionDate; + if (infectionDuration > 60 * 60 * 24 * 14 * 1000) { setUserCovidPositivity(telegramID, false, success => { callback(success ? false : undefined); }); From bc4745d3714642f284d6be957a52f05c4d446082 Mon Sep 17 00:00:00 2001 From: Nareshkumar Rao Date: Thu, 5 Aug 2021 21:44:51 +0200 Subject: [PATCH 18/18] hotfix --- src/db/models/User.helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/models/User.helper.ts b/src/db/models/User.helper.ts index baf7909..7e6bb8c 100644 --- a/src/db/models/User.helper.ts +++ b/src/db/models/User.helper.ts @@ -39,7 +39,7 @@ export async function getUserCovidPositivity( ): Promise { const user = await getUserByTelegramID(telegramID); if (!user) throw new Error("User not found"); - const infectionDuration = Date.now() - +user.infectionDate; + const infectionDuration = new Date().getTime() - user.infectionDate.getTime(); if (infectionDuration > 60 * 60 * 24 * 14 * 1000) { await setUserCovidPositivity(telegramID, false); return false;