Browse Source

extracted functions into helpers, use some nominal types

tsMigration
Nareshkumar Rao 3 years ago
parent
commit
84cc73e2f9
  1. 2
      .gitignore
  2. 3
      package.json
  3. 0
      src/db/models/Contact.helper.ts
  4. 53
      src/db/models/User.helper.ts
  5. 2
      src/db/models/User.ts
  6. 21
      src/db/utils.ts
  7. 26
      src/routes/CodeRoute.ts
  8. 46
      src/routes/LoginRoute.ts
  9. 52
      src/routes/TelegramWebhookRoute.ts
  10. 49
      src/routes/VerifyRoute.ts
  11. 15
      src/types.ts
  12. 2
      tsconfig.json

2
.gitignore

@ -118,4 +118,4 @@ dist
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
built/**/*
dist/**/*

3
package.json

@ -5,7 +5,8 @@
"main": "src/app.js", "main": "src/app.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "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": "", "author": "",
"license": "ISC", "license": "ISC",

0
src/db/models/Contact.helper.ts

53
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);
});
}

2
src/db/models/User.ts

@ -37,7 +37,7 @@ export const User = sequelize.define<UserInstance>("User", {
User.sync().then(() => { User.sync().then(() => {
if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) { if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) {
User.create({ User.create({
telegram: 12345,
telegram: 12345 as TelegramID,
}).catch(() => { }).catch(() => {
console.log("Couldn't create admin account. Probably exists."); console.log("Couldn't create admin account. Probably exists.");
}); });

21
src/db/utils.ts

@ -3,29 +3,30 @@ import { sendTelegramMessage } from "../telegram";
import { TelegramID, UserRowID } from "../types"; import { TelegramID, UserRowID } from "../types";
import { Contact } from "./models/Contact"; import { Contact } from "./models/Contact";
import { User } from "./models/User"; import { User } from "./models/User";
import { getUserByRowID, getUserByTelegramID } from "./models/User.helper";
export function addContact( export function addContact(
userATelegram: TelegramID, userATelegram: TelegramID,
userBRowID: UserRowID,
done: (success: boolean, message: string) => void
userBTelegram: TelegramID,
callback: (success: boolean, message?: string) => void
): 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) { if (!userA || !userB) {
done(false, "Could not find user.");
callback(false, "Could not find user.");
return; return;
} }
Contact.create({ user: userA!.id, with: userBRowID })
Contact.create({ user: userA.id, with: userB.id })
.then(() => { .then(() => {
console.log( 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) => { .catch((e) => {
done(false, e);
callback(false, e);
}); });
}); });
}); });

26
src/routes/CodeRoute.ts

@ -1,15 +1,16 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import bcrypt from "bcrypt"; import bcrypt from "bcrypt";
import QRCode, { QRCodeToDataURLOptions } from "qrcode"; import QRCode, { QRCodeToDataURLOptions } from "qrcode";
import { TelegramID } from "../types";
import { TelegramID, VerificationString } from "../types";
import { User, UserInstance } from "../db/models/User"; import { User, UserInstance } from "../db/models/User";
import { getUserByTelegramID } from "../db/models/User.helper";
export function CodeRoute(req: Request, res: Response) { export function CodeRoute(req: Request, res: Response) {
if (!req.session.user) {
if (!req.session.userTelegramID) {
res.status(401).send("Not logged in"); res.status(401).send("Not logged in");
return; return;
} }
createQRCode(req.session.user, (err, url) => {
createQRCode(req.session.userTelegramID, (err, url) => {
res.status(url ? 200 : 401).send({ error: err, data: url }); res.status(url ? 200 : 401).send({ error: err, data: url });
}); });
} }
@ -18,13 +19,8 @@ function createQRCode(
telegram: TelegramID, telegram: TelegramID,
callback: (errorMessage: string | Error, url?: string) => void callback: (errorMessage: string | Error, url?: string) => void
): void { ): void {
User.findOne({
where: {
telegram: telegram,
},
})
.then((user) => {
user &&
getUserByTelegramID(telegram, (user) => {
!!user &&
refreshVerification(user, (result) => { refreshVerification(user, (result) => {
const verifyURL = `${ const verifyURL = `${
process.env.WEBSITE_URL process.env.WEBSITE_URL
@ -37,9 +33,6 @@ function createQRCode(
} }
); );
}); });
})
.catch((error) => {
callback(error);
}); });
} }
@ -49,8 +42,11 @@ function refreshVerification(
): void { ): void {
let newVerification = bcrypt let newVerification = bcrypt
.hashSync(`${new Date().getTime()}-${user.telegram}`, 5) .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.verification = newVerification;
user.save().then((result) => { user.save().then((result) => {
callback(result); callback(result);

46
src/routes/LoginRoute.ts

@ -3,6 +3,7 @@ import crypto from "crypto";
import { addContact, createUser } from "../db/utils"; import { addContact, createUser } from "../db/utils";
import { TelegramID, UserRowID } from "../types"; import { TelegramID, UserRowID } from "../types";
import { User } from "../db/models/User"; import { User } from "../db/models/User";
import { getUserByTelegramID } from "../db/models/User.helper";
type TelegramLoginResponse = { type TelegramLoginResponse = {
id: TelegramID; id: TelegramID;
@ -17,37 +18,37 @@ interface LoginRequest extends Request {
export function LoginRoute(req: LoginRequest, res: Response) { export function LoginRoute(req: LoginRequest, res: Response) {
const telegramResponse = req.body.telegramResponse; const telegramResponse = req.body.telegramResponse;
authUser(telegramResponse, (authObject) => {
if (authObject) {
authUser(telegramResponse, (authorized) => {
if (authorized) {
// User is already logged in // User is already logged in
if (req.session.user == telegramResponse.id) {
res.send(authObject);
if (req.session.userTelegramID == telegramResponse.id) {
res.send(authorized);
return; 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.regenerate(() => {
req.session.user = telegramResponse.id;
req.session.userTelegramID = telegramResponse.id;
if (verified) { if (verified) {
addContact(telegramResponse.id, verifiedBy, (contactSuccess) => {
addContact(telegramResponse.id, verifiedBy, (success) => {
res.send({ res.send({
authorized: authObject.authorized,
contactSuccess: contactSuccess,
authorized: authorized,
contactSuccess: success,
}); });
}); });
} else { } else {
res.send(authObject);
res.send(authorized);
} }
}); });
} else { } else {
res.status(401).send(authObject);
res.status(401).send(authorized);
} }
}); });
} }
function authUser( function authUser(
telegramResponse: TelegramLoginResponse, telegramResponse: TelegramLoginResponse,
callback: (callbackObject: { authorized: boolean }) => void
callback: (authorized: boolean, message?: string) => void
): void { ): void {
let dataCheckArray = []; let dataCheckArray = [];
@ -70,20 +71,17 @@ function authUser(
const authorized = confirmationHash == telegramResponse.hash; const authorized = confirmationHash == telegramResponse.hash;
if (!authorized) { 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 { } else {
callback({ authorized: true });
createUser(telegramResponse.id, (success, message) => {
callback(success, message);
});
} }
}); });
} }

52
src/routes/TelegramWebhookRoute.ts

@ -2,6 +2,7 @@ import { Request, Response } from "express";
import { Op } from "sequelize"; import { Op } from "sequelize";
import { Contact } from "../db/models/Contact"; import { Contact } from "../db/models/Contact";
import { User } from "../db/models/User"; import { User } from "../db/models/User";
import { getUserByRowID, getUserByTelegramID } from "../db/models/User.helper";
import { strings_en } from "../strings"; import { strings_en } from "../strings";
import { sendTelegramMessage } from "../telegram"; import { sendTelegramMessage } from "../telegram";
import { TelegramID } from "../types"; import { TelegramID } from "../types";
@ -32,8 +33,8 @@ export function TelegramWebhookRoute(
const messageText = req.body.message.text; const messageText = req.body.message.text;
const telegramID = req.body.message.from.id; const telegramID = req.body.message.from.id;
if (messageText.toLowerCase() == "/covidpositive") { if (messageText.toLowerCase() == "/covidpositive") {
userInfected(telegramID, (result) => {
if (result.saved) {
userInfected(telegramID, (success) => {
if (success) {
sendTelegramMessage( sendTelegramMessage(
telegramID, telegramID,
strings_en.telegram_inform_positive strings_en.telegram_inform_positive
@ -53,29 +54,20 @@ export function TelegramWebhookRoute(
} }
function informContacts(telegramID: TelegramID) { function informContacts(telegramID: TelegramID) {
User.findOne({
where: {
telegram: telegramID,
},
}).then((user) => {
getUserByTelegramID(telegramID, (user) => {
if (user) { if (user) {
const userRowID = user.id;
Contact.findAll({ Contact.findAll({
where: { where: {
[Op.or]: [{ user: userRowID }, { with: userRowID }],
[Op.or]: [{ user: user.id }, { with: user.id }],
}, },
}).then((result) => { }).then((result) => {
result.forEach((contact) => { result.forEach((contact) => {
const otherPersonID = 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( sendTelegramMessage(
otherPerson.telegram,
otherUser.telegram,
strings_en.telegram_inform_infect strings_en.telegram_inform_infect
); );
}); });
@ -87,31 +79,21 @@ function informContacts(telegramID: TelegramID) {
function userInfected( function userInfected(
telegramID: TelegramID, telegramID: TelegramID,
callback: (callbackObject: { saved: boolean }) => void
callback: (success: boolean) => void
): void { ): void {
User.findOne({
where: {
telegram: telegramID,
},
})
.then((user) => {
if (!user) {
callback({ saved: false });
} else {
getUserByTelegramID(telegramID, (user) => {
if (!!user) {
user.isInfected = true; user.isInfected = true;
user user
.save() .save()
.then((result) => { .then((result) => {
if (result) {
callback({ saved: true });
}
callback(!!result);
}) })
.catch((err) => {
callback({ saved: false });
.catch(() => {
callback(false);
}); });
} else {
callback(false);
} }
})
.catch((err) => {
callback({ saved: false });
}); });
} }

49
src/routes/VerifyRoute.ts

@ -1,5 +1,6 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import { User } from "../db/models/User"; import { User } from "../db/models/User";
import { getUserByVerification } from "../db/models/User.helper";
import { addContact } from "../db/utils"; import { addContact } from "../db/utils";
import { UserRowID, VerificationString } from "../types"; import { UserRowID, VerificationString } from "../types";
@ -10,40 +11,34 @@ interface VerifyRequest extends Request {
} }
export function VerifyRoute(req: VerifyRequest, res: Response) { 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) {
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 // If Logged In
addContact(req.session.user, withUserID!, (success, msg) => {
addContact(
req.session.userTelegramID,
verifiedByUser.telegram,
(success, message) => {
res res
.status(success ? 200 : 400) .status(success ? 200 : 400)
.send({ success: success, message: msg, loggedIn: true });
});
} else {
// If Not Logged In
res.send({ success: success, message: msg, loggedIn: false });
.send({ success: success, message: message, loggedIn: true });
} }
);
} else { } else {
res.status(400).send({ success: success, message: msg });
}
// If Not Logged In
res.send({
success: !!verifiedByUser,
message: message,
loggedIn: false,
}); });
} }
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 { } else {
callback(false, "No such verification");
res.status(400).send({ success: !!verifiedByUser, message: message });
} }
});
}
);
} }

15
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" { declare module "express-session" {
interface Session { interface Session {
verified: boolean;
verifiedBy: UserRowID;
user: TelegramID;
isVerified: boolean;
verifiedByTelegramID: TelegramID;
userTelegramID: TelegramID;
} }
} }

2
tsconfig.json

@ -2,6 +2,6 @@
"extends": "@tsconfig/node14/tsconfig.json", "extends": "@tsconfig/node14/tsconfig.json",
"include": ["./src/**/*"], "include": ["./src/**/*"],
"compilerOptions": { "compilerOptions": {
"outDir": "./built"
"outDir": "./dist"
} }
} }

Loading…
Cancel
Save