@opengis/fastify-table
Version:
core-plugins
152 lines (151 loc) • 6.72 kB
JavaScript
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);
}