import { randomBytes } from 'node:crypto' import { validationResult } from 'express-validator' import jwt from 'jsonwebtoken' import config from '../config/appConfig.js' import { Building, User } from '../db/index.js' import { getUsernameFromEmail, success, warning } from '../helpers/index.js' import Mailer from '../mailer.js' import { hashString } from '../utils/crypto.js' import { ResponseCode } from '../ENUMS.js' const mailer = new Mailer() if (config.mailer.mode === 'smtp') { mailer.init({ host: config.mailer.host, port: config.mailer.port, user: config.mailer.user, pass: config.mailer.pass }, config.mailer.mode) } else if (config.mailer.mode === 'sendmail') { mailer.init({ sendmail: true }, config.mailer.mode) } else { mailer.init(null, null) } export const confirm = async (req, res) => { const validationErrors = validationResult(req) if (!validationErrors.isEmpty()) { return warning(res, { code: ResponseCode.ValidationError }) } const { token } = req.params const result = await User.findOneAndUpdate({ confirmationToken: token, confirmationExpireAt: { $gt: new Date() } }, { emailConfirmed: true, confirmationExpiresAt: null, confirmationToken: null }, { new: true, runValidators: true }) res.redirect(`${config.endpoints.app}/?verified=${!!result}&expired=${!result}`) } export const login = async (req, res) => { const validationErrors = validationResult(req) if (!validationErrors.isEmpty()) { return warning(res, { code: ResponseCode.ValidationError }) } const { emailOrUsername, password } = req.body const result = await User.findUserAndVerify(emailOrUsername, password) if (result) { if (config.emailVerification === 'on' && !result.emailConfirmed && result.role === 'administrator') { return warning(res, { code: ResponseCode.Confirm }, 200) } const token = jwt.sign({ role: result.role, userId: result._id }, config.jwt.secrect, { expiresIn: config.jwt.expiresIn }) return success(res, { code: ResponseCode.LoggedIn, data: { token } }) } warning(res, { code: ResponseCode.WrongCredentials }, 200) } export const register = async (req, res) => { const validationErrors = validationResult(req) if (!validationErrors.isEmpty()) { return warning(res, { code: ResponseCode.ValidationError }) } const { email, password, gmlid } = req.body const result = await User.createNewAccount(email, { email, username: getUsernameFromEmail(email), encryptedPassword: hashString(password), role: 'administrator', sharingAllowed: true }) if (result) { const building = await Building.create({ gmlid, owner: result._id, createdBy: result._id }) if (config.emailVerification === 'on') { const token = randomBytes(64).toString('hex') const expireAt = Date.now() + 86400000 // DEVINFO Gültigkeit des Token 24h await User.updateOne({ _id: result._id }, { linkedTo: building._id, confirmationToken: token, confirmationExpireAt: expireAt }) console.log(`LOG Confirmation Token: ${config.endpoints.api}/confirm/${token}`) // DEVINFO Hier kann der E-Mail-Text (Registrierung/Bestätigung) bearbeitet werden. const subject = '3% Plus - E-Mail-Bestätigung' const message = ` Sehr geehrte Frau/Sehr geehrter Herr, um Ihr Benutzerkonto verwenden zu können müssen Sie Ihre E-Mail bestätigen. Klicken Sie hierzu bitte auf den nachfolgenden Link: ${config.endpoints.api}/confirm/${token} Mit freundlichen Grüßen Ihr Administrator Wenn diese E-Mail nicht an Sie gerichtet ist, dann löschen Sie diese Nachricht bitte oder informieren den Absender über die falsche Zustellung. ` mailer.sendMail(email, subject, message, null) } else { await User.updateOne({ _id: result._id }, { linkedTo: building._id }) } return success(res, { code: ResponseCode.Registered, data: { confirmRequired: config.emailVerification === 'on' } }, 201) } warning(res, { code: ResponseCode.EmailInUse }, 200) } export const resetPassword = async (req, res) => { const validationErrors = validationResult(req) if (!validationErrors.isEmpty()) { return warning(res, { code: ResponseCode.ValidationError, validationErrors }) } const { email } = req.body const result = await User.findOne({ email }) if (result) { const token = randomBytes(64).toString('hex') const expireAt = Date.now() + 86400000 // DEVINFO Gültigkeit des Token 24h await User.findByIdAndUpdate(result._id, { resetExpireAt: expireAt, resetToken: token, lastResetRequest: Date.now() }, { runValidators: true }) console.log(`LOG Reset Token: ${config.endpoints.app}/reset-password?token=${token}`) // DEVINFO Hier kann der E-Mail-Text (Registrierung/Bestätigung) bearbeitet werden. const subject = '3% Plus - Passwort vergessen' const message = ` Sehr geehrte Frau/Sehr geehrter Herr, um Ihr Passwort zurückzusetzen klicken Sie bitte auf den nachfolgenden Link: ${config.endpoints.app}/reset-password?token=${token} Mit freundlichen Grüßen Ihr Administrator Wenn diese E-Mail nicht an Sie gerichtet ist, dann löschen Sie diese Nachricht bitte oder informieren den Absender über die falsche Zustellung. ` mailer.sendMail(email, subject, message, null) return success(res, {}) } warning(res, { code: ResponseCode.NotFound, result }, 404) } export const updatePassword = async (req, res) => { const validationErrors = validationResult(req) if (!validationErrors.isEmpty()) { return warning(res, { code: ResponseCode.ValidationError }) } const { token } = req.params const { password } = req.body const result = await User.findOneAndUpdate({ resetToken: token, resetExpireAt: { $gt: new Date() } }, { encryptedPassword: hashString(password) }, { new: true, runValidators: true }) if (result) { return success(res, { code: ResponseCode.UpdateSuccess }) } warning(res, { code: ResponseCode.NotFound }) }