first commit

This commit is contained in:
2026-07-16 20:48:43 -03:00
commit 5696cee264
1111 changed files with 322270 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
import bcrypt from "bcryptjs";
const hashPassword = async (password: string, saltLength = 12) => {
const salt = await bcrypt.genSalt(saltLength);
const hashedPassword = await bcrypt.hash(password, salt);
return hashedPassword;
};
export default hashPassword;
+3
View File
@@ -0,0 +1,3 @@
export function isNull<Type>(data: any, returnValue: Type): Type {
return data ? data : returnValue;
};
@@ -0,0 +1,5 @@
const NoPermissionMessage = () => {
return "No tienes permisos para realizar esta acción.";
};
export { NoPermissionMessage };
+11
View File
@@ -0,0 +1,11 @@
import { isNull } from "./IsNull";
export function joinStrings(params: any[], concat: string): string {
let returnValue: string = "";
params.forEach((param) => {
const value = isNull<string>(param, concat);
returnValue += value + " ";
});
return returnValue.trimEnd();
}
+165
View File
@@ -0,0 +1,165 @@
import EmployeesList from "../Models/Employees/Employee";
import { EmployeeRoles } from "../Models/Employees/Employees.Interface";
import UserList from "../Models/Users/Users";
import { IUserDocument } from "../Models/Users/Users.Adapter.Mongoose";
import { NoPermissionMessage } from "./NoPermissionMessage";
import CompaniesList from "../Models/Companies/Companies";
import { ICompanyDocument } from "../Models/Companies/Companies.Adapter.Mongoose";
import ClientsList from "../Models/Clients/Clients";
import { PlanFeatures } from "../Models/Plans/Plans.interface";
import PlanSubscriptionsList from "../Models/PlanSubscriptions/PlanSubscriptons";
import { IClientDocument } from "../Models/Clients/Clients.Adapter.Mongoose";
export type ValidateSessionUserParams = {
sessionUser: string;
};
export type ValidateSessionUserResult = {
user: IUserDocument;
};
export async function validateSessionUser(
data: ValidateSessionUserParams
): Promise<ValidateSessionUserResult> {
const sessionUser = await UserList.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 { user: sessionUser };
}
export type ValidatePermissionsByCompanyParams = {
companyId: string;
sessionUser: string;
};
export type ValidatePermissionsByCompanyResult = {
company: ICompanyDocument;
};
export async function validatePermissionsByCompany(
data: ValidatePermissionsByCompanyParams
): Promise<ValidatePermissionsByCompanyResult> {
const company = await CompaniesList.companies.findOne({
_id: data.companyId,
});
if (!company) {
throw new Error("No se ha encontrado la organización");
}
const hasAdminPermission = await EmployeesList.checkRoleById(
data.companyId,
data.sessionUser,
EmployeeRoles.ADMIN
);
if (!hasAdminPermission) {
throw new Error(NoPermissionMessage());
}
return { company };
}
export type ValidatePermissionsByClientParams = {
sessionUser: string;
feature?: PlanFeatures;
clientId: string;
};
export type ValidationPermissionsByClientResult = {
client: IClientDocument;
company: ICompanyDocument;
};
export async function validatePermissionsByClient(
data: ValidatePermissionsByClientParams
): Promise<ValidationPermissionsByClientResult> {
const client = await ClientsList.clients.findOne({ _id: data.clientId });
if (!client) {
throw new Error("No se ha encontrado el cliente");
}
const clientCompany = await CompaniesList.companies.findOne({
_id: String(client.companyId),
});
if (!clientCompany) {
throw new Error("No se ha encontrado la empresa del cliente");
}
// Verificar si el usuario de sesión es un empleado con permisos de administrador
const hasAdminPermission = await EmployeesList.checkRoleById(
clientCompany.id,
data.sessionUser,
EmployeeRoles.ADMIN
);
if (!hasAdminPermission) {
throw new Error(NoPermissionMessage());
}
if (data.feature) {
const canAccess = await PlanSubscriptionsList.checkFeature({
userId: String(clientCompany.ownerId),
feature: data.feature,
});
if (!canAccess) {
throw new Error("La organizacion no tiene un plan que soporte el módulo de pagos");
}
}
return { client, company: clientCompany };
}
export type GetAuthorizedUsersParams = {
companyId: string;
};
export type GetAuthorizedUsersResult = {
companyId: string;
authorizedUsers: string[];
};
export async function getAuthorizedUsers(
data: GetAuthorizedUsersParams
): Promise<GetAuthorizedUsersResult> {
const company = await CompaniesList.companies.findOne({
_id: data.companyId,
});
if (!company) {
throw new Error("No se ha encontrado la organización");
}
const employees = await EmployeesList.findByCompanyId({
companyId: data.companyId,
});
let returnValue: string[] = [];
for (const employee of employees) {
const hasAdminPermission = await EmployeesList.checkRoleById(
data.companyId,
String(employee.userId),
EmployeeRoles.ADMIN
);
if (hasAdminPermission) {
returnValue.push(String(employee.userId));
}
}
return {
companyId: company.id,
authorizedUsers: returnValue,
};
}
+11
View File
@@ -0,0 +1,11 @@
export const formatCurrency = (value: number): string => {
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "ARS",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
// Reemplazamos el símbolo "ARS" o "ARS$" por "$"
return formatter.format(value).replace(/^ARS\s?/, "$");
};
+27
View File
@@ -0,0 +1,27 @@
import fs from "fs";
import path from "path";
export default function getAvatar(
userId: string,
avatar: string | null | undefined,
fallback: string
): string {
const rootPath = process.env.PUBLIC_AVATAR_URL;
if (userId == null) return fallback;
if (avatar == undefined || avatar == null || avatar === "") return fallback;
const rootDir =
process.env.NODE_ENV !== "production"
? process.env.GET_AVATAR_USERS_FILES_ROOT_DEV
: process.env.GET_AVATAR_USERS_FILES_ROOT_PRO;
const uploadPath = path.join(__dirname, rootDir + userId);
const filePath = path.join(uploadPath, avatar);
if (!fs.existsSync(filePath)) {
return fallback;
}
return avatar ? rootPath + userId + "/" + avatar : "";
}
+27
View File
@@ -0,0 +1,27 @@
import fs from "fs";
import path from "path";
export default function getHeader(
companyId: string,
header: string | null | undefined,
fallback: string
): string {
const rootPath = process.env.PUBLIC_HEADER_URL;
if (companyId == null) return fallback;
if (header == undefined || header == null || header === "") return fallback;
const rootDir =
process.env.NODE_ENV !== "production"
? process.env.GET_HEADER_COMPANIES_FILES_ROOT_DEV
: process.env.GET_HEADER_COMPANIES_FILES_ROOT_PRO;
const uploadPath = path.join(__dirname, rootDir + companyId);
const filePath = path.join(uploadPath, header);
if (!fs.existsSync(filePath)) {
return fallback;
}
return header ? rootPath + companyId + "/" + header : "";
}
+27
View File
@@ -0,0 +1,27 @@
import fs from "fs";
import path from "path";
export default function getServiceImage(
serviceId: string,
image: string | null | undefined,
fallback: string
): string {
const rootPath = process.env.PUBLIC_SERVICE_IMAGE_URL;
if (serviceId == null) return fallback;
if (image == undefined || image == null || image === "") return fallback;
const rootDir =
process.env.NODE_ENV !== "production"
? process.env.GET_SERVICE_IMAGE_FILES_ROOT_DEV
: process.env.GET_SERVICE_IMAGE_FILES_ROOT_PRO;
const uploadPath = path.join(__dirname, rootDir + serviceId);
const filePath = path.join(uploadPath, image);
if (!fs.existsSync(filePath)) {
return fallback;
}
return image ? rootPath + serviceId + "/" + image : "";
}
@@ -0,0 +1,13 @@
const getTimeStringFromNumber = (decimalHours: number): string => {
// Calcula las horas y minutos
const hours = Math.floor(decimalHours);
const minutes = Math.round((decimalHours - hours) * 60);
// Asegúrate de que el formato sea siempre de dos dígitos
const paddedHours = hours.toString().padStart(2, "0");
const paddedMinutes = minutes.toString().padStart(2, "0");
return `${paddedHours}:${paddedMinutes}`;
};
export { getTimeStringFromNumber };
+26
View File
@@ -0,0 +1,26 @@
import sharp from "sharp";
/**
* Resizes and crops an image to the specified dimensions.
*
* @param {sharp.Sharp} img - The image to be resized and cropped.
* @param {number} width - The desired width of the output image.
* @param {number} height - The desired height of the output image.
* @return {Promise<sharp.Sharp>} A promise that resolves to the resized and cropped image.
*/
const resizeAndCrop = async (
img: sharp.Sharp,
width: number,
height: number
): Promise<sharp.Sharp> => {
const buffer = await img
.resize(width, height, {
fit: "cover", // rellena todo el espacio, recorta si es necesario
position: "centre", // centra el recorte (opcional, es el valor por defecto)
})
.toBuffer();
return sharp(buffer);
};
export { resizeAndCrop };