UNPKG

@opengis/fastify-table

Version:

core-plugins

152 lines (151 loc) 6.72 kB
import path from "node:path"; import { fileURLToPath } from "url"; import { randomBytes } from "node:crypto"; import { readFile } from "node:fs/promises"; import config from "../../../../../config.js"; import getRedis from "../../../../plugins/redis/funcs/getRedis.js"; import logger from "../../../../plugins/logger/getLogger.js"; import pgClients from "../../../../plugins/pg/pgClients.js"; import getTemplate from "../../../../plugins/table/funcs/getTemplate.js"; import sendNotification from "../../../../plugins/auth/funcs/sendNotification.js"; const rclient = getRedis(); const template = "recovery-password-email-template"; const dirname = path.dirname(fileURLToPath(import.meta.url)); function formatResponse(url, noredirect, reply) { const response = { "recovery?no-such-email=1": "Дана електронна адреса не прив'язана до жодного облікового запису", "recovery?thank-you-page=1": "Повідомлення з кодом для відновлення паролю відправлено на вказану електронну адресу", "recovery?notification-error=1": "Помилка при відправці повідомлення", "recovery?pass-not-match=1": "Паролі не співпадають", "recovery?code-is-invalid=1": "Код вже використаний або невалідний", login: "Пароль успішно змінено", }[url] || url; if (!noredirect) { return reply.redirect(`/${url}`); } return reply.status(url === "login" ? 200 : 400).send(response); } /** * Відновлення пароля за поштою користувача або генерування нового * * @method POST * @summary Відновлення пароля користувача * @priority 1 * @alias recovery-password-email * @type api * @tag auth * @param {String} query.code Персональний код користувача * @param {String} body.email Електрона пошта * @param {String} body.password Дійсний пароль * @param {String} body.repassword Новий пароль * @errors 400,500 * @returns {Number} status Номер помилки * @returns {String|Object} error Опис помилки * @returns {String|Object} message Повідомлення про успішне виконання або об'єкт з параметрами * @returns {String} redirect Шлях до переадресації */ export default async function passwordRecovery(req, reply) { const { pg = pgClients.client, query = {}, body = {}, hostname, unittest, } = req; const { nocache = config.local } = query; if (!query.code && !body.email) { return reply.status(400).send('Parameter "email" is required.'); } if (query.code && !body.password) { return reply.status(400).send('Param "password" is required.'); } // step 1 - send email code async function sendEmailToChangePassword() { const code = randomBytes(8).toString("hex"); const row = config.pg ? await pg .query("select uid from admin.users where $1 in (email,login)", [ body.email, ]) .then((el) => el.rows?.[0] || {}) : {}; if (!row?.uid) { return "recovery?no-such-email=1"; } const keyCacheTimeout = `${pg?.options?.database}:verification-email:${row.uid}:timeout`; const checkCodeTimeout = config.redis ? await rclient.get(keyCacheTimeout) : false; if (checkCodeTimeout && !nocache) { return reply .status(400) .send(`Код вже відправлено на пошту ${body.email}. Перевірте або спробуйте через 1 хвилину`); } const q = "update admin.users set user_personal_code=$1 where $2 in (login,email)"; if (config.pg) { await pg.query(q, [code, body.email]); } const userQuery = `select coalesce(sur_name,'')|| coalesce(' '||user_name,'')||coalesce(' '||father_name,'') as user from admin.users where $1 in (login,email) limit 1`; const { user: userName } = config.pg ? await pg .query(userQuery, [body.email]) .then((el) => el.rows?.[0] || {}) : {}; try { const customPt = await getTemplate("pt", template); const pt = customPt || (await readFile(path.join(dirname, `../templates/pt/${template}.html`), "utf8")); const options = { pg, to: body?.email, template: pt, data: { login: userName, code, domain: `${req.protocol || "https"}://${hostname}`, url: `recovery?code=${code}`, }, title: `Recover password ${hostname}`, nocache, }; if (config?.mailSetting?.from) { await sendNotification(options, unittest); } if (config.redis) { await rclient.set(keyCacheTimeout, 1, "EX", 1 * 60); } return "recovery?thank-you-page=1"; } catch (err) { logger.file("recovery/error", { uid: row?.uid, stack: err.stack, error: err.toString(), }); logger.error(err); return "recovery?notification-error=1"; } } // step 2 - change user password with code (email) and new password (body) async function changePassword() { if (body.password !== body.repassword) { return "recovery?pass-not-match=1"; } try { const q = "update admin.users set password=$1,user_personal_code='' where user_personal_code=$2 returning uid"; const res = config.pg ? await pg .query(q, [body.password, query.code]) .then((el) => el.rows?.[0] || {}) : {}; if (!res?.uid) { return "recovery?code-is-invalid=1"; } return "login"; } catch (err) { logger.file("recovery/error", { error: err.toString() }); logger.error(err); throw err; } } const url = !query.code ? await sendEmailToChangePassword() : await changePassword(); return formatResponse(url, query.noredirect, reply); }