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/.gitignore b/.gitignore index be303de..b6347cf 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,5 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +dist/**/* \ No newline at end of file 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/package-lock.json b/package-lock.json index 4ffc3c8..99d31f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,13 @@ "sqlite3": "^5.0.2" }, "devDependencies": { + "@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", "eslint": "^7.31.0", "prettier": "2.3.2" } @@ -218,11 +225,126 @@ "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/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/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", + "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", @@ -4600,11 +4722,126 @@ "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/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/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", + "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 72dbf92..643413d 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", @@ -23,6 +24,13 @@ "sqlite3": "^5.0.2" }, "devDependencies": { + "@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", "eslint": "^7.31.0", "prettier": "2.3.2" } diff --git a/src/app.js b/src/app.ts similarity index 50% rename from src/app.js rename to src/app.ts index ff5be0f..f0d4dc0 100644 --- a/src/app.js +++ b/src/app.ts @@ -1,14 +1,15 @@ -const express = require("express"); -const session = require("express-session"); -const cors = require("cors"); -require("dotenv-flow").config(); +import * as dotenvFlow from "dotenv-flow"; +dotenvFlow.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"); -const { CovidRoute } = require("./routes/CovidRoute"); +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"; +import { CovidRoute } from "./routes/CovidRoute"; console.log(`Node Environment: ${process.env.NODE_ENV}`); diff --git a/src/db/db.js b/src/db/db.js deleted file mode 100644 index 4a6048e..0000000 --- a/src/db/db.js +++ /dev/null @@ -1,89 +0,0 @@ -const session = require("express-session"); -const { Sequelize, DataTypes } = require("sequelize"); -var SequelizeStore = require("connect-session-sequelize")(session.Store); - -const isProduction = process.env.NODE_ENV == "production"; - -const sequelize = (() => { - if (isProduction) { - return new Sequelize( - process.env.DB_DATA_NAME, - process.env.DB_USER, - process.env.DB_PASS, - { - host: process.env.DB_PATH, - dialect: process.env.DB_DATA_DIALECT, - } - ); - } else { - return new Sequelize("sqlite::memory:"); - } -})(); - -const storeDB = (() => { - if (isProduction) { - return new Sequelize( - process.env.DB_STORE_NAME, - process.env.DB_USER, - process.env.DB_PASS, - { - host: process.env.DB_PATH, - dialect: process.env.DB_DATA_DIALECT, - } - ); - } else { - return new Sequelize("sqlite::memory:"); - } -})(); - -const store = new SequelizeStore({ - db: storeDB, -}); - -const Contact = sequelize.define("Contact", { - user: { - type: DataTypes.INTEGER, - allowNull: false, - }, - with: { - type: DataTypes.INTEGER, - allowNull: false, - }, -}); - -const User = sequelize.define("User", { - telegram: { - type: DataTypes.INTEGER, - allowNull: false, - unique: true, - }, - verification: { - type: DataTypes.STRING, - }, - isInfected: { - type: DataTypes.BOOLEAN, - }, - infectionDate: { - type: DataTypes.DATE, - } -}); - -Contact.sync(); - -User.sync().then(() => { - if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { - User.create({ - telegram: process.env.ADMIN_USERNAME, - }).catch(() => { - console.log("Couldn't create admin account. Probably exists."); - }); - } -}); - -store.sync(); - -exports.User = User; -exports.Contact = Contact; -exports.sequelize = sequelize; -exports.storeDB = storeDB; -exports.store = store; diff --git a/src/db/db.ts b/src/db/db.ts new file mode 100644 index 0000000..180856b --- /dev/null +++ b/src/db/db.ts @@ -0,0 +1,44 @@ +import ConnectSessionSequelize from "connect-session-sequelize"; +import session from "express-session"; +import { Sequelize } from "sequelize"; + +const SequelizeStore = ConnectSessionSequelize(session.Store); + +const isProduction: boolean = process.env.NODE_ENV == "production"; + +export const sequelize: Sequelize = (() => { + if (isProduction) { + return new 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("sqlite::memory:"); + } +})(); + +export const storeDB: Sequelize = (() => { + if (isProduction) { + return new 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("sqlite::memory:"); + } +})(); + +export const store = new SequelizeStore({ + db: storeDB, +}); +store.sync(); 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/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.helper.ts b/src/db/models/User.helper.ts new file mode 100644 index 0000000..342522d --- /dev/null +++ b/src/db/models/User.helper.ts @@ -0,0 +1,80 @@ +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); + }); +} + +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/db/models/User.ts b/src/db/models/User.ts new file mode 100644 index 0000000..36900b5 --- /dev/null +++ b/src/db/models/User.ts @@ -0,0 +1,50 @@ +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; + infectionDate: Date; +} +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, + defaultValue: false, + }, + infectionDate: { + type: DataTypes.DATE, + }, +}); + +User.sync().then(() => { + if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { + User.create({ + telegram: 12345 as TelegramID, + }).catch(() => { + console.log("Couldn't create admin account. Probably exists."); + }); + } +}); diff --git a/src/db/utils.js b/src/db/utils.js deleted file mode 100644 index b396bdc..0000000 --- a/src/db/utils.js +++ /dev/null @@ -1,48 +0,0 @@ -const { strings_en } = require("../strings"); -const { sendTelegramMessage } = require("../telegram"); -const { User, Contact } = require("./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 }) - .then(() => { - console.log( - `Registering contact between ${user.id} and ${withUserID}` - ); - sendTelegramMessage( - withUser.telegram, - strings_en.telegram_qr_scanned, - () => {} - ); - done(true, "Successfully added contact"); - }) - .catch((e) => { - done(false, e); - }); - }); - }); -} - -function createUser(telegram, done) { - User.create({ - telegram: telegram, - }) - .then((user) => { - if (!user) { - done(false, "Could not create user"); - } else { - done(true, "Success"); - } - }) - .catch((reason) => { - if (reason.name == "SequelizeUniqueConstraintError") { - done(false, "User already exists"); - } else { - done(false, "Unknown error"); - } - }); -} - -exports.addContact = addContact; -exports.createUser = createUser; diff --git a/src/db/utils.ts b/src/db/utils.ts new file mode 100644 index 0000000..53f92f0 --- /dev/null +++ b/src/db/utils.ts @@ -0,0 +1,56 @@ +import { strings_en } from "../strings"; +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, + 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; + } + + 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); + }); + }); + }); +} + +export function createUser( + telegram: TelegramID, + callback: (success: boolean, message: string) => void +): void { + 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"); + } + }); +} diff --git a/src/routes/CodeRoute.js b/src/routes/CodeRoute.js deleted file mode 100644 index 78372e9..0000000 --- a/src/routes/CodeRoute.js +++ /dev/null @@ -1,47 +0,0 @@ -const bcrypt = require("bcrypt"); -const QRCode = require("qrcode"); -const { User } = require("../db/db"); - -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 }); - }); -} - -function createQRCode(telegram, done) { - 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); - }); - }); - }) - .catch((err) => { - done(err); - }); -} - -function refreshVerification(user, done) { - let newVerification = bcrypt - .hashSync(`${new Date().getTime()}-${user.hash}`, 5) - .replace(/[^a-zA-Z0-9]+/g, ""); - newVerification = newVerification.substr(0, newVerification.length / 2); - user.verification = newVerification; - user.save().then((result) => { - done(result); - }); -} - -exports.CodeRoute = CodeRoute; diff --git a/src/routes/CodeRoute.ts b/src/routes/CodeRoute.ts new file mode 100644 index 0000000..acbf941 --- /dev/null +++ b/src/routes/CodeRoute.ts @@ -0,0 +1,54 @@ +import { Request, Response } from "express"; +import bcrypt from "bcrypt"; +import QRCode, { QRCodeToDataURLOptions } from "qrcode"; +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.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 }); + }); +} + +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); + } + ); + }); + }); +} + +function refreshVerification( + user: UserInstance, + callback: (success: UserInstance) => void +): void { + let newVerification = bcrypt + .hashSync(`${new Date().getTime()}-${user.telegram}`, 5) + .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/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..e2470e0 --- /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.send({covidPositive: isInfected}); + }); + } +} + diff --git a/src/routes/LoginRoute.js b/src/routes/LoginRoute.js deleted file mode 100644 index 89d0292..0000000 --- a/src/routes/LoginRoute.js +++ /dev/null @@ -1,78 +0,0 @@ -const crypto = require("crypto"); -const { User } = require("../db/db"); -const { addContact, createUser } = require("../db/utils"); - -function LoginRoute(req, res) { - const telegramResponse = req.body.telegramResponse; - - authUser(telegramResponse, (success, msg) => { - if (success) { - // User is already logged in - if (req.session.user == telegramResponse.id) { - res.send({ authorized: success }); - return; - } - - const verified = req.session.verified; - const verifiedBy = req.session.verifiedBy; - req.session.regenerate(() => { - req.session.user = telegramResponse.id; - if (verified) { - addContact(telegramResponse.id, verifiedBy, (contactSuccess) => { - res.send({ - authorized: success, - message: msg, - contactSuccess: contactSuccess, - }); - }); - } else { - res.send({ authorized: success, message: msg }); - } - }); - } else { - res.status(401).send({ authorized: success, message: msg }); - } - }); -} - -function authUser(telegramResponse, done) { - 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 - .createHash("sha256") - .update(process.env.TELEGRAM_TOKEN) - .digest(); - const confirmationHash = crypto - .createHmac("sha256", secretKey) - .update(dataCheckString) - .digest("hex"); - - const authorized = confirmationHash == telegramResponse.hash; - - if (!authorized) { - done({ authorized: false }); - } - - User.findOne({ - where: { - telegram: telegramResponse.id, - }, - }).then((user) => { - if (!user) { - createUser(telegramResponse.id, (success) => { - done({ authorized: success }); - }); - } else { - done({ authorized: true }); - } - }); -} - -exports.LoginRoute = LoginRoute; diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts new file mode 100644 index 0000000..18f64ff --- /dev/null +++ b/src/routes/LoginRoute.ts @@ -0,0 +1,87 @@ +import { Request, Response } from "express"; +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; + hash: string; +}; + +interface LoginRequest extends Request { + body: { + telegramResponse: TelegramLoginResponse; + }; +} + +export function LoginRoute(req: LoginRequest, res: Response) { + const telegramResponse = req.body.telegramResponse; + authUser(telegramResponse, (authorized) => { + if (authorized) { + // User is already logged in + if (req.session.userTelegramID == telegramResponse.id) { + res.send({authorized: authorized}); + return; + } + const verified = req.session.isVerified; + const verifiedBy = req.session.verifiedByTelegramID; + req.session.regenerate(() => { + req.session.userTelegramID = telegramResponse.id; + if (verified) { + addContact(telegramResponse.id, verifiedBy, (success) => { + res.send({ + authorized: authorized, + contactSuccess: success, + }); + }); + } else { + res.send({authorized: authorized}); + } + }); + } else { + res.status(401).send(authorized); + } + }); +} + +function authUser( + telegramResponse: TelegramLoginResponse, + callback: (authorized: boolean, message?: string) => void +): void { + 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 + .createHash("sha256") + .update(process.env.TELEGRAM_TOKEN!) + .digest(); + const confirmationHash = crypto + .createHmac("sha256", secretKey) + .update(dataCheckString) + .digest("hex"); + + const authorized = confirmationHash == telegramResponse.hash; + + if (!authorized) { + callback(false); + return; + } + + getUserByTelegramID(telegramResponse.id, (user) => { + if (!!user) { + callback(true); + } else { + createUser(telegramResponse.id, (success, message) => { + callback(success, message); + }); + } + }); +} diff --git a/src/routes/TelegramWebhookRoute.js b/src/routes/TelegramWebhookRoute.js deleted file mode 100644 index e0c78d6..0000000 --- a/src/routes/TelegramWebhookRoute.js +++ /dev/null @@ -1,97 +0,0 @@ -const { Op } = require("sequelize"); -const { User, Contact } = require("../db/db"); -const { strings_en } = require("../strings"); -const { sendTelegramMessage } = require("../telegram"); - -function TelegramWebhookRoute(req, res) { - try { - if (req.body.message.connected_website) { - sendTelegramMessage( - req.body.message.from.id, - "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."); - } - }); - } - } - } catch (e) { - console.log("Could not get Telegram Message"); - } - - res.send(); -} - -function informContacts(telegramID, doneCallback = () => {}) { - User.findOne({ - 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, - strings_en.telegram_inform_infect - ); - }); - }); - }); - } - }); -} - -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 }); - }); - } - }) - .catch((err) => { - doneCallback({ saved: false }); - }); -} - -exports.TelegramWebhookRoute = TelegramWebhookRoute; diff --git a/src/routes/TelegramWebhookRoute.ts b/src/routes/TelegramWebhookRoute.ts new file mode 100644 index 0000000..353af6a --- /dev/null +++ b/src/routes/TelegramWebhookRoute.ts @@ -0,0 +1,99 @@ +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"; + +interface TelegramWebhookRequest extends Request { + body: { + message: { + text: string; + from: { + id: TelegramID; + }; + connected_website: string; + }; + }; +} + +export function TelegramWebhookRoute( + req: TelegramWebhookRequest, + res: Response +) { + try { + if (req.body.message.connected_website) { + sendTelegramMessage( + req.body.message.from.id, + "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, (success) => { + if (success) { + 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"); + } + + 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 + ); + }); + }); + }); + } + }); +} + +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); + } + }); +} diff --git a/src/routes/VerifyRoute.js b/src/routes/VerifyRoute.js deleted file mode 100644 index 4f39cfb..0000000 --- a/src/routes/VerifyRoute.js +++ /dev/null @@ -1,41 +0,0 @@ -const { User } = require("../db/db"); -const { addContact } = require("../db/utils"); - -function VerifyRoute(req, res) { - checkVerification(req.body.id, (success, msg, withUserID) => { - req.session.verified = success; - req.session.verifiedBy = withUserID; - - if (success) { - 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 }); - }); - } else { - // If Not Logged In - res.send({ success: success, message: msg, loggedIn: false }); - } - } else { - res.status(400).send({ success: success, message: msg }); - } - }); -} - -function checkVerification(id, done) { - User.findOne({ - where: { - verification: decodeURIComponent(id), - }, - }).then((user) => { - if (user) { - done(true, "User verified", user.id); - } else { - done(false, "No such verification"); - } - }); -} - -exports.VerifyRoute = VerifyRoute; diff --git a/src/routes/VerifyRoute.ts b/src/routes/VerifyRoute.ts new file mode 100644 index 0000000..0c05992 --- /dev/null +++ b/src/routes/VerifyRoute.ts @@ -0,0 +1,44 @@ +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"; + +interface VerifyRequest extends Request { + body: { + id: VerificationString; + }; +} + +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, + }); + } + } else { + res.status(400).send({ success: !!verifiedByUser, message: message }); + } + } + ); +} diff --git a/src/session.js b/src/session.js deleted file mode 100644 index 4812e45..0000000 --- a/src/session.js +++ /dev/null @@ -1,18 +0,0 @@ -const { store } = require("./db/db"); - -const sessionOpts = { - secret: process.env.SERVER_SESSION_SECRET, - resave: false, - saveUninitialized: false, - cookie: { - secure: true, - sameSite: "none", - maxAge: Number(process.env.SESSION_LENGTH), - }, - store: store, -}; - -const corsOpts = { credentials: true, origin: true, secure: true }; - -exports.sessionOpts = sessionOpts; -exports.corsOpts = corsOpts; diff --git a/src/session.ts b/src/session.ts new file mode 100644 index 0000000..dd1739c --- /dev/null +++ b/src/session.ts @@ -0,0 +1,16 @@ +import { SessionOptions } from "express-session"; +import { store } from "./db/db"; + +export const sessionOpts: SessionOptions = { + secret: process.env.SERVER_SESSION_SECRET!, + resave: false, + saveUninitialized: false, + cookie: { + secure: true, + sameSite: "none", + maxAge: Number(process.env.SESSION_LENGTH), + }, + store: store, +}; + +export const corsOpts = { credentials: true, origin: true, secure: true }; diff --git a/src/strings.js b/src/strings.ts similarity index 91% rename from src/strings.js rename to src/strings.ts index 210f529..6c608b7 100644 --- a/src/strings.js +++ b/src/strings.ts @@ -1,4 +1,4 @@ -const strings_en = { +export const strings_en = { telegram_inform_infect: "ATTENTION! Someone you have been \ in contact with has reported being tested POSITIVE with \ @@ -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; diff --git a/src/telegram.js b/src/telegram.ts similarity index 59% rename from src/telegram.js rename to src/telegram.ts index 4fb241f..7d631ab 100644 --- a/src/telegram.js +++ b/src/telegram.ts @@ -1,6 +1,9 @@ -const { default: axios } = require("axios"); +import axios from "axios"; +import { TelegramID } from "./types"; -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/types.ts b/src/types.ts new file mode 100644 index 0000000..a2f39b6 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,14 @@ +/* + * 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 { + isVerified: boolean; + verifiedByTelegramID: TelegramID; + userTelegramID: TelegramID; + } +} diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index ea1a9a1..0000000 --- a/src/utils.js +++ /dev/null @@ -1,5 +0,0 @@ -function getCookieExpiry() { - return new Date(Date.now() + process.env.COOKIE_EXPIRY_DURATION); -} - -exports.getCookieExpiry = getCookieExpiry; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..6f3cbe4 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,3 @@ +export function getCookieExpiry(): Date { + return new Date(Date.now() + Number(process.env.COOKIE_EXPIRY_DURATION)); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ce20dfc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@tsconfig/node14/tsconfig.json", + "include": ["./src/**/*"], + "compilerOptions": { + "outDir": "./dist" + } +}