Snai3i-MarketPlace / backend / src / services / users / users.services.ts
users.services.ts
Raw
import usersLogs, { IUserLogs, userLogger } from './users.logs';
import { formatString } from '../../utils/Strings';
import { db } from '../../settings';
import { HttpCodes } from '../../config/Errors';
import { ErrorResponseC, SuccessResponseC } from '../services.response';
import { Response } from 'express';
import { ResultSetHeader, RowDataPacket } from 'mysql2';
import { AuthServices } from '../auth/auth.service';
import { Optimize } from '../../utils/Function';

export class UsersServices {
  static getStatistics = async (): Promise<ResponseT> => {
    try {
      const sqlquery = `SELECT
        (SELECT COUNT(*) FROM teachers) AS teachers,
        (SELECT COUNT(*) FROM schools) AS schools,
        (SELECT COUNT(*) FROM inst_designers) AS inst_designers,
        (SELECT COUNT(*) FROM orders) AS total_orders,
        (SELECT COUNT(*) FROM courses) AS courses
        `;
      const [statistics] = await db.query<any[]>(sqlquery);
      const resp: ICode<IUserLogs> = usersLogs.GET_STATISTICS_SUCCESS;
      const msg = resp.message;
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        statistics[0],
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_STATISTICS_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_STATISTICS_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getUsersBySuperAdmin = async (
    res: Response,
    user_id: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery = `SELECT u.user_id, u.email, u.firstName, u.lastName, u.role, u.phone, s.schoolName FROM users u 
            LEFT JOIN schools s ON u.user_id = s.user_id
            WHERE u.user_id != ?`;
      const [users] = await db.query<UserI[]>(sqlquery, [user_id]);

      const resp: ICode<IUserLogs> = usersLogs.GET_USERS_BY_SUPER_ADMIN_SUCCESS;
      const msg = formatString(resp.message, { count: users.length });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        users,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(
        usersLogs.GET_USERS_BY_SUPER_ADMIN_ERROR.message,
        {
          error: (err as Error)?.message || '',
        }
      );
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_USERS_BY_SUPER_ADMIN_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getUsersByAdmin = async (
    res: Response,
    user_id: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery = `SELECT u.user_id, u.email, u.firstName, u.lastName, u.role, u.phone, s.schoolName FROM users u 
       LEFT JOIN schools s ON u.user_id = s.user_id
       WHERE u.user_id != ? AND u.role != "super_admin" AND u.role != "admin" AND u.role != "inst_admin"`;
      const [users] = await db.query<UserI[]>(sqlquery, [user_id]);
      const resp: ICode<IUserLogs> = usersLogs.GET_USERS_BY_ADMIN_SUCCESS;
      const msg = formatString(resp.message, { count: users.length });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        users,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_USERS_BY_ADMIN_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_USERS_BY_ADMIN_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getUserById = async (
    res: Response,
    userId: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM users WHERE user_id = ?';
      const [[user]] = await db.query<UserI[]>(sqlquery, [userId]);
      if (!user) {
        const msg = formatString(usersLogs.GET_USER_BY_ID_ERROR.message, {
          userId,
        });
        userLogger.error(msg);
        return new ErrorResponseC(
          usersLogs.GET_USER_BY_ID_ERROR.type,
          HttpCodes.NotFound.code,
          msg
        );
      }

      // get additional info base on role
      const additionalInfo = await AuthServices.getAdditionalInfo(
        user.role,
        user.user_id
      );
      if (additionalInfo.isErr) {
        const msg = formatString(usersLogs.GET_USER_BY_ID_ERROR.message, {
          error: (additionalInfo.err as Error)?.message || '',
        });
        userLogger.error(msg, additionalInfo.err as Error);
        return new ErrorResponseC(
          usersLogs.GET_USER_BY_ID_ERROR.type,
          HttpCodes.InternalServerError.code,
          msg
        );
      }

      const resp: ICode<IUserLogs> = usersLogs.GET_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        userId: user.user_id,
        email: user.email,
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        { ...Optimize(user), ...additionalInfo.data },
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static updateUserById = async (
    res: Response,
    userId: number,
    data: {
      email: string;
      firstName: string;
      lastName: string;
      role: string;
      phone: string | null;
      additionalInfo: any;
    }
  ): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM users WHERE user_id = ?';
      const [[user]] = await db.query<UserI[]>(sqlquery, [userId]);
      if (!user) {
        const msg = formatString(usersLogs.UPDATE_USER_BY_ID_ERROR.message, {
          userId,
        });
        userLogger.error(msg);
        return new ErrorResponseC(
          usersLogs.UPDATE_USER_BY_ID_ERROR.type,
          HttpCodes.NotFound.code,
          msg
        );
      }

      const sqlUpdateQuery =
        'UPDATE users SET email = ?, firstName = ?, lastName = ?, role = ?, phone = ? WHERE user_id = ?';

      const result = await db.query<ResultSetHeader[]>(sqlUpdateQuery, [
        data.email,
        data.firstName,
        data.lastName,
        data.role,
        data.phone,
        userId,
      ]);

      // if (data.password) {
      //   const hashedPassword = bcrypt.hashSync(data.password, 10);
      //   const sqlUpdatePasswordQuery =
      //     'UPDATE users SET password = ? WHERE user_id = ?';
      //   await db.query<ResultSetHeader[]>(sqlUpdatePasswordQuery, [
      //     hashedPassword,
      //     userId,
      //   ]);
      // }

      // update additional info base on role
      if (
        user.role !== 'admin' &&
        user.role !== 'super_admin' &&
        user.role !== 'inst_admin'
      ) {
        const updateAdditionalInfo = await this.updateAdditionalInfo(
          user.role,
          user.user_id,
          data.additionalInfo
        );
        if (updateAdditionalInfo.isErr) {
          const msg = formatString(usersLogs.UPDATE_USER_BY_ID_ERROR.message, {
            error: (updateAdditionalInfo.err as Error)?.message || '',
          });
          userLogger.error(msg, updateAdditionalInfo.err as Error);
          return new ErrorResponseC(
            usersLogs.UPDATE_USER_BY_ID_ERROR.type,
            HttpCodes.InternalServerError.code,
            msg
          );
        }
      }

      const resp: ICode<IUserLogs> = usersLogs.UPDATE_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        userId,
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        { ...Optimize(user), ...data },
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.UPDATE_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.UPDATE_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static updateTeacherById = async (
    res: Response,
    teacherId: number,
    data: {
      email: string;
      firstName: string;
      lastName: string;
      role: string;
      phone: string | null;
      additionalInfo: any;
    }
  ): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM teachers WHERE teacher_id = ?';
      const [[teacher]] = await db.query<Teacher[]>(sqlquery, [teacherId]);
      if (!teacher) {
        const msg = formatString(usersLogs.UPDATE_USER_BY_ID_ERROR.message, {
          teacherId,
        });
        userLogger.error(msg);
        return new ErrorResponseC(
          usersLogs.UPDATE_USER_BY_ID_ERROR.type,
          HttpCodes.NotFound.code,
          msg
        );
      }

      const sqlUpdateQuery =
        'UPDATE users SET email = ?, firstName = ?, lastName = ?, role = ?, phone = ? WHERE user_id = ?';

      const result = await db.query<ResultSetHeader[]>(sqlUpdateQuery, [
        data.email,
        data.firstName,
        data.lastName,
        data.role,
        data.phone,
        teacher.user_id,
      ]);

      // update additional info base on role
      const updateAdditionalInfo = await this.updateAdditionalInfo(
        'teacher',
        teacher.user_id,
        data.additionalInfo
      );
      if (updateAdditionalInfo.isErr) {
        const msg = formatString(usersLogs.UPDATE_USER_BY_ID_ERROR.message, {
          error: (updateAdditionalInfo.err as Error)?.message || '',
        });
        userLogger.error(msg, updateAdditionalInfo.err as Error);
        return new ErrorResponseC(
          usersLogs.UPDATE_USER_BY_ID_ERROR.type,
          HttpCodes.InternalServerError.code,
          msg
        );
      }

      const resp: ICode<IUserLogs> = usersLogs.UPDATE_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        teacherId,
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        { ...Optimize(teacher), ...data },
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.UPDATE_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.UPDATE_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static updateAdditionalInfo = async (
    role: string,
    user_id: number,
    additionalInfo: any
  ): Promise<{
    isErr: boolean;
    err: any;
  }> => {
    const tableName =
      role === 'teacher'
        ? 'teachers'
        : role === 'school'
        ? 'schools'
        : 'inst_designers';

    if (!additionalInfo) return { isErr: false, err: null };
    const additionalInfoKeys = Object.keys(additionalInfo);
    const additionalInfoValues = Object.values(additionalInfo);

    const additionalInfoColumns = additionalInfoKeys.join(' = ?, ');
    additionalInfoValues.push(user_id);

    try {
      const sqlquery = `UPDATE ${tableName} SET ${additionalInfoColumns} = ? WHERE user_id = ?`;

      
      await db.query<RowDataPacket[]>(sqlquery, additionalInfoValues);

      return { isErr: false, err: null };
    } catch (err) {
      return { isErr: true, err };
    }
  };

  static deleteUserById = async (
    res: Response,
    userId: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM users WHERE user_id = ?';
      const [[user]] = await db.query<UserI[]>(sqlquery, [userId]);
      if (!user) {
        const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
          userId,
        });
        userLogger.error(msg);
        return new ErrorResponseC(
          usersLogs.DELETE_USER_BY_ID_ERROR.type,
          HttpCodes.NotFound.code,
          msg
        );
      }

      const sqlDeleteQuery = 'DELETE FROM users WHERE user_id = ?';
      await db.query<RowDataPacket[]>(sqlDeleteQuery, [userId]);

      // delete additional info base on role
      if (
        user.role !== 'admin' &&
        user.role !== 'super_admin' &&
        user.role !== 'inst_admin'
      ) {
        const tableName =
          user.role === 'teacher'
            ? 'teachers'
            : user.role === 'school'
            ? 'schools'
            : 'inst_designers';
        const sqlDeleteAdditionalInfoQuery = `DELETE FROM ${tableName} WHERE user_id = ?`;
        await db.query<RowDataPacket[]>(sqlDeleteAdditionalInfoQuery, [userId]);
      }

      const resp: ICode<IUserLogs> = usersLogs.DELETE_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        userId,
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        { ...Optimize(user) },
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.DELETE_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static deleteTeacherById = async (
    res: Response,
    teacherId: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM teachers WHERE teacher_id = ?';
      const [[teacher]] = await db.query<Teacher[]>(sqlquery, [teacherId]);
      if (!teacher) {
        const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
          teacherId,
        });
        userLogger.error(msg);
        return new ErrorResponseC(
          usersLogs.DELETE_USER_BY_ID_ERROR.type,
          HttpCodes.NotFound.code,
          msg
        );
      }

      const sqlDeleteQuery = 'DELETE FROM users WHERE user_id = ?';
      await db.query<RowDataPacket[]>(sqlDeleteQuery, [teacher.user_id]);

      const resp: ICode<IUserLogs> = usersLogs.DELETE_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        teacherId,
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        { ...Optimize(teacher) },
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.DELETE_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static bulkDeleteUsers = async (
    res: Response,
    userIds: number[]
  ): Promise<ResponseT> => {
    try {
      //   const sqlDeleteQuery = 'DELETE FROM users WHERE user_id IN (?)';
      //   await db.query<RowDataPacket[]>(sqlDeleteQuery, [userIds]);

      for (const userId of userIds) {
        const sqlDeleteQuery = 'DELETE FROM users WHERE user_id = ?';
        await db.query<RowDataPacket[]>(sqlDeleteQuery, [userId]);
      }

      const resp: ICode<IUserLogs> = usersLogs.DELETE_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        userIds: userIds.join(', '),
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(resp.type, {}, msg, HttpCodes.Accepted.code);
    } catch (err) {
      const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.DELETE_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static bulkDeleteTeachers = async (
    res: Response,
    teacherIds: number[]
  ): Promise<ResponseT> => {
    try {
      for (const teacherId of teacherIds) {
        const sqlquery = 'SELECT * FROM teachers WHERE teacher_id = ?';
        const [[teacher]] = await db.query<Teacher[]>(sqlquery, [teacherId]);
        if (!teacher) {
          const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
            teacherId,
          });
          userLogger.error(msg);
          return new ErrorResponseC(
            usersLogs.DELETE_USER_BY_ID_ERROR.type,
            HttpCodes.NotFound.code,
            msg
          );
        }

        const sqlDeleteQuery = 'DELETE FROM users WHERE user_id = ?';
        await db.query<RowDataPacket[]>(sqlDeleteQuery, [teacher.user_id]);
      }

      const resp: ICode<IUserLogs> = usersLogs.DELETE_USER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        teacherIds: teacherIds.join(', '),
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(resp.type, {}, msg, HttpCodes.Accepted.code);
    } catch (err) {
      const msg = formatString(usersLogs.DELETE_USER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.DELETE_USER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getTeachersBySchool = async (
    res: Response,
    schoolId: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery =
        'SELECT t.*, u.firstName, u.lastName, u.user_id, u.email, u.phone FROM teachers t JOIN users u ON t.user_id = u.user_id WHERE t.school_id = ?';
      const [teachers] = await db.query<Teacher[]>(sqlquery, [schoolId]);
      const resp: ICode<IUserLogs> = usersLogs.GET_TEACHERS_BY_SCHOOL_SUCCESS;
      const msg = formatString(resp.message, { count: teachers.length });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        teachers,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_TEACHERS_BY_SCHOOL_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_TEACHERS_BY_SCHOOL_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getTeacherById = async (
    res: Response,
    teacherId: number
  ): Promise<ResponseT> => {
    try {
      const sqlquery =
        'SELECT t.*, u.firstName, u.lastName, u.user_id, u.email, u.phone FROM teachers t JOIN users u ON t.user_id = u.user_id WHERE t.teacher_id = ?';
      const [[teacher]] = await db.query<Teacher[]>(sqlquery, [teacherId]);
      if (!teacher) {
        const msg = formatString(usersLogs.GET_TEACHER_BY_ID_ERROR.message, {
          teacherId,
        });
        userLogger.error(msg);
        return new ErrorResponseC(
          usersLogs.GET_TEACHER_BY_ID_ERROR.type,
          HttpCodes.NotFound.code,
          msg
        );
      }

      const resp: ICode<IUserLogs> = usersLogs.GET_TEACHER_BY_ID_SUCCESS;
      const msg = formatString(resp.message, {
        teacherId,
      });
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        teacher,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_TEACHER_BY_ID_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_TEACHER_BY_ID_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getSchools = async (res: Response): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM schools';
      const [schools] = await db.query<School[]>(sqlquery);
      const resp: ICode<IUserLogs> = usersLogs.GET_SCHOOLS_SUCCESS;
      const msg = resp.message;
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        schools,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_SCHOOLS_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_SCHOOLS_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getTeachers = async (res: Response): Promise<ResponseT> => {
    try {
      const sqlquery =
        'SELECT t.*, u.email FROM teachers t JOIN users u ON t.user_id = u.user_id';
      const [teachers] = await db.query<Teacher[]>(sqlquery);
      const resp: ICode<IUserLogs> = usersLogs.GET_TEACHERS_SUCCESS;
      const msg = resp.message;
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        teachers,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_TEACHERS_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_TEACHERS_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };

  static getInstDesigners = async (res: Response): Promise<ResponseT> => {
    try {
      const sqlquery = 'SELECT * FROM inst_designers';
      const [instDesigners] = await db.query<any[]>(sqlquery);
      const resp: ICode<IUserLogs> = usersLogs.GET_INST_DESIGNERS_SUCCESS;
      const msg = resp.message;
      userLogger.info(msg, { type: resp.type });
      return new SuccessResponseC(
        resp.type,
        instDesigners,
        msg,
        HttpCodes.Accepted.code
      );
    } catch (err) {
      const msg = formatString(usersLogs.GET_INST_DESIGNERS_ERROR.message, {
        error: (err as Error)?.message || '',
      });
      userLogger.error(msg, err as Error);
      return new ErrorResponseC(
        usersLogs.GET_INST_DESIGNERS_ERROR.type,
        HttpCodes.InternalServerError.code,
        msg
      );
    }
  };
}