From d889f92643de988aa16e43f5480a6ca244cd41c9 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 25 Aug 2024 18:07:34 +0200 Subject: [PATCH] default values for env and critic check for values --- src/command/refreshCommandHandler.ts | 3 +- src/controller/inviteController.ts | 12 ++++--- src/data-source.ts | 9 ++--- src/env.defaults.ts | 51 ++++++++++++++++++++++++++++ src/helpers/jwtHelper.ts | 7 ++-- src/helpers/mailHelper.ts | 13 +++---- src/helpers/stringHelper.ts | 17 ++++++---- src/index.ts | 7 ++-- 8 files changed, 92 insertions(+), 27 deletions(-) create mode 100644 src/env.defaults.ts diff --git a/src/command/refreshCommandHandler.ts b/src/command/refreshCommandHandler.ts index 174445e..42a5d16 100644 --- a/src/command/refreshCommandHandler.ts +++ b/src/command/refreshCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../data-source"; import { refresh } from "../entity/refresh"; +import { REFRESH_EXPIRATION } from "../env.defaults"; import InternalException from "../exceptions/internalException"; import { JWTHelper } from "../helpers/jwtHelper"; import { StringHelper } from "../helpers/stringHelper"; @@ -28,7 +29,7 @@ export default abstract class RefreshCommandHandler { .values({ token: refreshToken, user: await UserService.getById(createRefresh.userId), - expiry: new Date(Date.now() + ms(process.env.REFRESH_EXPIRATION)), + expiry: new Date(Date.now() + ms(REFRESH_EXPIRATION)), }) .execute() .then((result) => { diff --git a/src/controller/inviteController.ts b/src/controller/inviteController.ts index 9613cbf..6fd709c 100644 --- a/src/controller/inviteController.ts +++ b/src/controller/inviteController.ts @@ -15,6 +15,7 @@ import MailHelper from "../helpers/mailHelper"; import InviteService from "../service/inviteService"; import UserService from "../service/userService"; import CustomRequestException from "../exceptions/customRequestException"; +import { CLUB_NAME } from "../env.defaults"; /** * @description start first user @@ -43,7 +44,7 @@ export async function inviteUser(req: Request, res: Response): Promise { throw new CustomRequestException(409, "Username and Mail are already in use"); } - var secret = speakeasy.generateSecret({ length: 20, name: `Mitgliederverwaltung ${process.env.CLUB_NAME}` }); + var secret = speakeasy.generateSecret({ length: 20, name: `Mitgliederverwaltung ${CLUB_NAME}` }); let createInvite: CreateInviteCommand = { username: username, @@ -58,7 +59,7 @@ export async function inviteUser(req: Request, res: Response): Promise { let mailhelper = new MailHelper(); await mailhelper.sendMail( mail, - `Email Bestätigung für Mitglieder Admin-Portal von ${process.env.CLUB_NAME}`, + `Email Bestätigung für Mitglieder Admin-Portal von ${CLUB_NAME}`, `Öffne folgenden Link: ${origin}/setup/verify?mail=${mail}&token=${token}` ); @@ -77,11 +78,14 @@ export async function verifyInvite(req: Request, res: Response): Promise { let { secret } = await InviteService.getByMailAndToken(mail, token); - const url = `otpauth://totp/Mitgliederverwaltung ${process.env.CLUB_NAME}?secret=${secret}`; + const url = `otpauth://totp/Mitgliederverwaltung ${CLUB_NAME}?secret=${secret}`; QRCode.toDataURL(url) .then((result) => { - res.send(result); + res.json({ + dataUrl: result, + otp: secret, + }); }) .catch((err) => { throw new InternalException("QRCode not created"); diff --git a/src/data-source.ts b/src/data-source.ts index aa9c708..1fea665 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -9,14 +9,15 @@ import { invite } from "./entity/invite"; import { Initial1724317398939 } from "./migrations/1724317398939-initial"; import { RefreshPrimaryChange1724573307851 } from "./migrations/1724573307851-refreshPrimaryChange"; import { Invite1724579024939 } from "./migrations/1724579024939-invite"; +import { DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME } from "./env.defaults"; const dataSource = new DataSource({ type: "mysql", - host: process.env.NODE_ENV || process.env.DBMODE ? "localhost" : process.env.DB_HOST, + host: process.env.NODE_ENV || process.env.DBMODE ? "localhost" : DB_HOST, port: 3306, - username: process.env.DB_USERNAME, - password: process.env.DB_PASSWORD, - database: process.env.DB_NAME, + username: DB_USERNAME, + password: DB_PASSWORD, + database: DB_NAME, synchronize: false, logging: process.env.NODE_ENV ? true : ["schema", "error", "warn", "log", "migration"], bigNumberStrings: false, diff --git a/src/env.defaults.ts b/src/env.defaults.ts new file mode 100644 index 0000000..0422e88 --- /dev/null +++ b/src/env.defaults.ts @@ -0,0 +1,51 @@ +import "dotenv/config"; +import ms from "ms"; + +export const DB_HOST = process.env.DB_HOST ?? ""; +export const DB_NAME = process.env.DB_NAME ?? ""; +export const DB_USERNAME = process.env.DB_USERNAME ?? ""; +export const DB_PASSWORD = process.env.DB_PASSWORD ?? ""; + +export const SERVER_PORT = Number(process.env.SERVER_PORT ?? 5000); + +export const JWT_SECRET = process.env.JWT_SECRET ?? "my_jwt_secret_string_ilughfnadiuhgq§$IUZGFVRweiouarbt1oub3h5q4a"; +export const JWT_EXPIRATION = process.env.JWT_EXPIRATION ?? "15m"; +export const REFRESH_EXPIRATION = process.env.REFRESH_EXPIRATION ?? "1d"; + +export const MAIL_USERNAME = process.env.MAIL_USERNAME ?? ""; +export const MAIL_PASSWORD = process.env.MAIL_PASSWORD ?? ""; +export const MAIL_HOST = process.env.MAIL_HOST ?? ""; +export const MAIL_PORT = Number(process.env.MAIL_PORT ?? "587"); +export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false"; + +export const CLUB_NAME = process.env.CLUB_NAME ?? ""; + +export function configCheck() { + if (DB_HOST == "" ?? typeof DB_HOST != "string") throw new Error("set valid value to DB_HOST"); + if (DB_NAME == "" ?? typeof DB_NAME != "string") throw new Error("set valid value to DB_NAME"); + if (DB_USERNAME == "" ?? typeof DB_USERNAME != "string") throw new Error("set valid value to DB_USERNAME"); + if (DB_PASSWORD == "" ?? typeof DB_PASSWORD != "string") throw new Error("set valid value to DB_PASSWORD"); + + if (typeof SERVER_PORT != "number") throw new Error("set valid numeric value to SERVER_PORT"); + + if (JWT_SECRET == "" ?? typeof JWT_SECRET != "string") throw new Error("set valid value to JWT_SECRET"); + checkMS(JWT_EXPIRATION, "JWT_EXPIRATION"); + checkMS(REFRESH_EXPIRATION, "REFRESH_EXPIRATION"); + + if (MAIL_USERNAME == "" ?? typeof MAIL_USERNAME != "string") throw new Error("set valid value to MAIL_USERNAME"); + if (MAIL_PASSWORD == "" ?? typeof MAIL_PASSWORD != "string") throw new Error("set valid value to MAIL_PASSWORD"); + if (MAIL_HOST == "" ?? typeof MAIL_HOST != "string") throw new Error("set valid value to MAIL_HOST"); + if (typeof MAIL_PORT != "number") throw new Error("set valid numeric value to MAIL_PORT"); + if (MAIL_SECURE != "true" && MAIL_SECURE != "false") throw new Error("set 'true' or 'false' to MAIL_SECURE"); +} + +function checkMS(input: string, origin: string) { + try { + const result = ms(input); + if (result === undefined) { + throw new Error(`set valid ms value to ${origin}`); + } + } catch (e) { + throw new Error(`set valid ms value to ${origin}`); + } +} diff --git a/src/helpers/jwtHelper.ts b/src/helpers/jwtHelper.ts index 1e5fdb0..5d79921 100644 --- a/src/helpers/jwtHelper.ts +++ b/src/helpers/jwtHelper.ts @@ -1,10 +1,11 @@ import jwt from "jsonwebtoken"; import { JWTData } from "../type/jwtTypes"; +import { JWT_SECRET, JWT_EXPIRATION } from "../env.defaults"; export abstract class JWTHelper { static validate(token: string): Promise { return new Promise((resolve, reject) => { - jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => { + jwt.verify(token, JWT_SECRET, (err, decoded) => { if (err) reject(err.message); else resolve(decoded); }); @@ -15,9 +16,9 @@ export abstract class JWTHelper { return new Promise((resolve, reject) => { jwt.sign( data, - process.env.JWT_SECRET, + JWT_SECRET, { - expiresIn: process.env.JWT_EXPIRATION, + expiresIn: JWT_EXPIRATION, }, (err, token) => { if (err) reject(err.message); diff --git a/src/helpers/mailHelper.ts b/src/helpers/mailHelper.ts index 971078b..b4c2dd9 100644 --- a/src/helpers/mailHelper.ts +++ b/src/helpers/mailHelper.ts @@ -1,16 +1,17 @@ import { Transporter, createTransport, TransportOptions } from "nodemailer"; +import { CLUB_NAME, MAIL_HOST, MAIL_PASSWORD, MAIL_PORT, MAIL_SECURE, MAIL_USERNAME } from "../env.defaults"; export default class MailHelper { private readonly transporter: Transporter; constructor() { this.transporter = createTransport({ - host: process.env.MAIL_HOST, - port: Number(process.env.MAIL_PORT), - secure: (process.env.MAIL_SECURE as "true" | "false") == "true", + host: MAIL_HOST, + port: MAIL_PORT, + secure: (MAIL_SECURE as "true" | "false") == "true", auth: { - user: process.env.MAIL_USERNAME, - pass: process.env.MAIL_PASSWORD, + user: MAIL_USERNAME, + pass: MAIL_PASSWORD, }, } as TransportOptions); } @@ -26,7 +27,7 @@ export default class MailHelper { return new Promise((resolve, reject) => { this.transporter .sendMail({ - from: `"${process.env.CLUB_NAME}" <${process.env.MAIL_USERNAME}>`, + from: `"${CLUB_NAME}" <${MAIL_USERNAME}>`, to: target, subject, text: content, diff --git a/src/helpers/stringHelper.ts b/src/helpers/stringHelper.ts index 4153e7a..b76a6aa 100644 --- a/src/helpers/stringHelper.ts +++ b/src/helpers/stringHelper.ts @@ -1,11 +1,14 @@ +import crypto from "crypto"; + export abstract class StringHelper { static random(len: number, charSet?: string): string { - charSet = charSet || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - var randomString = ""; - for (var i = 0; i < len; i++) { - var randomPoz = Math.floor(Math.random() * charSet.length); - randomString += charSet.substring(randomPoz, randomPoz + 1); - } - return randomString; + // charSet = charSet || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + // var randomString = ""; + // for (var i = 0; i < len; i++) { + // var randomPoz = Math.floor(Math.random() * charSet.length); + // randomString += charSet.substring(randomPoz, randomPoz + 1); + // } + // return randomString; + return crypto.randomBytes(len).toString("base64"); } } diff --git a/src/index.ts b/src/index.ts index ce0e32d..0c5333e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,9 @@ import "dotenv/config"; import express from "express"; +import { configCheck, SERVER_PORT } from "./env.defaults"; +configCheck(); + declare global { namespace Express { export interface Request { @@ -18,6 +21,6 @@ dataSource.initialize(); const app = express(); import router from "./routes/index"; router(app); -app.listen(process.env.SERVER_PORT, () => { - console.log(`listening on *:${process.env.SERVER_PORT}`); +app.listen(SERVER_PORT, () => { + console.log(`listening on *:${SERVER_PORT}`); });