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" + } +}