123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // db
- import { selPool, updPool } from "#db";
- import { DbSchema } from "#db-schema";
- import { sql } from "slonik";
- // api
- import { api } from "#api";
- // error
- import { ApiError } from "#exceptions/api-error.js";
- // other
- import { z } from "zod";
- import bcript from "bcrypt";
- import { v7 as uuidv7 } from "uuid";
- import tokenService from "../services/token-service.js";
- import { UserAuthService } from "../services/user-auth-service.js";
- import { ConfirmPinsService } from "#modules/users/confirm-pins/confirm-pins-service.js";
- import { RouterUtils } from "#utils/router-utils.js";
- import { config } from "#config";
- import { Request, Response } from "express";
- class authController {
- // --- Регистрация ---
- async register(
- req: Request,
- res: Response,
- // next: NextFunction
- ) {
- // валидация запроса
- const { email } = api.auth.POST_Registration.req.parse(req.body);
- const isUserExist = await UserAuthService.checkUserExistByEmail(email);
- // если пользователь уже зарегистрирован
- if (isUserExist) {
- RouterUtils.validAndSendResponse(
- api.auth.POST_Registration.res,
- res,
- { code: "alreadyExists" },
- 400,
- );
- return;
- }
- // отправка пина
- const transactionId = uuidv7();
- try {
- await ConfirmPinsService.sendConfirmPin(transactionId, email);
- } catch {
- RouterUtils.validAndSendResponse(
- api.auth.POST_Registration.res,
- res,
- { code: "pinIsNotSent" },
- 400,
- );
- return;
- }
- RouterUtils.validAndSendResponse(api.auth.POST_Registration.res, res, {
- code: "pinIsSent",
- transactionId: transactionId,
- });
- }
- async confirmRegistration(req: Request, res: Response) {
- // валидация запроса
- const { password, transactionId, confirmPin } =
- api.auth.POST_ConfirmRegistration.req.parse(req.body);
- // проверка пина
- const pinInfo = await ConfirmPinsService.checkConfirmPin(
- transactionId,
- confirmPin,
- );
- switch (pinInfo.status) {
- case "rotten": {
- RouterUtils.validAndSendResponse(
- api.auth.POST_ConfirmRegistration.res,
- res,
- { code: "pinIsRotten" },
- 400,
- );
- return;
- }
- case "tooManyTries": {
- RouterUtils.validAndSendResponse(
- api.auth.POST_ConfirmRegistration.res,
- res,
- { code: "tooManyTries" },
- 400,
- );
- return;
- }
- case "wrong": {
- RouterUtils.validAndSendResponse(
- api.auth.POST_ConfirmRegistration.res,
- res,
- {
- code: "pinIsWrong",
- triesRemained: pinInfo.triesRemained,
- },
- 400,
- );
- return;
- }
- }
- // пин правильный
- const email = pinInfo.email;
- // регистрация
- const hashPassword = await bcript.hash(password, 3);
- const userId = uuidv7();
- await updPool.query(
- sql.unsafe`
- insert into usr.users
- (user_id, email, password)
- values
- (${userId}, ${email}, ${hashPassword})`,
- );
- // токены
- const { accessToken, refreshToken } = tokenService.generateTokens({
- email,
- userId,
- });
- await tokenService.insertRefreshToken(userId, refreshToken);
- tokenService.setRefreshTokenInCookie(res, refreshToken);
- RouterUtils.validAndSendResponse(
- api.auth.POST_ConfirmRegistration.res,
- res,
- {
- code: "registered",
- accessToken,
- userData: {
- email,
- userId,
- },
- },
- );
- }
- async login(req: Request, res: Response) {
- // валидация запроса
- const { email, password } = api.auth.POST_Login.req.parse(req.body);
- // поиск юзера
- const user = await selPool.maybeOne(
- sql.type(
- z.object({
- userId: DbSchema.usr.users.userId,
- password: DbSchema.usr.users.password,
- wrongPassTries: DbSchema.usr.users.wrongPassTries,
- }),
- )`
- select
- user_id as "userId",
- password,
- wrong_pass_tries as "wrongPassTries"
- from
- usr.users
- where
- email = ${email}`,
- );
- if (!user) {
- RouterUtils.validAndSendResponse(
- api.auth.POST_Login.res,
- res,
- {
- code: "userNotFound",
- },
- 400,
- );
- return;
- }
- // если количество попыток превышено
- if (user.wrongPassTries > config.PASSWORD_MAX_TRIES - 1) {
- RouterUtils.validAndSendResponse(
- api.auth.POST_Login.res,
- res,
- {
- code: "tooManyTries",
- // TODO: сделать дату разблокировки
- unblockingDate: new Date().toISOString(),
- },
- 400,
- );
- return;
- }
- // проверка пароля
- const isPassEquals = await bcript.compare(password, user.password);
- if (!isPassEquals) {
- await UserAuthService.authTriesIncrement(user.userId);
- const triesRemained = config.PASSWORD_MAX_TRIES - 1 - user.wrongPassTries;
- RouterUtils.validAndSendResponse(
- api.auth.POST_Login.res,
- res,
- {
- code: "passIsWrong",
- triesRemained,
- },
- 400,
- );
- return;
- }
- // успешный логин
- await UserAuthService.resetAuthTries(user.userId);
- // токены
- const { accessToken, refreshToken } = tokenService.generateTokens({
- email,
- userId: user.userId,
- });
- await tokenService.insertRefreshToken(user.userId, refreshToken);
- tokenService.setRefreshTokenInCookie(res, refreshToken);
- RouterUtils.validAndSendResponse(
- api.auth.POST_Login.res,
- res,
- {
- code: "success",
- accessToken,
- userData: { email, userId: user.userId },
- },
- 200,
- );
- }
- async logout(req: Request, res: Response) {
- const { refreshToken } = req.cookies;
- const userData = tokenService.validateRefreshToken(refreshToken);
- await tokenService.removeToken(userData.userId, refreshToken);
- res.clearCookie("refreshToken");
- RouterUtils.validAndSendResponse(api.auth.POST_Logout.res, res, {
- code: "success",
- });
- }
- async logoutAllDevices(req: Request, res: Response) {
- const { refreshToken } = req.cookies;
- const userData = tokenService.validateRefreshToken(refreshToken);
- await tokenService.removeAllUserTokens(userData.userId);
- res.clearCookie("refreshToken");
- RouterUtils.validAndSendResponse(api.auth.POST_LogoutAllDevices.res, res, {
- code: "success",
- });
- }
- async refresh(req: Request, res: Response) {
- const { refreshToken } = req.cookies;
- if (!refreshToken) throw ApiError.UnauthorizedError();
- const userData = tokenService.validateRefreshToken(refreshToken);
- const tokenIsExist = await tokenService.refreshTokenIsExist(
- userData.userId,
- refreshToken,
- );
- if (!userData || !tokenIsExist) {
- res.clearCookie("refreshToken");
- throw ApiError.UnauthorizedError();
- }
- // обновляем токены
- const newUserData = await selPool.maybeOne(
- sql.type(
- z.object({
- email: DbSchema.usr.users.email,
- }),
- )`select email from usr.users where user_id = ${userData.userId}`,
- );
- if (!newUserData) {
- throw ApiError.UnauthorizedError();
- }
- const newTokens = tokenService.generateTokens({
- userId: userData.userId,
- email: newUserData.email,
- });
- await tokenService.updateRefreshToken(
- userData.userId,
- refreshToken,
- newTokens.refreshToken,
- );
- tokenService.setRefreshTokenInCookie(res, newTokens.refreshToken);
- RouterUtils.validAndSendResponse(api.auth.POST_Refresh.res, res, {
- code: "success",
- accessToken: newTokens.accessToken,
- userData: {
- email: newUserData.email,
- userId: userData.userId,
- },
- });
- }
- }
- export const AuthController = new authController();
|