1314 lines
48 KiB
TypeScript
1314 lines
48 KiB
TypeScript
import fs from "fs";
|
|
import path from "path";
|
|
import { rm } from "fs/promises";
|
|
import axios from "axios";
|
|
import {
|
|
IUser,
|
|
IUserView,
|
|
SignUpParams,
|
|
LoginParams,
|
|
LoginByTokenParams,
|
|
LoginByGoogleParams,
|
|
SetUserFileParams,
|
|
VerificationUserParams,
|
|
PaginateUsersParams,
|
|
PaginateUsersResults,
|
|
UpdateUserParams,
|
|
FindUsersParams,
|
|
ChangePasswordParams,
|
|
CollaboratorView,
|
|
FindCollaboratorByEmailParams,
|
|
RecoveryAccountParams,
|
|
IGoogleData,
|
|
DeleteUserParams,
|
|
SavePayerEmailParams,
|
|
GetPayerEmailParams,
|
|
PayerEmailResult,
|
|
CheckUserPhoneParams,
|
|
UserAssistanceInfo,
|
|
SysAdminPaginateUsersParams,
|
|
SysAdminUpdateProfileParams,
|
|
SysAdminSetVerifiedParams,
|
|
SysAdminDeleteUserParams,
|
|
SysAdminOrganizationsStatusParams,
|
|
SysAdminUserOrganizationsResult,
|
|
} from "./Users.Interface";
|
|
import { UsersMongooseAdapter } from "./Users.Adapter.Mongoose";
|
|
import { IUsersAdapter, IUsersManager } from "./Users.Interface";
|
|
import { ISession } from "../Session.model";
|
|
import bcrypt from "bcryptjs";
|
|
import jwt from "jsonwebtoken";
|
|
import Joi from "joi";
|
|
import hashPassword from "../../helpers/HashPassword";
|
|
import { isNull } from "../../helpers/IsNull";
|
|
import { joinStrings } from "../../helpers/String";
|
|
import { NoPermissionMessage } from "../../helpers/NoPermissionMessage";
|
|
import { NotificationsManager } from "../Notifications/Notifications";
|
|
import sharp from "sharp";
|
|
import getAvatar from "../../helpers/getAvatar";
|
|
import PlanSubscriptionsList from "../../Models/PlanSubscriptions/PlanSubscriptons";
|
|
import { ISubscriptionInfo } from "../PlanSubscriptions/PlanSubscriptions.interface";
|
|
import MetricsList from "../Metrics/Metrics";
|
|
import PlansList from "../Plans/Plans";
|
|
import CompaniesList from "../Companies/Companies";
|
|
import EmployeesList from "../Employees/Employee";
|
|
import SchedulesList from "../Schedules/Schedules";
|
|
import SchedulesEnabledList from "../SchedulesEnabled/SchedulesEnabled";
|
|
import EmployeesServicesList from "../EmployeesServices/EmployeesServices";
|
|
import AppointmentList from "../Appointments/Appointments";
|
|
import MessageConversationList from "../MessageConversations/MessagesConversations";
|
|
import SystemNoficationsList from "../SystemNotifications/SystemNotification";
|
|
import HeatMapList from "../HeatMap/HeatMap";
|
|
import ClientsList from "../Clients/Clients";
|
|
import RepeatsList from "../Repeats/Repeats";
|
|
import { OAuth2Client } from "google-auth-library";
|
|
import SchedulesDisabledList from "../SchedulesDisabled/SchedulesDisabled";
|
|
|
|
const cleanEmail = (email: string): string => {
|
|
return email.toLowerCase().trim();
|
|
};
|
|
|
|
class UsersManager implements IUsersManager {
|
|
users: IUsersAdapter;
|
|
|
|
constructor() {
|
|
this.users = new UsersMongooseAdapter();
|
|
}
|
|
|
|
private async getOrganizationSubscriptions(
|
|
userId: string
|
|
): Promise<Record<string, ISubscriptionInfo | null>> {
|
|
const subscriptions: Record<string, ISubscriptionInfo | null> = {};
|
|
|
|
// 1. Get companies where user is owner
|
|
const ownerCompanies = await CompaniesList.companies.find({ ownerId: userId });
|
|
for (const company of ownerCompanies) {
|
|
if (company.id) {
|
|
const sub = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: company.ownerId,
|
|
});
|
|
subscriptions[company.id] = sub;
|
|
}
|
|
}
|
|
|
|
// 2. Get companies where user is employee
|
|
const employeeRecords = await EmployeesList.employees.find({ userId: userId });
|
|
for (const employee of employeeRecords) {
|
|
const company = await CompaniesList.companies.findOne({ _id: employee.companyId });
|
|
if (company && company.id && !subscriptions[company.id]) {
|
|
const sub = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: company.ownerId,
|
|
});
|
|
subscriptions[company.id] = sub;
|
|
}
|
|
}
|
|
|
|
return subscriptions;
|
|
}
|
|
|
|
public async checkUserPhone(data: CheckUserPhoneParams): Promise<boolean> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
if (!sessionUser.phoneAreaCode || sessionUser.phoneAreaCode == "") {
|
|
return false;
|
|
}
|
|
|
|
if (!sessionUser.phoneNumber || sessionUser.phoneNumber == "") {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public async getPayerEmail(data: GetPayerEmailParams): Promise<PayerEmailResult> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
return {
|
|
mpPayerEmail: sessionUser.mpPayerEmail ? cleanEmail(sessionUser.mpPayerEmail) : "",
|
|
};
|
|
}
|
|
|
|
public async savePayerEmail(data: SavePayerEmailParams): Promise<void> {
|
|
const { email } = data;
|
|
|
|
// Define a validation profile using Joi
|
|
const validationProfile = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
sessionUser: Joi.string().required(),
|
|
});
|
|
|
|
// Validate the data using the validation profile
|
|
const { error } = validationProfile.validate(data, {
|
|
abortEarly: false,
|
|
});
|
|
|
|
// Throw an error if validation fails
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
sessionUser.mpPayerEmail = cleanEmail(email);
|
|
|
|
await sessionUser.save();
|
|
}
|
|
|
|
public async deleteUser(data: DeleteUserParams): Promise<void> {
|
|
const userCheck = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!userCheck) {
|
|
throw new Error("El usuario que intenta dar de baja no existe.");
|
|
}
|
|
|
|
const ownerOrgs = await CompaniesList.companies.find({
|
|
ownerId: data.sessionUser,
|
|
});
|
|
|
|
//No puede tener organizaciones creadas.
|
|
if (ownerOrgs.length > 0) {
|
|
throw new Error(
|
|
"Para darte de baja primero tenes que eliminar las organizaciones que creaste."
|
|
);
|
|
}
|
|
|
|
//No debe tener una suscripcion.
|
|
const sub = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
if (sub) {
|
|
if (sub.plan.price > 0) {
|
|
throw new Error("No podés darte de baja mientras tengas un plan pago vigente. Podés eliminar tus organizaciones para borrar su contenido y seguir usando la cuenta hasta el vencimiento.");
|
|
}
|
|
}
|
|
|
|
try {
|
|
//Si es colaborador en una o mas organizaciones, tenemos que:
|
|
const employees = await EmployeesList.employees.find({
|
|
userId: data.sessionUser,
|
|
});
|
|
|
|
for (const employee of employees) {
|
|
const organization = await CompaniesList.companies.findOne({
|
|
_id: employee.companyId,
|
|
});
|
|
|
|
if (!organization) {
|
|
continue;
|
|
}
|
|
|
|
const collaboratorUser = await this.users.findOne({
|
|
_id: employee.userId,
|
|
});
|
|
|
|
if (!collaboratorUser) {
|
|
continue;
|
|
}
|
|
|
|
//1. Eliminar sus horarios de la organizacion.
|
|
await SchedulesList.deleteSchedulesByEmployee({
|
|
employeeId: String(employee.id),
|
|
});
|
|
//2. Eliminar los horarios habilitados de la organizacion.
|
|
await SchedulesEnabledList.deleteSchedulesEnabledByEmployee({
|
|
employeeId: String(employee.id),
|
|
});
|
|
|
|
//3. Eliminar los horarios deshabilitados de la organizacion.
|
|
await SchedulesDisabledList.deleteSchedulesDisabledByEmployee({
|
|
employeeId: String(employee.id),
|
|
});
|
|
|
|
//3. Eliminar los servicios asignados al colaborador.
|
|
await EmployeesServicesList.deleteEmployeeServiceByEmployee({
|
|
employeeId: String(employee.id),
|
|
});
|
|
//4. Cambiar los turnos que tenga al duenio de la organizacion.
|
|
const appointments = await AppointmentList.Appointments.find({
|
|
employeeId: String(employee.id),
|
|
});
|
|
|
|
for (const appointment of appointments) {
|
|
await AppointmentList.changeEmployeeToOwner({
|
|
appointmentId: String(appointment.id),
|
|
companyId: String(employee.companyId),
|
|
});
|
|
}
|
|
|
|
//5. Eliminar el HeatMap del Colaborador.
|
|
await HeatMapList.deleteHeatMapByEmployee({
|
|
employeeId: String(employee.id),
|
|
});
|
|
|
|
//6. Eliminar el colaborador de la organizacion.
|
|
await EmployeesList.deleteEmployee({
|
|
employeeId: String(employee.id),
|
|
});
|
|
|
|
//7.Recalcular las metricas del duenio.
|
|
await MetricsList.calculateMetrics({
|
|
userId: String(organization.ownerId),
|
|
});
|
|
|
|
//Enviamos la notificacion al duenio de la organizacion a la que pertenecia el colaborador.
|
|
await SystemNoficationsList.createNotification({
|
|
userId: String(organization.ownerId),
|
|
subject: "Colaborador eliminado",
|
|
message: `El colaborador ${UserList.getUserFullName(
|
|
collaboratorUser
|
|
)} ha sido eliminado de tu organización. y sus turnos se han transferido a ti ya que se ha dado de baja de la plataforma.`,
|
|
});
|
|
}
|
|
|
|
//Eliminar las repeticiones de turnos.
|
|
await RepeatsList.deleteRepeatsByUser({
|
|
userId: data.sessionUser,
|
|
});
|
|
|
|
//Desvincular los clientes creados con el usuario sin borrar el historial de la organizacion.
|
|
const clients = await ClientsList.clients.find({
|
|
userId: data.sessionUser,
|
|
});
|
|
|
|
for (const client of clients) {
|
|
if (!client.id) {
|
|
continue;
|
|
}
|
|
|
|
await ClientsList.clients.detachUser({ clientId: String(client.id) });
|
|
}
|
|
|
|
//Eliminar las conversaciones
|
|
await MessageConversationList.deleteConversationsByUser({
|
|
userId: data.sessionUser,
|
|
});
|
|
|
|
//Eliminar las suscripciones
|
|
await PlanSubscriptionsList.deletePlanSuscriptionByUser({
|
|
sessionUser: data.sessionUser,
|
|
});
|
|
|
|
//Eliminar las notificaciones
|
|
await SystemNoficationsList.deleteNotificationsByUser({
|
|
userId: data.sessionUser,
|
|
});
|
|
|
|
//Eliminar foto de perfil.
|
|
await this.deleteUserFiles(data.sessionUser);
|
|
|
|
//Elimminar el usuario.
|
|
await this.users.delete(data.sessionUser);
|
|
} catch (e) {
|
|
console.log("Error al eliminar el usuario:", e);
|
|
}
|
|
}
|
|
|
|
private async deleteUserFiles(userId: string): Promise<void> {
|
|
const rootDir =
|
|
process.env.NODE_ENV !== "production"
|
|
? process.env.UPLOAD_USERS_FILES_ROOT_DEV
|
|
: process.env.UPLOAD_USERS_FILES_ROOT_PRO;
|
|
|
|
if (!rootDir) {
|
|
throw new Error("No se ha encontrado la ruta de los archivos");
|
|
}
|
|
|
|
const userDir = path.join(__dirname, rootDir, userId);
|
|
|
|
try {
|
|
await rm(userDir, { recursive: true, force: true });
|
|
} catch (err) {
|
|
console.error(`Error al eliminar el directorio ${userDir}:`, err);
|
|
}
|
|
}
|
|
|
|
public getUserFullName(data: IUser): string {
|
|
return joinStrings([data.firstName, data.lastName], " ");
|
|
}
|
|
|
|
private getVerificationCode(): string {
|
|
return Math.floor(Math.random() * 1000000000)
|
|
.toString()
|
|
.substring(0, 6);
|
|
}
|
|
|
|
public async signUp(data: SignUpParams): Promise<IUser> {
|
|
// Destructure email and password from the data object
|
|
const {
|
|
email,
|
|
password,
|
|
firstName,
|
|
lastName,
|
|
phoneCountryCode = "549",
|
|
phoneAreaCode,
|
|
phoneNumber,
|
|
} = data;
|
|
|
|
// Define a validation profile using Joi
|
|
const validationProfile = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
password: Joi.string().alphanum().min(8).required(),
|
|
firstName: Joi.string().required(),
|
|
lastName: Joi.string().required(),
|
|
phoneAreaCode: Joi.string().required(),
|
|
phoneNumber: Joi.string().required(),
|
|
});
|
|
|
|
// Validate the data using the validation profile
|
|
const { error } = validationProfile.validate(data, {
|
|
abortEarly: false,
|
|
});
|
|
|
|
// Throw an error if validation fails
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
const user = await this.users.findOne({ email: cleanEmail(email) });
|
|
|
|
if (user) {
|
|
throw new Error("El usuario ya se encuentra registrado.");
|
|
}
|
|
|
|
const passwordCifrado = await hashPassword(password);
|
|
|
|
const verificationCode = this.getVerificationCode();
|
|
|
|
const newUserData: IUser = {
|
|
email: cleanEmail(email),
|
|
firstName: firstName,
|
|
lastName: lastName,
|
|
phoneCountryCode: phoneCountryCode,
|
|
phoneAreaCode: phoneAreaCode,
|
|
phoneNumber: phoneNumber,
|
|
password: passwordCifrado,
|
|
verificationCode: verificationCode,
|
|
};
|
|
|
|
const newUser = await this.users.create(newUserData);
|
|
|
|
const signUpUrl = "https://turnosxpress.com.ar/landing/login/verification";
|
|
|
|
await NotificationsManager.sendEmail({
|
|
email: cleanEmail(email),
|
|
subject: "TurnosXpress :: Bienvenido/a",
|
|
message: `Hola ${firstName}, Gracias por registrarte en turnosXpress. Primero debes activar tu cuenta ingresando en ${signUpUrl} . Tu código de verificación es: ${verificationCode}`,
|
|
});
|
|
|
|
return newUser;
|
|
}
|
|
|
|
public async recoveryAccount(data: RecoveryAccountParams): Promise<void> {
|
|
const validationProfile = Joi.object({
|
|
email: Joi.string().email().required().messages({
|
|
"string.email": "El correo ingresado no es valido",
|
|
"any.required": "El correo es requerido",
|
|
"string.empty": "El correo es requerido",
|
|
}),
|
|
recoveryCode: Joi.string().length(6).optional().messages({
|
|
"string.length": "El código de verificacion debe tener 6 digitos",
|
|
}),
|
|
newPassword: Joi.string().alphanum().min(8).optional(),
|
|
sendRecoveryCode: Joi.boolean().required().messages({
|
|
"any.required": "Faltan parametros requeridos",
|
|
"any.empty": "Faltan parametros requeridos",
|
|
}),
|
|
});
|
|
|
|
// Validate the data using the validation profile
|
|
const { error } = validationProfile.validate(data, {
|
|
abortEarly: false,
|
|
});
|
|
|
|
// Throw an error if validation fails
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
const user = await this.users.findOne({ email: cleanEmail(data.email) });
|
|
|
|
if (!user) {
|
|
throw new Error("El email ingresado no se encuentra registrado");
|
|
}
|
|
|
|
if (data.sendRecoveryCode) {
|
|
const code = this.getVerificationCode();
|
|
|
|
user.recoveryCode = code;
|
|
|
|
await user.save();
|
|
|
|
await NotificationsManager.sendEmail({
|
|
email: cleanEmail(data.email),
|
|
subject: "TurnosXpress :: Recuperación de cuenta",
|
|
message: `Hola ${user.firstName}, Te enviamos un código de verificación para recuperar tu cuenta. Tu código de verificación es: ${code}. Ingresa https://turnosxpress.com.ar/landing/recover-account/verify para establecer una nueva clave`,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
if (!data.recoveryCode) {
|
|
throw new Error("Debes ingresar el código de verificación");
|
|
}
|
|
|
|
if (user.recoveryCode !== data.recoveryCode) {
|
|
throw new Error("El código de verificación es incorrecto");
|
|
}
|
|
|
|
if (!data.newPassword) {
|
|
throw new Error("Debes ingresar una nueva clave");
|
|
}
|
|
|
|
const passwordCifrado = await hashPassword(data.newPassword);
|
|
|
|
user.password = passwordCifrado;
|
|
user.recoveryCode = undefined;
|
|
await user.save();
|
|
}
|
|
|
|
public async verificateAndLoginUser(data: VerificationUserParams): Promise<ISession> {
|
|
const { email, password, verificationCode } = data;
|
|
|
|
// Define a validation profile using Joi
|
|
const validationProfile = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
verificationCode: Joi.string().required(),
|
|
password: Joi.string().alphanum().min(8).required(),
|
|
});
|
|
|
|
// Validate the data using the validation profile
|
|
const { error } = validationProfile.validate(data, {
|
|
abortEarly: false,
|
|
});
|
|
|
|
// Throw an error if validation fails
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
const userTest = await this.users.findOne({ email: cleanEmail(email) });
|
|
|
|
if (!userTest) {
|
|
throw new Error("Usuario no encontrado");
|
|
}
|
|
|
|
if (userTest.verificationCode !== verificationCode) {
|
|
throw new Error("El código de verificación es incorrecto");
|
|
}
|
|
|
|
userTest.verificated = true;
|
|
|
|
await userTest.save();
|
|
|
|
try {
|
|
const freePlan = await PlansList.plans.findOne({ price: 0 });
|
|
if (freePlan) {
|
|
const hasSub = await PlanSubscriptionsList.planSuscriptions.findOne({ sessionUser: String(userTest._id) });
|
|
if (!hasSub) {
|
|
const startDate = new Date();
|
|
const endDate = new Date(startDate.getTime() + 10 * 365 * 24 * 60 * 60 * 1000); // 10 years
|
|
await PlanSubscriptionsList.planSuscriptions.create({
|
|
userId: String(userTest._id),
|
|
planId: String(freePlan._id),
|
|
mpPlanId: String(freePlan._id),
|
|
startDate: startDate,
|
|
endDate: endDate,
|
|
autoRenew: true,
|
|
mpStatus: "authorized",
|
|
mpInitPoint: "/landing/dashboard"
|
|
});
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error("Error asignando plan gratuito:", e);
|
|
}
|
|
|
|
return await this.login({
|
|
email: cleanEmail(email),
|
|
password: password,
|
|
});
|
|
}
|
|
|
|
public async login(data: LoginParams): Promise<ISession> {
|
|
const { email, password } = data;
|
|
|
|
// Define a validation profile using Joi
|
|
const validationProfile = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
password: Joi.string().alphanum().min(8).required(),
|
|
});
|
|
|
|
// Validate the data using the validation profile
|
|
const { error } = validationProfile.validate(data, {
|
|
abortEarly: false,
|
|
});
|
|
|
|
// Throw an error if validation fails
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
const userTest = await this.users.findOne({ email: cleanEmail(email) });
|
|
|
|
if (!userTest) {
|
|
throw new Error("Usuario no encontrado");
|
|
}
|
|
|
|
if (!userTest.password) {
|
|
throw new Error("Clave no proporcionada");
|
|
}
|
|
|
|
const passwordTest = await bcrypt.compare(password, userTest.password);
|
|
|
|
if (!passwordTest) {
|
|
throw new Error("Credenciales invalidas");
|
|
}
|
|
|
|
if (!userTest.verificated) {
|
|
throw new Error("Debes verificar tu cuenta. Revisa la bandeja de entrada de tu correo");
|
|
}
|
|
//....
|
|
|
|
userTest.lastLoginDate = new Date();
|
|
await userTest.save();
|
|
|
|
const subscription = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: userTest.id,
|
|
});
|
|
|
|
const metrics = await MetricsList.getMetrics(userTest.id);
|
|
|
|
const userFullName = joinStrings([userTest.firstName, userTest.lastName], " ");
|
|
|
|
// Create a payload object with session data
|
|
const payload: ISession = {
|
|
loged: true,
|
|
userEmail: cleanEmail(userTest.email),
|
|
userFullName: userFullName,
|
|
userId: isNull<string>(userTest.id, ""),
|
|
userToken: "",
|
|
avatar: getAvatar(userTest.id, userTest.avatar, userFullName),
|
|
subscription: subscription,
|
|
organizationSubscriptions: await this.getOrganizationSubscriptions(userTest.id),
|
|
metrics: metrics,
|
|
};
|
|
|
|
//console.log(payload);
|
|
|
|
// Generate a JWT token using the payload and a secret word
|
|
const token = jwt.sign(payload, `${process.env.SECRET_WORD}`, {
|
|
expiresIn: `${process.env.TOKEN_SESSION_TIME}` as any,
|
|
});
|
|
|
|
// Create a user object by merging the payload and the token
|
|
const user: ISession = { ...payload, ...{ userToken: token } };
|
|
|
|
// Return the user object
|
|
return user;
|
|
}
|
|
|
|
public async loginByToken({ token }: LoginByTokenParams): Promise<ISession> {
|
|
try {
|
|
// Verify the token using the secret word. This also decodes it.
|
|
const decodedPayload = jwt.verify(token, `${process.env.SECRET_WORD}`) as ISession & {
|
|
iat: number;
|
|
exp: number;
|
|
};
|
|
|
|
const userTest = await this.users.findOne({
|
|
email: cleanEmail(decodedPayload.userEmail),
|
|
});
|
|
|
|
if (!userTest) {
|
|
throw new Error("Ha ocurrido un error al reestablecer la sesión");
|
|
}
|
|
|
|
const subscription = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: decodedPayload.userId,
|
|
});
|
|
|
|
const metrics = await MetricsList.getMetrics(decodedPayload.userId);
|
|
|
|
const userFullName = joinStrings([userTest.firstName, userTest.lastName], " ");
|
|
|
|
// Create the session payload. This ensures data is up-to-date.
|
|
const payload: ISession = {
|
|
loged: true,
|
|
userEmail: cleanEmail(userTest.email),
|
|
userFullName: userFullName,
|
|
userId: isNull<string>(userTest.id, ""),
|
|
userToken: "", // Will be filled next
|
|
avatar: getAvatar(userTest.id, userTest.avatar, userFullName),
|
|
subscription: subscription,
|
|
organizationSubscriptions: await this.getOrganizationSubscriptions(decodedPayload.userId),
|
|
metrics: metrics,
|
|
};
|
|
|
|
const nowInSeconds = Math.floor(Date.now() / 1000);
|
|
const fiveMinutesInSeconds = 5 * 60;
|
|
let finalToken = token; // Assume we reuse the old token
|
|
|
|
// If the token expires in less than 5 minutes, generate a new one.
|
|
if (decodedPayload.exp < nowInSeconds + fiveMinutesInSeconds) {
|
|
finalToken = jwt.sign(payload, `${process.env.SECRET_WORD}`, {
|
|
expiresIn: `${process.env.TOKEN_SESSION_TIME}` as any,
|
|
});
|
|
}
|
|
|
|
// Return the session object with the appropriate token
|
|
return { ...payload, userToken: finalToken };
|
|
} catch (error) {
|
|
// Throw an error if the token is invalid or expired
|
|
throw new Error("Invalid token");
|
|
}
|
|
}
|
|
|
|
private async getGoogleInfo(data: LoginByGoogleParams): Promise<IGoogleData> {
|
|
const profileResponse = await axios.get("https://www.googleapis.com/oauth2/v2/userinfo", {
|
|
headers: {
|
|
Authorization: `Bearer ${data.token}`,
|
|
},
|
|
});
|
|
|
|
return profileResponse.data as IGoogleData;
|
|
}
|
|
|
|
public async loginByAndroid(data: LoginByGoogleParams): Promise<ISession> {
|
|
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
|
|
|
if (!GOOGLE_CLIENT_ID) {
|
|
throw new Error("Error en el servidor, no se ha configurado el CLIENT_ID de Google");
|
|
}
|
|
|
|
const client = new OAuth2Client(GOOGLE_CLIENT_ID);
|
|
|
|
const ticket = await client.verifyIdToken({
|
|
idToken: data.token,
|
|
audience: GOOGLE_CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
|
|
});
|
|
|
|
const payload = ticket.getPayload();
|
|
if (!payload) {
|
|
throw new Error("Invalid token");
|
|
}
|
|
|
|
const googleUserId = payload.sub;
|
|
const email = payload.email;
|
|
const name = payload.name;
|
|
|
|
if (!email) {
|
|
throw new Error("Invalid token");
|
|
}
|
|
|
|
const userCheck = await this.users.findOne({
|
|
email: cleanEmail(email),
|
|
});
|
|
|
|
if (userCheck) {
|
|
userCheck.external_id = googleUserId;
|
|
userCheck.external_service = "google";
|
|
userCheck.verificated = true;
|
|
await userCheck.save();
|
|
|
|
const subscription = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: userCheck.id,
|
|
});
|
|
|
|
const metrics = await MetricsList.getMetrics(userCheck.id);
|
|
|
|
const userFullName = joinStrings([userCheck.firstName, userCheck.lastName], " ");
|
|
|
|
const payload: ISession = {
|
|
loged: true,
|
|
userEmail: cleanEmail(userCheck.email),
|
|
userFullName: userFullName,
|
|
userId: isNull<string>(String(userCheck.id), ""),
|
|
userToken: "",
|
|
avatar: getAvatar(userCheck.id, userCheck.avatar, userFullName),
|
|
subscription: subscription,
|
|
organizationSubscriptions: await this.getOrganizationSubscriptions(String(userCheck.id)),
|
|
metrics: metrics,
|
|
};
|
|
|
|
// Generate a JWT token using the payload and a secret word
|
|
const token = jwt.sign(payload, `${process.env.SECRET_WORD}`, {
|
|
expiresIn: `${process.env.TOKEN_SESSION_TIME}` as any,
|
|
});
|
|
|
|
// Create a user object by merging the payload and the token
|
|
return { ...payload, ...{ userToken: token } };
|
|
} else {
|
|
const newUserData: IUser = {
|
|
email: cleanEmail(email),
|
|
firstName: name,
|
|
external_id: googleUserId,
|
|
external_service: "google",
|
|
};
|
|
|
|
const newUser = await this.users.create(newUserData);
|
|
|
|
if (!newUser.id) {
|
|
throw new Error("Error creating user");
|
|
}
|
|
|
|
const subscription = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: newUser.id,
|
|
});
|
|
|
|
const metrics = await MetricsList.getMetrics(newUser.id);
|
|
|
|
const userFullName = joinStrings([newUser.firstName, newUser.lastName], " ");
|
|
|
|
const payload: ISession = {
|
|
loged: true,
|
|
userEmail: cleanEmail(newUser.email),
|
|
userFullName: userFullName,
|
|
userId: isNull<string>(String(newUser.id), ""),
|
|
userToken: "",
|
|
avatar: getAvatar(
|
|
isNull<string>(String(newUser.id), ""),
|
|
newUser.avatar,
|
|
userFullName
|
|
),
|
|
subscription: subscription,
|
|
organizationSubscriptions: await this.getOrganizationSubscriptions(String(newUser.id)),
|
|
metrics: metrics,
|
|
};
|
|
|
|
// Generate a JWT token using the payload and a secret word
|
|
const token = jwt.sign(payload, `${process.env.SECRET_WORD}`, {
|
|
expiresIn: `${process.env.TOKEN_SESSION_TIME}` as any,
|
|
});
|
|
|
|
// Create a user object by merging the payload and the token
|
|
return { ...payload, ...{ userToken: token } };
|
|
}
|
|
}
|
|
|
|
public async loginByGoogle(data: LoginByGoogleParams): Promise<ISession> {
|
|
try {
|
|
const googleData = await this.getGoogleInfo(data);
|
|
|
|
//TODO: creo que debo verificar por email.
|
|
|
|
// const userCheck = await this.users.findOne({
|
|
// external_id: googleId,
|
|
// external_service: "google",
|
|
// email: email,
|
|
// });
|
|
|
|
const userCheck = await this.users.findOne({
|
|
email: cleanEmail(googleData.email),
|
|
});
|
|
|
|
if (userCheck) {
|
|
userCheck.external_id = googleData.id;
|
|
userCheck.external_service = "google";
|
|
userCheck.verificated = true;
|
|
await userCheck.save();
|
|
|
|
const subscription = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: userCheck.id,
|
|
});
|
|
|
|
const metrics = await MetricsList.getMetrics(userCheck.id);
|
|
|
|
const userFullName = joinStrings([userCheck.firstName, userCheck.lastName], " ");
|
|
|
|
const payload: ISession = {
|
|
loged: true,
|
|
userEmail: cleanEmail(userCheck.email),
|
|
userFullName: userFullName,
|
|
userId: isNull<string>(String(userCheck.id), ""),
|
|
userToken: "",
|
|
avatar: getAvatar(userCheck.id, userCheck.avatar, userFullName),
|
|
subscription: subscription,
|
|
organizationSubscriptions: await this.getOrganizationSubscriptions(String(userCheck.id)),
|
|
metrics: metrics,
|
|
};
|
|
|
|
// Generate a JWT token using the payload and a secret word
|
|
const token = jwt.sign(payload, `${process.env.SECRET_WORD}`, {
|
|
expiresIn: `${process.env.TOKEN_SESSION_TIME}` as any,
|
|
});
|
|
|
|
// Create a user object by merging the payload and the token
|
|
return { ...payload, ...{ userToken: token } };
|
|
} else {
|
|
const newUserData: IUser = {
|
|
email: cleanEmail(googleData.email),
|
|
firstName: googleData.name,
|
|
external_id: googleData.id,
|
|
external_service: "google",
|
|
};
|
|
|
|
const newUser = await this.users.create(newUserData);
|
|
|
|
if (!newUser.id) {
|
|
throw new Error("Error creating user");
|
|
}
|
|
|
|
const subscription = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: newUser.id,
|
|
});
|
|
|
|
const metrics = await MetricsList.getMetrics(newUser.id);
|
|
|
|
const userFullName = joinStrings([newUser.firstName, newUser.lastName], " ");
|
|
|
|
const payload: ISession = {
|
|
loged: true,
|
|
userEmail: cleanEmail(newUser.email),
|
|
userFullName: userFullName,
|
|
userId: isNull<string>(String(newUser.id), ""),
|
|
userToken: "",
|
|
avatar: getAvatar(
|
|
isNull<string>(String(newUser.id), ""),
|
|
newUser.avatar,
|
|
userFullName
|
|
),
|
|
subscription: subscription,
|
|
organizationSubscriptions: await this.getOrganizationSubscriptions(String(newUser.id)),
|
|
metrics: metrics,
|
|
};
|
|
|
|
// Generate a JWT token using the payload and a secret word
|
|
const token = jwt.sign(payload, `${process.env.SECRET_WORD}`, {
|
|
expiresIn: `${process.env.TOKEN_SESSION_TIME}` as any,
|
|
});
|
|
|
|
// Create a user object by merging the payload and the token
|
|
return { ...payload, ...{ userToken: token } };
|
|
}
|
|
|
|
return {} as ISession;
|
|
} catch (error) {
|
|
console.log(error);
|
|
throw new Error("An error occurred while trying to login with Google");
|
|
}
|
|
}
|
|
|
|
public async changePassword(data: ChangePasswordParams): Promise<void> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
let passwordTest = false;
|
|
|
|
if (sessionUser.password) {
|
|
passwordTest = await bcrypt.compare(data.oldPassword, sessionUser.password);
|
|
}
|
|
|
|
if (!passwordTest) {
|
|
throw new Error("Credenciales invalidas");
|
|
}
|
|
|
|
if (!sessionUser.verificated) {
|
|
throw new Error("Debes verificar tu cuenta. Revisa la bandeja de entrada de tu correo");
|
|
}
|
|
|
|
const validationProfile = Joi.object({
|
|
newPassword: Joi.string().alphanum().min(8).required(),
|
|
});
|
|
|
|
// Validate the data using the validation profile
|
|
const { error } = validationProfile.validate(
|
|
{ newPassword: data.newPassword },
|
|
{
|
|
abortEarly: true,
|
|
}
|
|
);
|
|
|
|
// Throw an error if validation fails
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
const passwordCifrado = await hashPassword(data.newPassword);
|
|
|
|
sessionUser.password = passwordCifrado;
|
|
sessionUser.recoveryCode = undefined;
|
|
await sessionUser.save();
|
|
|
|
await NotificationsManager.sendEmail({
|
|
email: cleanEmail(sessionUser.email),
|
|
subject: "TurnosXpress :: Contraseña actualizada",
|
|
message: `Hola ${sessionUser.firstName}, has actualizado tu contraseña con exito`,
|
|
});
|
|
}
|
|
|
|
public async paginate(filters: PaginateUsersParams): Promise<PaginateUsersResults> {
|
|
return await this.users.paginate(filters);
|
|
}
|
|
|
|
public async findOne(data: FindUsersParams): Promise<IUserView> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
return {
|
|
id: sessionUser.id,
|
|
email: cleanEmail(sessionUser.email),
|
|
firstName: sessionUser.firstName,
|
|
lastName: sessionUser.lastName,
|
|
fullName: this.getFullName(sessionUser),
|
|
avatar: getAvatar(sessionUser.id, sessionUser.avatar, this.getFullName(sessionUser)),
|
|
phoneNumber: isNull<string>(sessionUser.phoneNumber, ""),
|
|
phoneCountryCode: isNull<string>(sessionUser.phoneCountryCode, ""),
|
|
phoneAreaCode: isNull<string>(sessionUser.phoneAreaCode, ""),
|
|
formattedPhoneNumber: this.getFormattedPhoneNumber(sessionUser),
|
|
street: isNull<string>(sessionUser.street, ""),
|
|
streetNumber: isNull<string>(sessionUser.streetNumber, ""),
|
|
builingFloor: isNull<string>(sessionUser.builingFloor, ""),
|
|
buildingApartament: isNull<string>(sessionUser.buildingApartament, ""),
|
|
block: isNull<string>(sessionUser.block, ""),
|
|
city: isNull<string>(sessionUser.city, ""),
|
|
state: isNull<string>(sessionUser.state, ""),
|
|
country: isNull<string>(sessionUser.country, ""),
|
|
zipCode: isNull<string>(sessionUser.zipCode, ""),
|
|
};
|
|
}
|
|
|
|
public async findByEmail(
|
|
data: FindCollaboratorByEmailParams
|
|
): Promise<CollaboratorView | null> {
|
|
const user = await this.users.findOne({ email: cleanEmail(data.email) });
|
|
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
id: user.id,
|
|
email: cleanEmail(user.email),
|
|
fullName: this.getFullName(user),
|
|
avatar: getAvatar(user.id, user.avatar, this.getFullName(user)),
|
|
};
|
|
}
|
|
|
|
private getFormattedPhoneNumber(user: IUser): string {
|
|
return `(${isNull<string>(user.phoneCountryCode, "")})-${isNull<string>(
|
|
user.phoneAreaCode,
|
|
""
|
|
)}-${isNull<string>(user.phoneNumber, "")}`;
|
|
}
|
|
|
|
private getFullName(user: IUser): string {
|
|
return joinStrings([user.firstName, user.lastName], " ");
|
|
}
|
|
|
|
public async updateUser(data: UpdateUserParams): Promise<void> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
if (data.sessionUser != data.id) {
|
|
throw new Error(NoPermissionMessage());
|
|
}
|
|
|
|
await this.users.update(data);
|
|
|
|
await NotificationsManager.sendEmail({
|
|
email: cleanEmail(sessionUser.email),
|
|
subject: "TurnosXpress :: Usuario actualizado",
|
|
message: `Hola ${sessionUser.firstName}, has actualizado tu perfil con éxito`,
|
|
});
|
|
}
|
|
|
|
public async setUserIcon(data: SetUserFileParams): Promise<void> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
if (!data.file) {
|
|
throw new Error("No se ha enviado el archivo");
|
|
}
|
|
|
|
const rootDir =
|
|
process.env.NODE_ENV !== "production"
|
|
? process.env.UPLOAD_USERS_FILES_ROOT_DEV
|
|
: process.env.UPLOAD_USERS_FILES_ROOT_PRO;
|
|
|
|
const uploadPath = path.join(__dirname, rootDir + data.sessionUser);
|
|
|
|
if (!fs.existsSync(uploadPath)) {
|
|
fs.mkdirSync(uploadPath, { recursive: true });
|
|
}
|
|
|
|
const fileExtension = data.file.originalname.split(".").pop();
|
|
const newFileName = "avatar." + fileExtension;
|
|
|
|
const filePath = path.join(uploadPath, newFileName);
|
|
|
|
await sharp(data.file.buffer)
|
|
.resize({ width: 96, height: 96 })
|
|
.rotate()
|
|
.webp()
|
|
.toFile(filePath);
|
|
|
|
sessionUser.avatar = newFileName;
|
|
await sessionUser.save();
|
|
}
|
|
|
|
public async getUserAssistanceInfo(data: FindUsersParams): Promise<UserAssistanceInfo> {
|
|
const sessionUser = await this.users.findOne({ _id: data.sessionUser });
|
|
|
|
if (!sessionUser) {
|
|
throw new Error(
|
|
"No se ha encontrado el usuario o no tiene permisos para realizar esta acción"
|
|
);
|
|
}
|
|
|
|
if (!data.email) {
|
|
throw new Error("Debe proporcionar el email del usuario");
|
|
}
|
|
|
|
const returnValue: UserAssistanceInfo = {
|
|
message: "",
|
|
email: "",
|
|
fullName: "",
|
|
verificated: false,
|
|
activationCode: undefined,
|
|
recoveryCode: undefined,
|
|
};
|
|
|
|
const usserAssitance = await this.users.findOne({ email: cleanEmail(data.email) });
|
|
|
|
if (!usserAssitance) {
|
|
returnValue.message = "No se ha encontrado un usuario con ese email";
|
|
return returnValue;
|
|
}
|
|
|
|
if (usserAssitance.verificated && !usserAssitance.recoveryCode) {
|
|
returnValue.email = cleanEmail(usserAssitance.email);
|
|
returnValue.message =
|
|
"El usuario ya se encuentra activo. Si no puedes ingresar, intenta recuperar tu cuenta.";
|
|
returnValue.verificated = true;
|
|
return returnValue;
|
|
}
|
|
|
|
if (usserAssitance.verificated && usserAssitance.recoveryCode) {
|
|
returnValue.email = cleanEmail(usserAssitance.email);
|
|
returnValue.message =
|
|
"El usuario ya se encuentra activo y ha intentado cambiar su clave de acceso. Antes de proporcionar el código de recuperación, asegurate de verificar que se trate realmente del cliente, para evitar que una persona no autorizada acceda a su cuenta.";
|
|
returnValue.verificated = true;
|
|
returnValue.recoveryCode = usserAssitance.recoveryCode;
|
|
return returnValue;
|
|
}
|
|
|
|
returnValue.email = cleanEmail(usserAssitance.email);
|
|
returnValue.fullName = this.getFullName(usserAssitance);
|
|
returnValue.verificated = false;
|
|
returnValue.activationCode = usserAssitance.verificationCode;
|
|
returnValue.message = `El usuario no se encuentra activo.`;
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
// --- SysAdmin Methods ---
|
|
|
|
public async sysAdminPaginateUsers(
|
|
data: SysAdminPaginateUsersParams
|
|
): Promise<PaginateUsersResults> {
|
|
return await this.users.sysAdminPaginate(data);
|
|
}
|
|
|
|
public async sysAdminUpdateProfile(data: SysAdminUpdateProfileParams): Promise<void> {
|
|
const { userId, ...updateData } = data;
|
|
const user = await this.users.findOne({ _id: userId });
|
|
if (!user) {
|
|
throw new Error("Usuario no encontrado");
|
|
}
|
|
|
|
await this.users.update({
|
|
id: userId,
|
|
sessionUser: userId, // Mocking sessionUser because update params require it
|
|
firstName: updateData.firstName,
|
|
lastName: updateData.lastName,
|
|
street: updateData.street,
|
|
streetNumber: updateData.streetNumber,
|
|
builingFloor: updateData.builingFloor,
|
|
buildingApartament: updateData.buildingApartament,
|
|
block: updateData.block,
|
|
city: updateData.city,
|
|
state: updateData.state,
|
|
country: updateData.country,
|
|
zipCode: updateData.zipCode,
|
|
phoneCountryCode: updateData.phoneCountryCode,
|
|
phoneAreaCode: updateData.phoneAreaCode,
|
|
phoneNumber: updateData.phoneNumber,
|
|
});
|
|
}
|
|
|
|
public async sysAdminSetVerifiedStatus(data: SysAdminSetVerifiedParams): Promise<void> {
|
|
const user = await this.users.findOne({ _id: data.userId });
|
|
if (!user) {
|
|
throw new Error("Usuario no encontrado");
|
|
}
|
|
user.verificated = data.isVerified;
|
|
if (data.isVerified) {
|
|
user.verificationCode = undefined;
|
|
}
|
|
await user.save();
|
|
}
|
|
|
|
public async sysAdminDeleteUser(data: SysAdminDeleteUserParams): Promise<void> {
|
|
const userCheck = await this.users.findOne({ _id: data.userId });
|
|
|
|
if (!userCheck) {
|
|
throw new Error("El usuario que intenta dar de baja no existe.");
|
|
}
|
|
|
|
const ownerOrgs = await CompaniesList.companies.find({
|
|
ownerId: data.userId,
|
|
});
|
|
|
|
//No puede tener organizaciones creadas.
|
|
if (ownerOrgs.length > 0) {
|
|
throw new Error(
|
|
"Para dar de baja al usuario primero tienes que eliminar las organizaciones que creó (manualmente)."
|
|
);
|
|
}
|
|
|
|
//No debe tener una suscripcion.
|
|
const sub = await PlanSubscriptionsList.getSubscriptionByUser({
|
|
sessionUser: data.userId,
|
|
});
|
|
|
|
if (sub) {
|
|
if (sub.plan.price > 0) {
|
|
throw new Error("Para dar de baja al usuario primero tienes que cancelar su suscripción (manualmente).");
|
|
}
|
|
}
|
|
|
|
try {
|
|
//Si es colaborador en una o mas organizaciones, tenemos que:
|
|
const employees = await EmployeesList.employees.find({
|
|
userId: data.userId,
|
|
});
|
|
|
|
for (const employee of employees) {
|
|
const organization = await CompaniesList.companies.findOne({
|
|
_id: employee.companyId,
|
|
});
|
|
|
|
if (!organization) continue;
|
|
|
|
const collaboratorUser = await this.users.findOne({
|
|
_id: employee.userId,
|
|
});
|
|
|
|
if (!collaboratorUser) continue;
|
|
|
|
await SchedulesList.deleteSchedulesByEmployee({ employeeId: String(employee.id) });
|
|
await SchedulesEnabledList.deleteSchedulesEnabledByEmployee({ employeeId: String(employee.id) });
|
|
await SchedulesDisabledList.deleteSchedulesDisabledByEmployee({ employeeId: String(employee.id) });
|
|
await EmployeesServicesList.deleteEmployeeServiceByEmployee({ employeeId: String(employee.id) });
|
|
|
|
const appointments = await AppointmentList.Appointments.find({ employeeId: String(employee.id) });
|
|
for (const appointment of appointments) {
|
|
await AppointmentList.changeEmployeeToOwner({
|
|
appointmentId: String(appointment.id),
|
|
companyId: String(employee.companyId),
|
|
});
|
|
}
|
|
|
|
await HeatMapList.deleteHeatMapByEmployee({ employeeId: String(employee.id) });
|
|
await EmployeesList.deleteEmployee({ employeeId: String(employee.id) });
|
|
await MetricsList.calculateMetrics({ userId: String(organization.ownerId) });
|
|
|
|
await SystemNoficationsList.createNotification({
|
|
userId: String(organization.ownerId),
|
|
subject: "Colaborador eliminado por SysAdmin",
|
|
message: `El colaborador ${this.getUserFullName(collaboratorUser)} ha sido dado de baja por la administración y sus turnos se han transferido a ti.`,
|
|
});
|
|
}
|
|
|
|
await RepeatsList.deleteRepeatsByUser({ userId: data.userId });
|
|
|
|
const clients = await ClientsList.clients.find({ userId: data.userId });
|
|
for (const client of clients) {
|
|
if (!client.id) continue;
|
|
|
|
await ClientsList.clients.detachUser({ clientId: String(client.id) });
|
|
}
|
|
|
|
await MessageConversationList.deleteConversationsByUser({ userId: data.userId });
|
|
await PlanSubscriptionsList.deletePlanSuscriptionByUser({ sessionUser: data.userId });
|
|
await SystemNoficationsList.deleteNotificationsByUser({ userId: data.userId });
|
|
await this.deleteUserFiles(data.userId);
|
|
|
|
await this.users.delete(data.userId);
|
|
} catch (e) {
|
|
console.log("Error al eliminar el usuario (SysAdmin):", e);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
public async sysAdminOrganizationsStatus(
|
|
data: SysAdminOrganizationsStatusParams
|
|
): Promise<SysAdminUserOrganizationsResult> {
|
|
const result: SysAdminUserOrganizationsResult = {
|
|
clientIn: [],
|
|
collaboratorIn: [],
|
|
};
|
|
|
|
const clients = await ClientsList.clients.find({ userId: data.userId });
|
|
for (const client of clients) {
|
|
const org = await CompaniesList.companies.findOne({ _id: String(client.companyId) });
|
|
if (org) {
|
|
result.clientIn.push({ companyId: String(org.id), companyName: org.name });
|
|
}
|
|
}
|
|
|
|
const employees = await EmployeesList.employees.find({ userId: data.userId });
|
|
for (const emp of employees) {
|
|
const org = await CompaniesList.companies.findOne({ _id: String(emp.companyId) });
|
|
if (org) {
|
|
result.collaboratorIn.push({ companyId: String(org.id), companyName: org.name });
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
const UserList = new UsersManager();
|
|
|
|
export default UserList;
|