Saltar a contenido

CRUD: la base de la gestión de datos

El concepto CRUD está estrechamente vinculado a la gestión de datos digitales. CRUD hace referencia a un acrónimo en el que se reúnen las primeras letras de las cuatro operaciones fundamentales de aplicaciones persistentes en sistemas de bases de datos:

  • Create (Crear registros)

  • Read bzw. Retrieve (Leer registros)

  • Update (Actualizar registros)

  • Delete bzw. Destroy (Borrar registros)

En pocas palabras, CRUD resume las funciones requeridas por un usuario para crear y gestionar datos. Varios procesos de gestión de datos están basados en CRUD, en los que dichas operaciones están específicamente adaptadas a los requisitos del sistema y de usuario, ya sea para la gestión de bases de datos o para el uso de aplicaciones. Para los expertos, las operaciones son las herramientas de acceso típicas e indispensables para comprobar, por ejemplo, los problemas de la base de datos, mientras que para los usuarios, CRUD significa crear una cuenta (créate) y utilizarla (read), actualizarla (update) o borrarla (delete) en cualquier momento. Dependiendo de la configuración regional, las operaciones CRUD pueden implementarse de diferentes maneras, como lo muestra la siguiente tabla:

CRUD-Operation SQL RESTful HTTP XQuery
Create INSERT POST, PUT insert
Read SELECT GET, HEAD copy/modify/return
Update UPDATE PUT, PATCH replace, rename
Delete DELETE DELETE delete

Debemos crear los 6 metodos por cada modelo segun demande los requerimientos del aplicativo.

  • Buscar todos los registros

  • Buscar por Id

  • Crear o Insertar un Registro

  • Actualizar o Cambiar el valor de uno o varios registos

  • Eliminar fisicamente uno o varios registros

  • Eliminar Logicamente uno o varios registros

Para ello se realizan los controladores y las rutas de de cada modelo, al igual que las configuraciones de la rutas de la siguiente manera:

Paso 1: Controladores

1.1 Controladores de los modelos de Autorizacion

1.1.1 Controlador User

src/controllers/Authorization/user.controller.ts

import { Request, Response } from 'express';
import { User, UserI } from '../../models/authorization/User';

export class UserController {
  public async getAllUsers(req: Request, res: Response): Promise<void> {
    try {
      const users: UserI[] = await User.findAll();
      res.status(200).json({ users });
    } catch (error) {
      res.status(500).json({ error: 'Error al obtener los usuarios' });
    }
  }
}
1.1.2 Controlador Role

src/controllers/Authorization/role.controller.ts

import { Request, Response } from 'express';
import { Role, RoleI } from '../../models/authorization/Role';

export class RoleController {
  // Obtener todos los roles
  public async getAllRoles(req: Request, res: Response): Promise<void> {
    try {
      const roles: RoleI[] = await Role.findAll();
      res.status(200).json({ roles });
    } catch (error) {
      res.status(500).json({ error: 'Error al obtener los roles' });
    }
  }

  // Obtener un rol por ID
  public async getRoleById(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const role = await Role.findOne({ where: { id, is_active: "ACTIVE" } });
      if (role) {
        res.status(200).json(role);
      } else {
        res.status(404).json({ error: 'Rol no encontrado o inactivo' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Error al obtener el rol' });
    }
  }

  // Crear un nuevo rol
  public async createRole(req: Request, res: Response): Promise<void> {
    const { name, is_active } = req.body;
    try {
      const newRole = await Role.create({ name, is_active });
      res.status(201).json(newRole);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Actualizar un rol
  public async updateRole(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    const { name, is_active } = req.body;
    try {
      const role = await Role.findOne({ where: { id, is_active: "ACTIVE" } });
      if (role) {
        await role.update({ name, is_active });
        res.status(200).json(role);
      } else {
        res.status(404).json({ error: 'Rol no encontrado o inactivo' });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Eliminar un rol físicamente
  public async deleteRole(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const role = await Role.findByPk(id);
      if (role) {
        await role.destroy();
        res.status(200).json({ message: 'Rol eliminado correctamente' });
      } else {
        res.status(404).json({ error: 'Rol no encontrado' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Error al eliminar el rol' });
    }
  }

  // Eliminar un rol lógicamente
  public async deleteRoleAdv(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const role = await Role.findOne({ where: { id, is_active: "ACTIVE" } });
      if (role) {
        await role.update({ is_active: "INACTIVE" });
        res.status(200).json({ message: 'Rol marcado como inactivo' });
      } else {
        res.status(404).json({ error: 'Rol no encontrado' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Error al marcar el rol como inactivo' });
    }
  }
}
1.1.3 Controlador RoleUser

src/controllers/Authorization/role_user.controller.ts

import { Request, Response } from 'express';
import { RoleUser, RoleUserI } from '../../models/authorization/RoleUser';

export class RoleUserController {
  // Obtener todos los RoleUsers
  public async getAllRoleUsers(req: Request, res: Response): Promise<void> {
    try {
      const roleUsers: RoleUserI[] = await RoleUser.findAll();
      res.status(200).json({ roleUsers });
    } catch (error) {
      res.status(500).json({ error: 'Error al obtener los usuarios de roles' });
    }
  }

  // Obtener un RoleUser por ID
  public async getRoleUserById(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const roleUser = await RoleUser.findOne({ where: { id, is_active: "ACTIVE" } });
      if (roleUser) {
        res.status(200).json(roleUser);
      } else {
        res.status(404).json({ error: 'RoleUser no encontrado o inactivo' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Error al obtener el RoleUser' });
    }
  }

  // Crear un nuevo RoleUser
  public async createRoleUser(req: Request, res: Response): Promise<void> {
    const { role_id, user_id, is_active } = req.body;
    try {
      const newRoleUser = await RoleUser.create({ role_id, user_id, is_active });
      res.status(201).json(newRoleUser);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Actualizar un RoleUser
  public async updateRoleUser(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    const { role_id, user_id, is_active } = req.body;
    try {
      const roleUser = await RoleUser.findOne({ where: { id, is_active: "ACTIVE" } });
      if (roleUser) {
        await roleUser.update({ role_id, user_id, is_active });
        res.status(200).json(roleUser);
      } else {
        res.status(404).json({ error: 'RoleUser no encontrado o inactivo' });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Eliminar un RoleUser físicamente
  public async deleteRoleUser(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const roleUser = await RoleUser.findByPk(id);
      if (roleUser) {
        await roleUser.destroy();
        res.status(200).json({ message: 'RoleUser eliminado correctamente' });
      } else {
        res.status(404).json({ error: 'RoleUser no encontrado' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Error al eliminar el RoleUser' });
    }
  }

  // Eliminar un RoleUser lógicamente
  public async deleteRoleUserAdv(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const roleUser = await RoleUser.findOne({ where: { id, is_active: "ACTIVE" } });
      if (roleUser) {
        await roleUser.update({ is_active: "INACTIVE" });
        res.status(200).json({ message: 'RoleUser marcado como inactivo' });
      } else {
        res.status(404).json({ error: 'RoleUser no encontrado' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Error al marcar el RoleUser como inactivo' });
    }
  }
}
1.1.4 Controlador Resource

src/controllers/Authorization/resource.controller.ts

import { Request, Response } from "express";
import { Resource, ResourceI } from "../../models/authorization/Resource";

export class ResourceController {
  // Obtener todos los recursos
  public async getAllResources(req: Request, res: Response): Promise<void> {
    try {
      const resources: ResourceI[] = await Resource.findAll({
        where: { is_active: "ACTIVE" },
      });
      res.status(200).json({ resources });
    } catch (error) {
      res.status(500).json({ error: "Error al obtener los recursos" });
    }
  }

  // Obtener un recurso por ID
  public async getResourceById(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const resource = await Resource.findOne({
        where: { id, is_active: "ACTIVE" },
      });
      if (resource) {
        res.status(200).json(resource);
      } else {
        res.status(404).json({ error: "Recurso no encontrado o inactivo" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error al obtener el recurso" });
    }
  }

  // Crear un nuevo recurso
  public async createResource(req: Request, res: Response): Promise<void> {
    const { path, method, is_active } = req.body;
    try {
      const newResource = await Resource.create({ path, method, is_active });
      res.status(201).json(newResource);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Actualizar un recurso
  public async updateResource(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    const { path, method, is_active } = req.body;
    try {
      const resource = await Resource.findOne({ where: { id, is_active: "ACTIVE" } });
      if (resource) {
        await resource.update({ path, method, is_active });
        res.status(200).json(resource);
      } else {
        res.status(404).json({ error: "Recurso no encontrado o inactivo" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Eliminar un recurso físicamente
  public async deleteResource(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const resource = await Resource.findByPk(id);
      if (resource) {
        await resource.destroy();
        res.status(200).json({ message: "Recurso eliminado correctamente" });
      } else {
        res.status(404).json({ error: "Recurso no encontrado" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error al eliminar el recurso" });
    }
  }

  // Eliminar un recurso lógicamente
  public async deleteResourceAdv(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const resource = await Resource.findOne({ where: { id, is_active: "ACTIVE" } });
      if (resource) {
        await resource.update({ is_active: "INACTIVE" });
        res.status(200).json({ message: "Recurso marcado como inactivo" });
      } else {
        res.status(404).json({ error: "Recurso no encontrado" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error al marcar el recurso como inactivo" });
    }
  }
}
1.1.5 Controlador ResourceRole

src/controllers/Authorization/resourceRole.controller.ts

import { Request, Response } from "express";
import { ResourceRole, ResourceRoleI } from "../../models/authorization/ResourceRole";

export class ResourceRoleController {
  // Obtener todos los ResourceRoles
  public async getAllResourceRoles(req: Request, res: Response): Promise<void> {
    try {
      const resourceRoles: ResourceRoleI[] = await ResourceRole.findAll({
        where: { is_active: "ACTIVE" },
      });
      res.status(200).json({ resourceRoles });
    } catch (error) {
      res.status(500).json({ error: "Error al obtener los ResourceRoles" });
    }
  }

  // Obtener un ResourceRole por ID
  public async getResourceRoleById(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const resourceRole = await ResourceRole.findOne({
        where: { id, is_active: "ACTIVE" },
      });
      if (resourceRole) {
        res.status(200).json(resourceRole);
      } else {
        res.status(404).json({ error: "ResourceRole no encontrado o inactivo" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error al obtener el ResourceRole" });
    }
  }

  // Crear un nuevo ResourceRole
  public async createResourceRole(req: Request, res: Response): Promise<void> {
    const { resource_id, role_id, is_active } = req.body;
    try {
      const newResourceRole = await ResourceRole.create({ resource_id, role_id, is_active });
      res.status(201).json(newResourceRole);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Actualizar un ResourceRole
  public async updateResourceRole(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    const { resource_id, role_id, is_active } = req.body;
    try {
      const resourceRole = await ResourceRole.findOne({ where: { id, is_active: "ACTIVE" } });
      if (resourceRole) {
        await resourceRole.update({ resource_id, role_id, is_active });
        res.status(200).json(resourceRole);
      } else {
        res.status(404).json({ error: "ResourceRole no encontrado o inactivo" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Eliminar un ResourceRole físicamente
  public async deleteResourceRole(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const resourceRole = await ResourceRole.findByPk(id);
      if (resourceRole) {
        await resourceRole.destroy();
        res.status(200).json({ message: "ResourceRole eliminado correctamente" });
      } else {
        res.status(404).json({ error: "ResourceRole no encontrado" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error al eliminar el ResourceRole" });
    }
  }

  // Eliminar un ResourceRole lógicamente
  public async deleteResourceRoleAdv(req: Request, res: Response): Promise<void> {
    const { id } = req.params;
    try {
      const resourceRole = await ResourceRole.findOne({ where: { id, is_active: "ACTIVE" } });
      if (resourceRole) {
        await resourceRole.update({ is_active: "INACTIVE" });
        res.status(200).json({ message: "ResourceRole marcado como inactivo" });
      } else {
        res.status(404).json({ error: "ResourceRole no encontrado" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error al marcar el ResourceRole como inactivo" });
    }
  }
}
1.1.6 Controlador RefreshToken

src/controllers/Authorization/refresh_token.controller.ts

import { Request, Response } from 'express';
import { RefreshToken, RefreshTokenI } from '../../models/authorization/RefreshToken';

export class RefreshTokenController {
  public async getAllRefreshToken(req: Request, res: Response): Promise<void> {
    try {
      const referes_tokens: RefreshTokenI[] = await RefreshToken.findAll();
      res.status(200).json({ referes_tokens });
    } catch (error) {
      res.status(500).json({ error: 'Error al obtener los los refresh token' });
    }
  }
}

1.2 Controladores de los modelos de Negocio

1.2.1 Controlador Client

src/controllers/client.controller.ts

import { Request, Response } from "express";
import { Client, ClientI } from "../models/Client";

export class ClientController {
  // Get all clients with status "ACTIVE"
  public async getAllClients(req: Request, res: Response) {
    try {
      const clients: ClientI[] = await Client.findAll({
        where: { status: 'ACTIVE' },
      });
      res.status(200).json({ clients });
    } catch (error) {
      res.status(500).json({ error: "Error fetching clients" });
    }
  }

  // Get a client by ID

  public async getClientById(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const client = await Client.findOne({
        where: { 
          id: pk, 
          status: 'ACTIVE' },
      });
      if (client) {
        res.status(200).json({client});
      } else {
        res.status(404).json({ error: "Client not found or inactive" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error fetching client" });
    }
  }

  // Create a new client
  public async createClient(req: Request, res: Response) {
    const { id, name, address, phone, email, password, status } = req.body;
    try {
      let body: ClientI = {
        name,
        address,
        phone,
        email,
        password,
        status,
      };

      const newClient = await Client.create({ ...body });
      res.status(201).json(newClient);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Update a client
  public async updateClient(req: Request, res: Response) {
    const { id: pk } = req.params;
    const { id, name, address, phone, email, password, status } = req.body;
    try {
      let body: ClientI = {
        name,
        address,
        phone,
        email,
        password,
        status,
      };

      const clientExist = await Client.findOne({
        where: { 
          id: pk, 
          status: 'ACTIVE' },
      });

      if (clientExist) {
        await clientExist.update(body, {
          where: { id: pk },
        });
        res.status(200).json(clientExist);
      } else {
        res.status(404).json({ error: "Client not found or inactive" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Delete a client physically
  public async deleteClient(req: Request, res: Response) {
    try {
      const { id } = req.params;
      const clientToDelete = await Client.findByPk(id);

      if (clientToDelete) {
        await clientToDelete.destroy();
        res.status(200).json({ message: "Client deleted successfully" });
      } else {
        res.status(404).json({ error: "Client not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error deleting client" });
    }
  }

  // Delete a client logically (change status to "INACTIVE")
  public async deleteClientAdv(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const clientToUpdate = await Client.findOne({
        where: { 
          id: pk, 
          status: 'ACTIVE' },
      });

      if (clientToUpdate) {
        await clientToUpdate.update({ status: 'INACTIVE' });
        res.status(200).json({ message: "Client marked as inactive" });
      } else {
        res.status(404).json({ error: "Client not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error marking client as inactive" });
    }
  }
}
1.2.2 Controlador ProductType

src/controllers/productType.controller.ts

import { Request, Response } from "express";
import { ProductType, ProductTypeI } from "../models/ProductType";

export class ProductTypeController {
  // Get all product types
  public async getAllProductTypes(req: Request, res: Response) {
    try {
      const productTypes: ProductTypeI[] = await ProductType.findAll({
        where: { status: true },
      });
      res.status(200).json({ productTypes });
    } catch (error) {
      res.status(500).json({ error: "Error fetching product types" });
    }
  }

  // Get a product type by ID
  public async getProductTypeById(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const productType = await ProductType.findOne({
        where: { id: pk, status: true },
      });
      if (productType) {
        res.status(200).json(productType);
      } else {
        res.status(404).json({ error: "Product type not found or inactive" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error fetching product type" });
    }
  }

  // Create a new product type
  public async createProductType(req: Request, res: Response) {
    const { name, description, status } = req.body;
    try {
      let body: ProductTypeI = {
        name,
        description,
        status,
      };

      const newProductType = await ProductType.create({ ...body });
      res.status(201).json(newProductType);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Update a product type
  public async updateProductType(req: Request, res: Response) {
    const { id: pk } = req.params;
    const { name, description, status } = req.body;
    try {
      let body: ProductTypeI = {
        name,
        description,
        status,
      };

      const productTypeExist = await ProductType.findOne({
        where: { id: pk, status: true },
      });

      if (productTypeExist) {
        await productTypeExist.update(body, {
          where: { id: pk },
        });
        res.status(200).json(productTypeExist);
      } else {
        res.status(404).json({ error: "Product type not found or inactive" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Delete a product type physically
  public async deleteProductType(req: Request, res: Response) {
    try {
      const { id } = req.params;
      const productTypeToDelete = await ProductType.findByPk(id);

      if (productTypeToDelete) {
        await productTypeToDelete.destroy();
        res.status(200).json({ message: "Product type deleted successfully" });
      } else {
        res.status(404).json({ error: "Product type not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error deleting product type" });
    }
  }

  // Delete a product type logically (change status to false)
  public async deleteProductTypeAdv(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const productTypeToUpdate = await ProductType.findOne({
        where: { id: pk, status: true },
      });

      if (productTypeToUpdate) {
        await productTypeToUpdate.update({ status: false });
        res.status(200).json({ message: "Product type marked as inactive" });
      } else {
        res.status(404).json({ error: "Product type not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error marking product type as inactive" });
    }
  }
}
1.2.3 Controlador Product

src/controllers/product.controller.ts

import { Request, Response } from "express";
import { Product, ProductI } from "../models/Product";

export class ProductController {
  // Get all products with status "ACTIVE"
  public async getAllProducts(req: Request, res: Response) {
    try {
      const products: ProductI[] = await Product.findAll({
        where: { status: "ACTIVE" },
      });
      res.status(200).json({ products });
    } catch (error) {
      res.status(500).json({ error: "Error fetching products" });
    }
  }

  // Get a product by ID
  public async getProductById(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const product = await Product.findOne({
        where: { id: pk, status: "ACTIVE" },
      });
      if (product) {
        res.status(200).json(product);
      } else {
        res.status(404).json({ error: "Product not found or inactive" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error fetching product" });
    }
  }

  // Create a new product
  public async createProduct(req: Request, res: Response) {
    const { id, name, brand, price, min_stock, quantity, product_type_id,status } = req.body;
    try {
      let body: ProductI = {
        name,
        brand,
        price,
        min_stock,
        quantity,
        product_type_id,
        status,
      };

      const newProduct = await Product.create({ ...body });
      res.status(201).json(newProduct);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Update a product
  public async updateProduct(req: Request, res: Response) {
    const { id: pk } = req.params;
    const { id,name, brand, price, min_stock, quantity, product_type_id, status } = req.body;
    try {
      let body: ProductI = {
        name,
        brand,
        price,
        min_stock,
        quantity,
        product_type_id,
        status,
      };

      const productExist = await Product.findOne({
        where: { id: pk, status: "ACTIVE" },
      });

      if (productExist) {
        await productExist.update(body, {
          where: { id: pk },
        });
        res.status(200).json(productExist);
      } else {
        res.status(404).json({ error: "Product not found or inactive" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Delete a product physically
  public async deleteProduct(req: Request, res: Response) {
    try {
      const { id } = req.params;
      const productToDelete = await Product.findByPk(id);

      if (productToDelete) {
        await productToDelete.destroy();
        res.status(200).json({ message: "Product deleted successfully" });
      } else {
        res.status(404).json({ error: "Product not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error deleting product" });
    }
  }

  // Delete a product logically (change status to "INACTIVE")
  public async deleteProductAdv(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const productToUpdate = await Product.findOne({
        where: { id: pk, status: "ACTIVE" },
      });

      if (productToUpdate) {
        await productToUpdate.update({ status: "INACTIVE" });
        res.status(200).json({ message: "Product marked as inactive" });
      } else {
        res.status(404).json({ error: "Product not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error marking product as inactive" });
    }
  }
}
1.2.4 Controlador Sale

src/controllers/sale.controller.ts

import { Request, Response } from "express";
import { Sale, SaleI } from "../models/Sale";

export class SaleController {
  // Get all sales with status "ACTIVE"
  public async getAllSales(req: Request, res: Response) {
    try {
      const sales: SaleI[] = await Sale.findAll({
        where: { status: "ACTIVE" },
      });
      res.status(200).json({ sales });
    } catch (error) {
      res.status(500).json({ error: "Error fetching sales" });
    }
  }

  // Get a sale by ID
  public async getSaleById(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const sale = await Sale.findOne({
        where: { 
          id: pk, 
          status: "ACTIVE" },
      });
      if (sale) {
        res.status(200).json(sale);
      } else {
        res.status(404).json({ error: "Sale not found or inactive" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error fetching sale" });
    }
  }

  // Create a new sale
  public async createSale(req: Request, res: Response) {
    const { id, sale_date, subtotal, tax, discounts, total, status, client_id } = req.body;
    try {
      let body: SaleI = {
        sale_date,
        subtotal,
        tax,
        discounts,
        total,
        status,
        client_id,
      };
      console.log(body)
      const newSale = await Sale.create({ ...body });
      res.status(201).json(newSale);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Update a sale
  public async updateSale(req: Request, res: Response) {
    const { id: pk } = req.params;
    const { id, sale_date, subtotal, tax, discounts, total, status, client_id } = req.body;
    try {
      let body: SaleI = {
        sale_date,
        subtotal,
        tax,
        discounts,
        total,
        status,
        client_id,

      };

      const saleExist = await Sale.findOne({
        where: { 
          id: pk, 
          status: "ACTIVE" },
      });

      if (saleExist) {
        await saleExist.update(body, {
          where: { id: pk },
        });
        res.status(200).json(saleExist);
      } else {
        res.status(404).json({ error: "Sale not found or inactive" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Delete a sale physically
  public async deleteSale(req: Request, res: Response) {
    try {
      const { id } = req.params;
      const saleToDelete = await Sale.findByPk(id);

      if (saleToDelete) {
        await saleToDelete.destroy();
        res.status(200).json({ message: "Sale deleted successfully" });
      } else {
        res.status(404).json({ error: "Sale not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error deleting sale" });
    }
  }

  // Delete a sale logically (change status to "INACTIVE")
  public async deleteSaleAdv(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const saleToUpdate = await Sale.findOne({
        where: { 
          id: pk, 
          status: "ACTIVE" },
      });

      if (saleToUpdate) {
        await saleToUpdate.update({ status: "INACTIVE" });
        res.status(200).json({ message: "Sale marked as inactive" });
      } else {
        res.status(404).json({ error: "Sale not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error marking sale as inactive" });
    }
  }
}
1.2.5 Controlador ProductSale

src/controllers/productSale.controller.ts

import { Request, Response } from "express";
import { ProductSale, ProductSaleI } from "../models/ProductSale";

export class ProductSaleController {
  // Get all product sales
  public async getAllProductSales(req: Request, res: Response) {
    try {
      const productSales: ProductSaleI[] = await ProductSale.findAll({
        where: { status: true },
      });
      res.status(200).json({ productSales });
    } catch (error) {
      res.status(500).json({ error: "Error fetching product sales" });
    }
  }

  // Get a product sale by ID
  public async getProductSaleById(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const productSale = await ProductSale.findOne({
        where: { id: pk, status: true },
      });
      if (productSale) {
        res.status(200).json(productSale);
      } else {
        res.status(404).json({ error: "Product sale not found or inactive" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error fetching product sale" });
    }
  }

  // Create a new product sale
  public async createProductSale(req: Request, res: Response) {
    const { total, sale_id, product_id } = req.body;
    try {
      let body: ProductSaleI = {
        total,
        sale_id,
        product_id,
      };

      const newProductSale = await ProductSale.create({ ...body });
      res.status(201).json(newProductSale);
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Update a product sale
  public async updateProductSale(req: Request, res: Response) {
    const { id: pk } = req.params;
    const { total, sale_id, product_id } = req.body;
    try {
      let body: ProductSaleI = {
        total,
        sale_id,
        product_id,
      };

      const productSaleExist = await ProductSale.findOne({
        where: { id: pk, status: true },
      });

      if (productSaleExist) {
        await productSaleExist.update(body, {
          where: { id: pk },
        });
        res.status(200).json(productSaleExist);
      } else {
        res.status(404).json({ error: "Product sale not found or inactive" });
      }
    } catch (error: any) {
      res.status(400).json({ error: error.message });
    }
  }

  // Delete a product sale physically
  public async deleteProductSale(req: Request, res: Response) {
    try {
      const { id } = req.params;
      const productSaleToDelete = await ProductSale.findByPk(id);

      if (productSaleToDelete) {
        await productSaleToDelete.destroy();
        res.status(200).json({ message: "Product sale deleted successfully" });
      } else {
        res.status(404).json({ error: "Product sale not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error deleting product sale" });
    }
  }

  // Delete a product sale logically (change status to false)
  public async deleteProductSaleAdv(req: Request, res: Response) {
    try {
      const { id: pk } = req.params;
      const productSaleToUpdate = await ProductSale.findOne({
        where: { id: pk, status: true },
      });

      if (productSaleToUpdate) {
        await productSaleToUpdate.update({ status: false });
        res.status(200).json({ message: "Product sale marked as inactive" });
      } else {
        res.status(404).json({ error: "Product sale not found" });
      }
    } catch (error) {
      res.status(500).json({ error: "Error marking product sale as inactive" });
    }
  }
}

Paso 2: Rutas

2.1 Rutas de los modelos de Autorizacion

1.2.1 Rutas de User

src/routes/authorization/user.ts

import { Application } from "express";
import { UserController } from '../../controllers/authorization/user.controller';

export class UserRoutes {
  public userController: UserController = new UserController();

  public routes(app: Application): void {
    app.route("/api/users").get(this.userController.getAllUsers);
    app.route("/api/users").post(this.userController.getAllUsers);
  }
}
1.2.2 Rutas de Role

src/routes/authorization/role.ts

import { Application } from "express";
import { RoleController } from '../../controllers/authorization/role.controller';

export class RoleRoutes {
  public roleController: RoleController = new RoleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/roles")
      .get(this.roleController.getAllRoles)
      .post(this.roleController.createRole);

    app.route("/api/roles/:id")
      .get(this.roleController.getRoleById)
      .patch(this.roleController.updateRole)
      .delete(this.roleController.deleteRole);

    app.route("/api/roles/:id/logic")
      .delete(this.roleController.deleteRoleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:



  }
}
1.2.3 Rutas de RoleUser

src/routes/authorization/role_user.ts

import { Application } from "express";
import { RoleUserController } from '../../controllers/authorization/role_user.controller';

export class RoleUserRoutes {
  public roleUserController: RoleUserController = new RoleUserController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/roleUsers")
      .get(this.roleUserController.getAllRoleUsers)
      .post(this.roleUserController.createRoleUser);

    app.route("/api/roleUsers/:id")
      .get(this.roleUserController.getRoleUserById)
      .patch(this.roleUserController.updateRoleUser)
      .delete(this.roleUserController.deleteRoleUser);

    app.route("/api/roleUsers/:id/logic")
      .delete(this.roleUserController.deleteRoleUserAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================



  }
}
1.2.4 Rutas de Resource

src/routes/authorization/resource.ts

import { Application } from "express";
import { ResourceController } from "../../controllers/authorization/resource.controller";

export class ResourceRoutes {
  public resourceController: ResourceController = new ResourceController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/resources")
      .get(this.resourceController.getAllResources)
      .post(this.resourceController.createResource);

    app.route("/api/resources/:id")
      .get(this.resourceController.getResourceById)
      .patch(this.resourceController.updateResource)
      .delete(this.resourceController.deleteResource);

    app.route("/api/resources/:id/logic")
      .delete(this.resourceController.deleteResourceAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:


  }
}
1.2.5 Rutas de ResourceRole

src/routes/authorization/resourceRole.ts

import { Application } from "express";
import { ResourceRoleController } from "../../controllers/authorization/resourceRole.controller";


export class ResourceRoleRoutes {
  public resourceRoleController: ResourceRoleController = new ResourceRoleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/resource-roles")
      .get(this.resourceRoleController.getAllResourceRoles)
      .post(this.resourceRoleController.createResourceRole);

    app.route("/api/resource-roles/:id")
      .get(this.resourceRoleController.getResourceRoleById)
      .patch(this.resourceRoleController.updateResourceRole)
      .delete(this.resourceRoleController.deleteResourceRole);

    app.route("/api/resource-roles/:id/logic")
      .delete(this.resourceRoleController.deleteResourceRoleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:


  }
}
1.2.6 Rutas de Refreshtoken

src/routes/authorization/refresh_token.ts

import { Application } from "express";
import { RefreshTokenController } from "../../controllers/authorization/refres_token.controller";

export class RefreshTokenRoutes {
  public refreshTokenController: RefreshTokenController = new RefreshTokenController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/refresk-token")
      .get(this.refreshTokenController.getAllRefreshToken);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:



  }
}

2.2 Rutas de los modelos de Negocio

2.2.1 Rutas de Client

src/routes/client.ts

import { Router, Application } from "express";
import { ClientController } from "../controllers/client.controller";

export class ClientRoutes {
  public clientController: ClientController = new ClientController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
     app.route("/api/clientes")
      .get(this.clientController.getAllClients)
      .post(this.clientController.createClient);

    app.route("/api/clientes/:id")
      .get(this.clientController.getClientById)
      .patch(this.clientController.updateClient)
      .delete(this.clientController.deleteClient);

    app.route("/api/clientes/:id/logic")
      .delete(this.clientController.deleteClientAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================


  }
}
2.2.2 Rutas de ProductType

src/routes/productType.ts

import { Application } from "express";
import { ProductTypeController } from "../controllers/productType.controller";

export class ProductTypeRoutes {
  public productTypeController: ProductTypeController = new ProductTypeController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/product-types")
      .get(this.productTypeController.getAllProductTypes)
      .post(this.productTypeController.createProductType);

    app.route("/product-types/:id")
      .get(this.productTypeController.getProductTypeById)
      .patch(this.productTypeController.updateProductType)
      .delete(this.productTypeController.deleteProductType);

    app.route("/product-types/:id/logic")
      .delete(this.productTypeController.deleteProductTypeAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:


  }
}
2.2.3 Rutas de Product

src/routes/product.ts

import { Application } from "express";
import { ProductController } from "../controllers/product.controller";

export class ProductRoutes {
  public productController: ProductController = new ProductController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/products")
      .get(this.productController.getAllProducts)
      .post(this.productController.createProduct);

    app.route("/products/:id")
      .get(this.productController.getProductById)
      .patch(this.productController.updateProduct)
      .delete(this.productController.deleteProduct);

    app.route("/products/:id/logic")
      .delete(this.productController.deleteProductAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:


  }
}
2.2.4 Rutas de Sale

src/routes/sale.ts

import { Application } from "express";
import { SaleController } from "../controllers/sale.controller";

export class SaleRoutes {
  public saleController: SaleController = new SaleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/ventas")
      .get(this.saleController.getAllSales)
      .post(this.saleController.createSale);

    app.route("/ventas/:id")
      .get(this.saleController.getSaleById)
      .patch(this.saleController.updateSale)
      .delete(this.saleController.deleteSale);

    app.route("/ventas/:id/logic")
      .delete(this.saleController.deleteSaleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:


  }
}
2.2.5 Rutas de ProductSale

src/routes/productSale.ts

import { Application } from "express";
import { ProductSaleController } from "../controllers/productSale.controller";

export class ProductSaleRoutes {
  public productSaleController: ProductSaleController = new ProductSaleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/product-sales")
      .get(this.productSaleController.getAllProductSales)
      .post(this.productSaleController.createProductSale);

    app.route("/product-sales/:id")
      .get(this.productSaleController.getProductSaleById)
      .patch(this.productSaleController.updateProductSale)
      .delete(this.productSaleController.deleteProductSale);

    app.route("/product-sales/:id/logic")
      .delete(this.productSaleController.deleteProductSaleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    // Si se requieren rutas protegidas, se pueden agregar aquí:


  }
}

2.2 Agregar Rutas a Routes/Index

src/routes/index.ts

import { Router } from "express";
import { ClientRoutes } from "./client";
import { SaleRoutes } from "./sale";
import { ProductRoutes } from "./product";
import { ProductTypeRoutes } from "./productType";
import { ProductSaleRoutes } from "./productSale";
import { UserRoutes } from "./authorization/user";
import { RoleRoutes } from "./authorization/role";
import { RoleUserRoutes } from "./authorization/role_user";
import { RefreshTokenRoutes } from "./authorization/refresk_token";
import { ResourceRoutes } from "./authorization/resource"; // Import ResourceRoutes
import { ResourceRoleRoutes } from "./authorization/resourceRole"; // Import ResourceRoleRoutes

export class Routes {
  public clientRoutes: ClientRoutes = new ClientRoutes();
  public saleRoutes: SaleRoutes = new SaleRoutes();
  public productRoutes: ProductRoutes = new ProductRoutes();
  public productTypeRoutes: ProductTypeRoutes = new ProductTypeRoutes();
  public productSaleRoutes: ProductSaleRoutes = new ProductSaleRoutes();
  public userRoutes: UserRoutes = new UserRoutes();
  public roleRoutes: RoleRoutes = new RoleRoutes();
  public roleUserRoutes: RoleUserRoutes = new RoleUserRoutes();
  public refreshTokenRoutes: RefreshTokenRoutes = new RefreshTokenRoutes();
  public resourceRoutes: ResourceRoutes = new ResourceRoutes(); // Add ResourceRoutes
  public resourceRoleRoutes: ResourceRoleRoutes = new ResourceRoleRoutes(); // Add ResourceRoutes

}

2.3 Agregar Rutas a Config/Index

src/config/index.ts

Agregar modulo de private routes() y su importacion

import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize, testConnection, getDatabaseInfo } from "../database/db";
import { Routes } from "../routes/index";

var cors = require("cors");

dotenv.config();

export class App {
  public app: Application;
  public routePrv: Routes = new Routes();

  constructor(private port?: number | string) {
    this.app = express();
    this.settings();
    this.middlewares();
    this.routes();
    this.dbConnection();
  }

  private settings(): void {
    this.app.set('port', this.port || process.env.PORT || 4000);
  }

  private middlewares(): void {
    this.app.use(morgan('dev'));
    this.app.use(cors());
    this.app.use(express.json());
    this.app.use(express.urlencoded({ extended: false }));
  }

  // Route configuration
  private routes(): void {
    this.routePrv.clientRoutes.routes(this.app);
    this.routePrv.saleRoutes.routes(this.app);
    this.routePrv.productRoutes.routes(this.app);
    this.routePrv.productTypeRoutes.routes(this.app);
    this.routePrv.productSaleRoutes.routes(this.app);
    this.routePrv.userRoutes.routes(this.app);
    this.routePrv.roleRoutes.routes(this.app);
    this.routePrv.roleUserRoutes.routes(this.app);
    this.routePrv.refreshTokenRoutes.routes(this.app);
    this.routePrv.resourceRoutes.routes(this.app);
    this.routePrv.resourceRoleRoutes.routes(this.app);
  }

  private async dbConnection(): Promise<void> {
    try {
      // Mostrar información de la base de datos seleccionada
      const dbInfo = getDatabaseInfo();
      console.log(`🔗 Intentando conectar a: ${dbInfo.engine.toUpperCase()}`);

      // Probar la conexión
      const isConnected = await testConnection();

      if (!isConnected) {
        throw new Error(`No se pudo conectar a la base de datos ${dbInfo.engine.toUpperCase()}`);
      }

      // Sincronizar la base de datos
      await sequelize.sync({ force: false });
      console.log(`📦 Base de datos sincronizada exitosamente`);

    } catch (error) {
      console.error("❌ Error al conectar con la base de datos:", error);
      process.exit(1); // Terminar la aplicación si no se puede conectar
    }
  }

  async listen() {
    await this.app.listen(this.app.get('port'));
    console.log(`🚀 Servidor ejecutándose en puerto ${this.app.get('port')}`);
  }
}

Luego,

2.4 Peticiones API REST

Para las peticiones API REST utilizaremos la extension en VS CODE RESTClient.

Instalar Extension

Luego, Crear carpeta src/http y crear archivo por modelo definidas en los archivos de rutas.

2.4.1 Http de los modelos de Autorizacion

2.4.1.1 Http de de User

src/http/authorization/user.http

### user.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### Esto es solo como ejemplo academico, dado que este modelo lo debemos manejar posteriormente con Auth
### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los usuarios
GET {{baseUrl}}/api/users

### Crear usuario (usando el mismo endpoint)
POST {{baseUrl}}/api/users
Content-Type: application/json

{
  "username": "newuser",
  "email": "newuser@example.com",
  "password": "123456",
  "is_active": "ACTIVE",
  "avatar": "avatar.jpg"
}

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.2 Http de de Role

src/http/authorization/role.http

### role.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los roles
GET {{baseUrl}}/api/roles

### Obtener rol por ID
GET {{baseUrl}}/api/roles/1

### Crear un nuevo rol
POST {{baseUrl}}/api/roles
Content-Type: application/json

{
  "name": "Administrator",
  "is_active": "ACTIVE"
}

### Actualizar un rol
PATCH {{baseUrl}}/api/roles/1
Content-Type: application/json

{
  "name": "Super Administrator",
  "is_active": "ACTIVE"
}

### Eliminar un rol físicamente
DELETE {{baseUrl}}/api/roles/1

### Eliminar un rol lógicamente
DELETE {{baseUrl}}/api/roles/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.3 Http de de RoleUser

src/http/authorization/role_user.http

### roleUser.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los roles de usuario
GET {{baseUrl}}/api/roleUsers

### Obtener rol de usuario por ID
GET {{baseUrl}}/api/roleUsers/1

### Crear un nuevo rol de usuario
POST {{baseUrl}}/api/roleUsers
Content-Type: application/json

{
  "role_id": 1,
  "user_id": 1,
  "is_active": "ACTIVE"
}

### Actualizar un rol de usuario
PATCH {{baseUrl}}/api/roleUsers/1
Content-Type: application/json

{
  "role_id": 2,
  "user_id": 1,
  "is_active": "ACTIVE"
}

### Eliminar un rol de usuario físicamente
DELETE {{baseUrl}}/api/roleUsers/1

### Eliminar un rol de usuario lógicamente
DELETE {{baseUrl}}/api/roleUsers/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.4 Http de de Resource

src/http/authorization/resource.http

### resource.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los recursos
GET {{baseUrl}}/api/resources

### Obtener recurso por ID
GET {{baseUrl}}/api/resources/1

### Crear un nuevo recurso
POST {{baseUrl}}/api/resources
Content-Type: application/json

{
  "path": "/api/clientes",
  "method": "GET",
  "is_active": "ACTIVE"
}

### Actualizar un recurso
PATCH {{baseUrl}}/api/resources/1
Content-Type: application/json

{
  "path": "/api/clientes/:id",
  "method": "GET",
  "is_active": "ACTIVE"
}

### Eliminar un recurso físicamente
DELETE {{baseUrl}}/api/resources/1

### Eliminar un recurso lógicamente
DELETE {{baseUrl}}/api/resources/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.5 Http de de ResourceRole

src/http/authorization/resourceRole.http

### resourceRole.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los roles de recursos
GET {{baseUrl}}/api/resource-roles

### Obtener rol de recurso por ID
GET {{baseUrl}}/api/resource-roles/1

### Crear un nuevo rol de recurso
POST {{baseUrl}}/api/resource-roles
Content-Type: application/json

{
  "resource_id": 1,
  "role_id": 1,
  "is_active": "ACTIVE"
}

### Actualizar un rol de recurso
PATCH {{baseUrl}}/api/resource-roles/1
Content-Type: application/json

{
  "resource_id": 1,
  "role_id": 2,
  "is_active": "ACTIVE"
}

### Eliminar un rol de recurso físicamente
DELETE {{baseUrl}}/api/resource-roles/1

### Eliminar un rol de recurso lógicamente
DELETE {{baseUrl}}/api/resource-roles/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.1.6 Http de de RefreshToken

src/http/authorization/refreshToken.http

### refreshToken.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los refresh tokens
GET {{baseUrl}}/refresk-token

### ================== RUTAS CON AUTENTICACIÓN ==================

2.4.2 Http de los modelos de Negocio

2.4.2.1 Http de de Client

src/http/client.http

// client.http
import dotenv from "dotenv";


@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6OCwiaWF0IjoxNzU5OTQyNjMxLCJleHAiOjE3NTk5NDMyMzF9.EAhQ6CjPXiwsq-hbvUSn9EXKF4CXcD-Q9IYMYlcOh3k
@baseUrl = http://localhost:4000

### Peticiones sin TOKEN

### Obtener todos los clientes 
GET {{baseUrl}}/api/clientes


### Obtener cliente por ID 
GET {{baseUrl}}/api/clientes/1

### Crear un nuevo cliente 
POST {{baseUrl}}/api/clientes
Content-Type: application/json

{
  "name": "Jaider Quintero",
  "address": "Km 1",
  "phone": "3001234567",
  "email": "jqm@gmail.com",
  "password": "12345678",
  "status": "ACTIVE"
}


### Actualizar un cliente 
PATCH {{baseUrl}}/api/clientes/1
Content-Type: application/json

{
  "name": "Juan Perez Actualizado",
  "address": "Calle 456",
  "phone": "3007654321",
  "email": "juan.perez@example.com",
  "password": "654321",
  "status": "ACTIVE"
}



### Eliminar un cliente físicamente 
DELETE {{baseUrl}}/api/clientes/1



### Eliminar un cliente lógicamente
DELETE {{baseUrl}}/api/clientes/1/logic


### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.2 Http de de ProductType

src/http/productType.http

### productType.http

@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los tipos de producto
GET {{baseUrl}}/product-types

### Obtener tipo de producto por ID
GET {{baseUrl}}/product-types/1

### Crear un nuevo tipo de producto
POST {{baseUrl}}/product-types
Content-Type: application/json

{
  "name": "Electrónicos",
  "description": "Productos electrónicos y tecnológicos",
  "status": "ACTIVE"
}

### Actualizar un tipo de producto
PATCH {{baseUrl}}/product-types/1
Content-Type: application/json

{
  "name": "Electrónicos Actualizados",
  "description": "Productos electrónicos y tecnológicos actualizados",
  "status": "ACTIVE"
}

### Eliminar un tipo de producto físicamente
DELETE {{baseUrl}}/product-types/1

### Eliminar un tipo de producto lógicamente
DELETE {{baseUrl}}/product-types/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.3 Http de de Product

src/http/product.http

### product.http

@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los productos
GET {{baseUrl}}/products

### Obtener producto por ID
GET {{baseUrl}}/products/1

### Crear un nuevo producto
POST {{baseUrl}}/products
Content-Type: application/json

{
  "name": "Laptop HP",
  "brand": "HP",
  "price": 1500000,
  "min_stock": 5,
  "quantity": 10,
  "product_type_id": 1,
  "status": "ACTIVE"
}

### Actualizar un producto
PATCH {{baseUrl}}/products/1
Content-Type: application/json

{
  "name": "Laptop HP Actualizada",
  "brand": "HP",
  "price": 1600000,
  "min_stock": 3,
  "quantity": 8,
  "product_type_id": 1,
  "status": "ACTIVE"
}

### Eliminar un producto físicamente
DELETE {{baseUrl}}/products/1

### Eliminar un producto lógicamente
DELETE {{baseUrl}}/products/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.4 Http de de Sale

src/http/sale.http

### sale.http

@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todas las ventas
GET {{baseUrl}}/ventas

### Obtener venta por ID
GET {{baseUrl}}/ventas/1

### Crear una nueva venta
POST {{baseUrl}}/ventas
Content-Type: application/json

{
  "sale_date": "2024-01-15T10:30:00.000Z",
  "subtotal": 100000,
  "tax": 19000,
  "discounts": 5000,
  "total": 114000,
  "status": "ACTIVE",
  "client_id": 1
}

### Actualizar una venta
PATCH {{baseUrl}}/ventas/1
Content-Type: application/json

{
  "sale_date": "2024-01-15T10:30:00.000Z",
  "subtotal": 120000,
  "tax": 22800,
  "discounts": 10000,
  "total": 132800,
  "status": "ACTIVE",
  "client_id": 1
}

### Eliminar una venta físicamente
DELETE {{baseUrl}}/ventas/1

### Eliminar una venta lógicamente
DELETE {{baseUrl}}/ventas/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================
2.4.2.5 Http de de ProductSale

src/http/productSale.http

### productSale.http

@baseUrl = http://localhost:3000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todas las ventas de productos
GET {{baseUrl}}/product-sales

### Obtener venta de producto por ID
GET {{baseUrl}}/product-sales/1

### Crear una nueva venta de producto
POST {{baseUrl}}/product-sales
Content-Type: application/json

{
  "total": 1500000,
  "sale_id": 1,
  "product_id": 1
}

### Actualizar una venta de producto
PATCH {{baseUrl}}/product-sales/1
Content-Type: application/json

{
  "total": 1600000,
  "sale_id": 1,
  "product_id": 1
}

### Eliminar una venta de producto físicamente
DELETE {{baseUrl}}/product-sales/1

### Eliminar una venta de producto lógicamente
DELETE {{baseUrl}}/product-sales/1/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

2.4 Como Probar las Peticiones API REST

Tomaremos como ejemplo las Rutas del Modelo Client

Para ello visualizamos dos archivos

  • src/http/client.http

  • src/routes/client.ts

En el archivo client.http se encuentran todas las rutas definidas en src/routes/client.ts

Se procede asi:

Y Asi se ve el resultado

Paso 3: Datos de Prueba (Opcional)

3.1 Instalar faker para datos de prueba

npm install @faker-js/faker

3.2 Crear script de población de datos

import { Client } from '../models/Client';
import { ProductType } from '../models/ProductType';
import { Product } from '../models/Product';
import { Sale } from '../models/Sale';
import { ProductSale } from '../models/ProductSale';
import { faker } from '@faker-js/faker';

async function createFakeData() {
    // Crear clientes falsos
    for (let i = 0; i < 50; i++) {
        await Client.create({
            name: faker.person.fullName(),
            address: faker.location.streetAddress(),
            phone: faker.phone.number(), // Genera un número de teléfono aleatorio
            email: faker.internet.email(),
            password: faker.internet.password(),
            status: 'ACTIVE',
        });
    }

    // Crear tipos de productos falsos
    for (let i = 0; i < 10; i++) {
        await ProductType.create({
            name: faker.commerce.department(),
            description: faker.commerce.productDescription(),
            status: 'ACTIVE',
        });
    }

    // Crear productos falsos
    const typeProducts = await ProductType.findAll();
    for (let i = 0; i < 20; i++) {
        await Product.create({
            name: faker.commerce.productName(),
            brand: faker.company.name(),
            price: faker.number.bigInt(),
            min_stock: faker.number.int({ min: 1, max: 10 }),
            quantity: faker.number.int({ min: 1, max: 100 }),
            product_type_id: typeProducts.length > 0
                ? typeProducts[faker.number.int({ min: 0, max: typeProducts.length - 1 })]?.id
                : null,
            status: 'ACTIVE',
        });
    }

    // Crear ventas falsas
    const clients = await Client.findAll();
    for (let i = 0; i < 100; i++) {
        await Sale.create({
            sale_date: faker.date.past(),
            subtotal: faker.number.bigInt(),
            tax: faker.number.bigInt(),
            discounts: faker.number.bigInt(),
            total: faker.number.bigInt(),
            status: 'ACTIVE',
            client_id: clients.length > 0
                ? clients[faker.number.int({ min: 0, max: clients.length - 1 })]?.id ?? null
                : null
        });
    }

//     // Crear productos ventas falsos
    const sales = await Sale.findAll();
    const products = await Product.findAll();
    for (let i = 0; i < 200; i++) {
        await ProductSale.create({
            total: faker.number.bigInt(),
            sale_id: sales[faker.number.int({ min: 0, max: sales.length - 1 })]?.id ?? null,
            product_id: products[faker.number.int({ min: 0, max: products.length - 1 })]?.id ?? null
        });
    }
}

createFakeData().then(() => {
    console.log('Datos falsos creados exitosamente');
}).catch((error) => {
    console.error('Error al crear datos falsos:', error);
});

// Para ejecutar este script, ejecute el siguiente comando:
// npm install -g ts-node
// ts-node src/faker/populate_data.ts
// npm install @faker-js/faker

Paso 4: Gitignore

1.1 Crear .gitignore

node_modules 
.env 
dist/ 
*.log 
.DS_Store

Paso 5: Ejecución del Proyecto

5.1 Scripts de package.json

{   
  "scripts": {     
    "build": "tsc",     
    "dev": "nodemon src/server.ts --exec ts-node",     
    "start": "node dist/server.js",     
    "populate": "ts-node src/faker/populate_data.ts"   

  } 

}

5.2 Ejecutar el proyecto

npm run dev   # Desarrollo 
npm run build # Producción 
npm start     
npm run populate # Poblar datos de prueba 

Finalmente,

Paso 6: Implementacion de Autenticacion y Autorizacion

6.1 Controlador Auth

Incluimos en los cotroladores un nuevo archivo para registrar usuarios y realizar login

src/controllers/Authorization/auth.controller.ts

import { Request, Response } from 'express';
import { User } from '../../models/authorization/User';

export class AuthController {
  public async register(req: Request, res: Response): Promise<void> {
    try {
      const { username, email, password, is_active, avatar } = req.body;
      const user_interface: User = await User.create({ username, email, password, is_active, avatar });
      const token = user_interface.generateToken();
      res.status(201).json({ user_interface, token });
    } catch (error) {
      res.status(500).json({ error: 'Error al registrar el usuario' });
    }
  }

  public async login(req: Request, res: Response): Promise<void> {
    try {
      const { email, password } = req.body;
      const user: User | null = await User.findOne(
        { 
          where: { 
            email,
            is_active: true 
          } 
      });
      if (!user || !(await user.checkPassword(password))) {
        res.status(401).json({ error: 'Credenciales inválidas' });
        return;
      }
      const token = user.generateToken();
      res.status(200).json({ user, token });
    } catch (error) {
      res.status(500).json({ error: 'Error al iniciar sesión' });
    }
  }

}

6.2 Ruta de Auth

src/routes/Authorization/auth.ts

import { Application } from "express";
import { AuthController } from '../../controllers/authorization/auth.controller';

export class AuthRoutes {
  public authController: AuthController = new AuthController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/register")
      .post(this.authController.register);

    app.route("/api/login")
      .post(this.authController.login);

    // ================== RUTAS CON AUTENTICACIÓN ==================

  }
}

Agregar estas rutas en

src/routes/Authorization/index.ts

.........
.........
.........

import { AuthRoutes } from "./authorization/auth"; // Add this import




export class Routes {
  ......
  ......





  public authRoutes: AuthRoutes = new AuthRoutes(); // Add this line

}

debe quedar asi:

import { Router } from "express";
import { ClientRoutes } from "./client";
import { SaleRoutes } from "./sale";
import { ProductRoutes } from "./product";
import { ProductTypeRoutes } from "./productType";
import { ProductSaleRoutes } from "./productSale";
import { UserRoutes } from "./authorization/user";
import { RoleRoutes } from "./authorization/role";
import { RoleUserRoutes } from "./authorization/role_user";
import { RefreshTokenRoutes } from "./authorization/refresk_token";
import { ResourceRoutes } from "./authorization/resource"; // Import ResourceRoutes
import { ResourceRoleRoutes } from "./authorization/resourceRole"; // Import ResourceRoleRoutes
import { AuthRoutes } from "./authorization/auth"; // Add this import


export class Routes {
  public clientRoutes: ClientRoutes = new ClientRoutes();
  public saleRoutes: SaleRoutes = new SaleRoutes();
  public productRoutes: ProductRoutes = new ProductRoutes();
  public productTypeRoutes: ProductTypeRoutes = new ProductTypeRoutes();
  public productSaleRoutes: ProductSaleRoutes = new ProductSaleRoutes();
  public userRoutes: UserRoutes = new UserRoutes();
  public roleRoutes: RoleRoutes = new RoleRoutes();
  public roleUserRoutes: RoleUserRoutes = new RoleUserRoutes();
  public refreshTokenRoutes: RefreshTokenRoutes = new RefreshTokenRoutes();
  public resourceRoutes: ResourceRoutes = new ResourceRoutes(); // Add ResourceRoutes
  public resourceRoleRoutes: ResourceRoleRoutes = new ResourceRoleRoutes(); // Add ResourceRoutes
  public authRoutes: AuthRoutes = new AuthRoutes(); // Add this line

}

y cerrar el ciclo actulizando las rutas en

src/config/index.http

// ...existing code...
  // Route configuration

  .....
  .....
  .....
  private routes(): void {
    .....
    ....
    ...
    ....

    this.routePrv.authRoutes.routes(this.app)
  }

Codigo index completo

import dotenv from "dotenv";
import express, { Application } from "express";
import morgan from "morgan";
import { sequelize, testConnection, getDatabaseInfo } from "../database/db";
import { Routes } from "../routes/index";

var cors = require("cors");

dotenv.config();

export class App {
  public app: Application;
  public routePrv: Routes = new Routes();

  constructor(private port?: number | string) {
    this.app = express();
    this.settings();
    this.middlewares();
    this.routes();
    this.dbConnection();
  }

  private settings(): void {
    this.app.set('port', this.port || process.env.PORT || 4000);
  }

  private middlewares(): void {
    this.app.use(morgan('dev'));
    this.app.use(cors());
    this.app.use(express.json());
    this.app.use(express.urlencoded({ extended: false }));
  }

  // Route configuration
  private routes(): void {
    this.routePrv.clientRoutes.routes(this.app);
    this.routePrv.saleRoutes.routes(this.app);
    this.routePrv.productRoutes.routes(this.app);
    this.routePrv.productTypeRoutes.routes(this.app);
    this.routePrv.productSaleRoutes.routes(this.app);
    this.routePrv.userRoutes.routes(this.app);
    this.routePrv.roleRoutes.routes(this.app);
    this.routePrv.roleUserRoutes.routes(this.app);
    this.routePrv.refreshTokenRoutes.routes(this.app);
    this.routePrv.resourceRoutes.routes(this.app);
    this.routePrv.resourceRoleRoutes.routes(this.app);
    this.routePrv.authRoutes.routes(this.app)
  }

  private async dbConnection(): Promise<void> {
    try {
      // Mostrar información de la base de datos seleccionada
      const dbInfo = getDatabaseInfo();
      console.log(`🔗 Intentando conectar a: ${dbInfo.engine.toUpperCase()}`);

      // Probar la conexión
      const isConnected = await testConnection();

      if (!isConnected) {
        throw new Error(`No se pudo conectar a la base de datos ${dbInfo.engine.toUpperCase()}`);
      }

      // Sincronizar la base de datos
      await sequelize.sync({ force: false });
      console.log(`📦 Base de datos sincronizada exitosamente`);

    } catch (error) {
      console.error("❌ Error al conectar con la base de datos:", error);
      process.exit(1); // Terminar la aplicación si no se puede conectar
    }
  }

  async listen() {
    await this.app.listen(this.app.get('port'));
    console.log(`🚀 Servidor ejecutándose en puerto ${this.app.get('port')}`);
  }
}

6.3 Http Auth

src/http/Authorization/auth.http

### auth.http

@baseUrl = http://localhost:4000
# @token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Registrar un nuevo usuario
POST {{baseUrl}}/api/register
Content-Type: application/json

{
  "username": "jquintero",
  "email": "jq@gmail.com",
  "password": "12345678",
  "is_active": "ACTIVE",
  "avatar": "avatar.jpg"
}

### Iniciar sesión
POST {{baseUrl}}/api/login
Content-Type: application/json

{
  "email": "jq@gmail.com",
  "password": "12345678"
}

### ================== RUTAS CON AUTENTICACIÓN ==================

Probar Registrar Usuario

Probar Registrar Login

6.4 Configurar Middleware

Con el Middleware protegemos todas las rutas usando JWT TOKEN

src/middleware/auth.ts

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { RefreshToken } from '../models/authorization/RefreshToken';
import { User } from '../models/authorization/User';


export const authMiddleware = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  const currentRoute = req.originalUrl;
  const currentMethod = req.method;

  console.log(currentRoute)
  if (!token) {
    res.status(401).json({ error: 'Acceso denegado: No se proporcionó el token principal.' });
    return;
  }

  try {
    // Verificar si el token principal es válido
    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret') as jwt.JwtPayload;

    // Buscar el usuario en la base de datos
    const user: User | null = await User.findOne({ where: { id: decoded.id, is_active: 'ACTIVE' } });
    if (!user) {
      res.status(401).json({ error: 'Usuario no encontrado o inactivo.' });
      return;
    }


    // Continuar con la solicitud
    next();
  } catch (error: any) {
    if (error.name === 'TokenExpiredError') {
      res.status(401).json({ error: 'El token principal ha expirado.' });
    } else if (error.name === 'JsonWebTokenError') {
      res.status(401).json({ error: 'Token inválido.' });
    } else {
      res.status(500).json({ error: 'Error interno del servidor.', details: error.message });
    }
  }
};

Y para probar protegemos una ruta con token por ejemplo en cliente

src/routes/client.ts

import { Router, Application } from "express";
import { ClientController } from "../controllers/client.controller";
import { authMiddleware } from "../middleware/auth";


export class ClientRoutes {
  public clientController: ClientController = new ClientController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
     app.route("/api/clientes/public")
      .get(this.clientController.getAllClients)
      .post(this.clientController.createClient);

    app.route("/api/clientes/public/:id")
      .get(this.clientController.getClientById)
      .patch(this.clientController.updateClient)
      .delete(this.clientController.deleteClient);

    app.route("/api/clientes/public/:id/logic")
      .delete(this.clientController.deleteClientAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================

    app.route("/api/clientes")
      .get(authMiddleware, this.clientController.getAllClients)

  }
}

Notese que cambie el end point de las rutas sin token las que tienen token para que no sean iguales y pueda realizar bien la proteccion de la ruta

Primero debemos registar el usuario para obtener el token o en su defecto solo hacer el login si ya lo hemos registrado.

Luego en el http de client.http pegar el token obtenido y ejecutar la ruta protegida de mostrar todos los clientes

src/http/client.http

// client.http
import dotenv from "dotenv";


@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
@baseUrl = http://localhost:4000

### Peticiones sin TOKEN

### Obtener todos los clientes 
GET {{baseUrl}}/api/clientes/public


### Obtener cliente por ID 
GET {{baseUrl}}/api/clientes/public/1

### Crear un nuevo cliente 
POST {{baseUrl}}/api/clientes/public
Content-Type: application/json

{
  "name": "Jaider Quintero",
  "address": "Km 1",
  "phone": "3001234567",
  "email": "jqm@gmail.com",
  "password": "12345678",
  "status": "ACTIVE"
}


### Actualizar un cliente 
PATCH {{baseUrl}}/api/clientes/public/1
Content-Type: application/json

{
  "name": "Juan Perez Actualizado",
  "address": "Calle 456",
  "phone": "3007654321",
  "email": "juan.perez@example.com",
  "password": "654321",
  "status": "ACTIVE"
}



### Eliminar un cliente físicamente
DELETE {{baseUrl}}/api/clientes/public/1



### Eliminar un cliente lógicamente
DELETE {{baseUrl}}/api/clientes/public/1/logic


### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los clientes (CON token)
GET {{baseUrl}}/api/clientes
Authorization: Bearer {{token}}

Hacemos lo mismo para el resto de las rutas de clientes e iguamente para el resto de las rutas de los modelos

6.5 Autenticacion con Token Rutas Modelos Autorizacion

6.5.1 Ruta y Http User

No haremos cambio en User dado que lo mabejaremos con Auth

6.5.2 Ruta y Http Role

src/routes/authorization/role.ts

import { Application } from "express";
import { RoleController } from '../../controllers/authorization/role.controller';
import { authMiddleware } from '../../middleware/auth';

export class RoleRoutes {
  public roleController: RoleController = new RoleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/roles/public")
      .get(this.roleController.getAllRoles)
      .post(this.roleController.createRole);

    app.route("/api/roles/public/:id")
      .get(this.roleController.getRoleById)
      .patch(this.roleController.updateRole)
      .delete(this.roleController.deleteRole);

    app.route("/api/roles/public/:id/logic")
      .delete(this.roleController.deleteRoleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/roles")
      .get(authMiddleware, this.roleController.getAllRoles)
      .post(authMiddleware, this.roleController.createRole);

    app.route("/api/roles/:id")
      .get(authMiddleware, this.roleController.getRoleById)
      .patch(authMiddleware, this.roleController.updateRole)
      .delete(authMiddleware, this.roleController.deleteRole);

    app.route("/api/roles/:id/logic")
      .delete(authMiddleware, this.roleController.deleteRoleAdv);
  }
}

src/http/authorization/role.ts

### role.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los roles (SIN token)
GET {{baseUrl}}/api/roles/public

### Obtener rol por ID (SIN token)
GET {{baseUrl}}/api/roles/public/1

### Crear un nuevo rol (SIN token)
POST {{baseUrl}}/api/roles/public
Content-Type: application/json

{
  "name": "Administrator",
  "is_active": "ACTIVE"
}

### Actualizar un rol (SIN token)
PATCH {{baseUrl}}/api/roles/public/1
Content-Type: application/json

{
  "name": "Super Administrator",
  "is_active": "ACTIVE"
}

### Eliminar un rol físicamente (SIN token)
DELETE {{baseUrl}}/api/roles/public/1

### Eliminar un rol lógicamente (SIN token)
DELETE {{baseUrl}}/api/roles/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los roles (CON token)
GET {{baseUrl}}/api/roles
Authorization: Bearer {{token}}

### Obtener rol por ID (CON token)
GET {{baseUrl}}/api/roles/1
Authorization: Bearer {{token}}

### Crear un nuevo rol (CON token)
POST {{baseUrl}}/api/roles
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "Manager",
  "is_active": "ACTIVE"
}

### Actualizar un rol (CON token)
PATCH {{baseUrl}}/api/roles/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "Senior Manager",
  "is_active": "ACTIVE"
}

### Eliminar un rol físicamente (CON token)
DELETE {{baseUrl}}/api/roles/1
Authorization: Bearer {{token}}

### Eliminar un rol lógicamente (CON token)
DELETE {{baseUrl}}/api/roles/1/logic
Authorization: Bearer {{token}}

6.5.3 Ruta y Http RoleUser

src/routes/authorization/role_user.ts

import { Application } from "express";
import { RoleUserController } from '../../controllers/authorization/role_user.controller';
import { authMiddleware } from '../../middleware/auth';

export class RoleUserRoutes {
  public roleUserController: RoleUserController = new RoleUserController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/roleUsers/public")
      .get(this.roleUserController.getAllRoleUsers)
      .post(this.roleUserController.createRoleUser);

    app.route("/api/roleUsers/public/:id")
      .get(this.roleUserController.getRoleUserById)
      .patch(this.roleUserController.updateRoleUser)
      .delete(this.roleUserController.deleteRoleUser);

    app.route("/api/roleUsers/public/:id/logic")
      .delete(this.roleUserController.deleteRoleUserAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/roleUsers")
      .get(authMiddleware, this.roleUserController.getAllRoleUsers)
      .post(authMiddleware, this.roleUserController.createRoleUser);

    app.route("/api/roleUsers/:id")
      .get(authMiddleware, this.roleUserController.getRoleUserById)
      .patch(authMiddleware, this.roleUserController.updateRoleUser)
      .delete(authMiddleware, this.roleUserController.deleteRoleUser);

    app.route("/api/roleUsers/:id/logic")
      .delete(authMiddleware, this.roleUserController.deleteRoleUserAdv);
  }
}

src/http/authorization/role_user.ts

### roleUser.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los roles de usuario (SIN token)
GET {{baseUrl}}/api/roleUsers/public

### Obtener rol de usuario por ID (SIN token)
GET {{baseUrl}}/api/roleUsers/public/1

### Crear un nuevo rol de usuario (SIN token)
POST {{baseUrl}}/api/roleUsers/public
Content-Type: application/json

{
  "role_id": 1,
  "user_id": 1,
  "is_active": "ACTIVE"
}

### Actualizar un rol de usuario (SIN token)
PATCH {{baseUrl}}/api/roleUsers/public/1
Content-Type: application/json

{
  "role_id": 2,
  "user_id": 1,
  "is_active": "ACTIVE"
}

### Eliminar un rol de usuario físicamente (SIN token)
DELETE {{baseUrl}}/api/roleUsers/public/1

### Eliminar un rol de usuario lógicamente (SIN token)
DELETE {{baseUrl}}/api/roleUsers/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los roles de usuario (CON token)
GET {{baseUrl}}/api/roleUsers
Authorization: Bearer {{token}}

### Obtener rol de usuario por ID (CON token)
GET {{baseUrl}}/api/roleUsers/1
Authorization: Bearer {{token}}

### Crear un nuevo rol de usuario (CON token)
POST {{baseUrl}}/api/roleUsers
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "role_id": 1,
  "user_id": 2,
  "is_active": "ACTIVE"
}

### Actualizar un rol de usuario (CON token)
PATCH {{baseUrl}}/api/roleUsers/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "role_id": 3,
  "user_id": 2,
  "is_active": "ACTIVE"
}

### Eliminar un rol de usuario físicamente (CON token)
DELETE {{baseUrl}}/api/roleUsers/1
Authorization: Bearer {{token}}

### Eliminar un rol de usuario lógicamente (CON token)
DELETE {{baseUrl}}/api/roleUsers/1/logic
Authorization: Bearer {{token}}

6.5.4 Ruta y Http Resource

src/routes/authorization/resource.ts

import { Application } from "express";
import { ResourceController } from "../../controllers/authorization/resource.controller";
import { authMiddleware } from '../../middleware/auth';

export class ResourceRoutes {
  public resourceController: ResourceController = new ResourceController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/resources/public")
      .get(this.resourceController.getAllResources)
      .post(this.resourceController.createResource);

    app.route("/api/resources/public/:id")
      .get(this.resourceController.getResourceById)
      .patch(this.resourceController.updateResource)
      .delete(this.resourceController.deleteResource);

    app.route("/api/resources/public/:id/logic")
      .delete(this.resourceController.deleteResourceAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/resources")
      .get(authMiddleware, this.resourceController.getAllResources)
      .post(authMiddleware, this.resourceController.createResource);

    app.route("/api/resources/:id")
      .get(authMiddleware, this.resourceController.getResourceById)
      .patch(authMiddleware, this.resourceController.updateResource)
      .delete(authMiddleware, this.resourceController.deleteResource);

    app.route("/api/resources/:id/logic")
      .delete(authMiddleware, this.resourceController.deleteResourceAdv);
  }
}

src/http/authorization/resource.ts

### resource.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los recursos (SIN token)
GET {{baseUrl}}/api/resources/public

### Obtener recurso por ID (SIN token)
GET {{baseUrl}}/api/resources/public/1

### Crear un nuevo recurso (SIN token)
POST {{baseUrl}}/api/resources/public
Content-Type: application/json

{
  "path": "/api/clientes",
  "method": "GET",
  "is_active": "ACTIVE"
}

### Actualizar un recurso (SIN token)
PATCH {{baseUrl}}/api/resources/public/1
Content-Type: application/json

{
  "path": "/api/clientes/:id",
  "method": "GET",
  "is_active": "ACTIVE"
}

### Eliminar un recurso físicamente (SIN token)
DELETE {{baseUrl}}/api/resources/public/1

### Eliminar un recurso lógicamente (SIN token)
DELETE {{baseUrl}}/api/resources/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los recursos (CON token)
GET {{baseUrl}}/api/resources
Authorization: Bearer {{token}}

### Obtener recurso por ID (CON token)
GET {{baseUrl}}/api/resources/1
Authorization: Bearer {{token}}

### Crear un nuevo recurso (CON token)
POST {{baseUrl}}/api/resources
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "path": "/api/users",
  "method": "POST",
  "is_active": "ACTIVE"
}

### Actualizar un recurso (CON token)
PATCH {{baseUrl}}/api/resources/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "path": "/api/users/:id",
  "method": "PATCH",
  "is_active": "ACTIVE"
}

### Eliminar un recurso físicamente (CON token)
DELETE {{baseUrl}}/api/resources/1
Authorization: Bearer {{token}}

### Eliminar un recurso lógicamente (CON token)
DELETE {{baseUrl}}/api/resources/1/logic
Authorization: Bearer {{token}}

6.5.5 Ruta y Http ResourceRole

src/routes/authorization/resourceRole.ts

import { Application } from "express";
import { ResourceRoleController } from "../../controllers/authorization/resourceRole.controller";
import { authMiddleware } from '../../middleware/auth';

export class ResourceRoleRoutes {
  public resourceRoleController: ResourceRoleController = new ResourceRoleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/resourceRoles/public")
      .get(this.resourceRoleController.getAllResourceRoles)
      .post(this.resourceRoleController.createResourceRole);

    app.route("/api/resourceRoles/public/:id")
      .get(this.resourceRoleController.getResourceRoleById)
      .patch(this.resourceRoleController.updateResourceRole)
      .delete(this.resourceRoleController.deleteResourceRole);

    app.route("/api/resourceRoles/public/:id/logic")
      .delete(this.resourceRoleController.deleteResourceRoleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/resourceRoles")
      .get(authMiddleware, this.resourceRoleController.getAllResourceRoles)
      .post(authMiddleware, this.resourceRoleController.createResourceRole);

    app.route("/api/resourceRoles/:id")
      .get(authMiddleware, this.resourceRoleController.getResourceRoleById)
      .patch(authMiddleware, this.resourceRoleController.updateResourceRole)
      .delete(authMiddleware, this.resourceRoleController.deleteResourceRole);

    app.route("/api/resourceRoles/:id/logic")
      .delete(authMiddleware, this.resourceRoleController.deleteResourceRoleAdv);
  }
}

src/http/authorization/resourceRole.ts

### resourceRole.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los roles de recursos (SIN token)
GET {{baseUrl}}/api/resourceRoles/public

### Obtener rol de recurso por ID (SIN token)
GET {{baseUrl}}/api/resourceRoles/public/1

### Crear un nuevo rol de recurso (SIN token)
POST {{baseUrl}}/api/resourceRoles/public
Content-Type: application/json

{
  "resource_id": 1,
  "role_id": 1,
  "is_active": "ACTIVE"
}

### Actualizar un rol de recurso (SIN token)
PATCH {{baseUrl}}/api/resourceRoles/public/1
Content-Type: application/json

{
  "resource_id": 1,
  "role_id": 2,
  "is_active": "ACTIVE"
}

### Eliminar un rol de recurso físicamente (SIN token)
DELETE {{baseUrl}}/api/resourceRoles/public/1

### Eliminar un rol de recurso lógicamente (SIN token)
DELETE {{baseUrl}}/api/resourceRoles/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los roles de recursos (CON token)
GET {{baseUrl}}/api/resourceRoles
Authorization: Bearer {{token}}

### Obtener rol de recurso por ID (CON token)
GET {{baseUrl}}/api/resourceRoles/1
Authorization: Bearer {{token}}

### Crear un nuevo rol de recurso (CON token)
POST {{baseUrl}}/api/resourceRoles
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "resource_id": 2,
  "role_id": 1,
  "is_active": "ACTIVE"
}

### Actualizar un rol de recurso (CON token)
PATCH {{baseUrl}}/api/resourceRoles/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "resource_id": 2,
  "role_id": 3,
  "is_active": "ACTIVE"
}

### Eliminar un rol de recurso físicamente (CON token)
DELETE {{baseUrl}}/api/resourceRoles/1
Authorization: Bearer {{token}}

### Eliminar un rol de recurso lógicamente (CON token)
DELETE {{baseUrl}}/api/resourceRoles/1/logic
Authorization: Bearer {{token}}

6.5.5 Ruta y Http RefreshToken

No trabajaremos la parte de autenticacion y autorizacion este modelo

6.5 Autenticacion con Token Rutas Modelos del Negocio

6.5.1 Ruta y Http Client

src/routes/client.ts

import { Router, Application } from "express";
import { ClientController } from "../controllers/client.controller";
import { authMiddleware } from "../middleware/auth";

export class ClientRoutes {
  public clientController: ClientController = new ClientController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/clientes/public")
      .get(this.clientController.getAllClients)
      .post(this.clientController.createClient);

    app.route("/api/clientes/public/:id")
      .get(this.clientController.getClientById)
      .patch(this.clientController.updateClient)
      .delete(this.clientController.deleteClient);

    app.route("/api/clientes/public/:id/logic")
      .delete(this.clientController.deleteClientAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/clientes")
      .get(authMiddleware, this.clientController.getAllClients)
      .post(authMiddleware, this.clientController.createClient);

    app.route("/api/clientes/:id")
      .get(authMiddleware, this.clientController.getClientById)
      .patch(authMiddleware, this.clientController.updateClient)
      .delete(authMiddleware, this.clientController.deleteClient);

    app.route("/api/clientes/:id/logic")
      .delete(authMiddleware, this.clientController.deleteClientAdv);
  }
}

src/http/client.http

// client.http
import dotenv from "dotenv";

@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA
@baseUrl = http://localhost:4000

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los clientes (SIN token)
GET {{baseUrl}}/api/clientes/public

### Obtener cliente por ID (SIN token)
GET {{baseUrl}}/api/clientes/public/1

### Crear un nuevo cliente (SIN token)
POST {{baseUrl}}/api/clientes/public
Content-Type: application/json

{
  "name": "Sandy Romero",
  "address": "Km 1",
  "phone": "3001234567",
  "email": "srm@gmail.com",
  "password": "12345678",
  "status": "ACTIVE"
}

### Actualizar un cliente (SIN token)
PATCH {{baseUrl}}/api/clientes/public/1
Content-Type: application/json

{
  "name": "Juan Perez Actualizado",
  "address": "Calle 456",
  "phone": "3007654321",
  "email": "juan.perez@example.com",
  "password": "654321",
  "status": "ACTIVE"
}

### Eliminar un cliente físicamente (SIN token)
DELETE {{baseUrl}}/api/clientes/public/1

### Eliminar un cliente lógicamente (SIN token)
DELETE {{baseUrl}}/api/clientes/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los clientes (CON token)
GET {{baseUrl}}/api/clientes
Authorization: Bearer {{token}}

### Obtener cliente por ID (CON token)
GET {{baseUrl}}/api/clientes/1
Authorization: Bearer {{token}}

### Crear un nuevo cliente (CON token)
POST {{baseUrl}}/api/clientes
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "María García",
  "address": "Calle 123",
  "phone": "3009876543",
  "email": "maria.garcia@example.com",
  "password": "87654321",
  "status": "ACTIVE"
}

### Actualizar un cliente (CON token)
PATCH {{baseUrl}}/api/clientes/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "María García Actualizada",
  "address": "Avenida 456",
  "phone": "3001234567",
  "email": "maria.garcia.updated@example.com",
  "password": "newpassword123",
  "status": "ACTIVE"
}

### Eliminar un cliente físicamente (CON token)
DELETE {{baseUrl}}/api/clientes/1
Authorization: Bearer {{token}}

### Eliminar un cliente lógicamente (CON token)
DELETE {{baseUrl}}/api/clientes/1/logic
Authorization: Bearer {{token}}

6.5.2 Ruta y Http ProductType

src/routes/productType.ts

import { Application } from "express";
import { ProductTypeController } from "../controllers/productType.controller";
import { authMiddleware } from "../middleware/auth";

export class ProductTypeRoutes {
  public productTypeController: ProductTypeController = new ProductTypeController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/product-types/public")
      .get(this.productTypeController.getAllProductTypes)
      .post(this.productTypeController.createProductType);

    app.route("/api/product-types/public/:id")
      .get(this.productTypeController.getProductTypeById)
      .patch(this.productTypeController.updateProductType)
      .delete(this.productTypeController.deleteProductType);

    app.route("/api/product-types/public/:id/logic")
      .delete(this.productTypeController.deleteProductTypeAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/product-types")
      .get(authMiddleware, this.productTypeController.getAllProductTypes)
      .post(authMiddleware, this.productTypeController.createProductType);

    app.route("/api/product-types/:id")
      .get(authMiddleware, this.productTypeController.getProductTypeById)
      .patch(authMiddleware, this.productTypeController.updateProductType)
      .delete(authMiddleware, this.productTypeController.deleteProductType);

    app.route("/api/product-types/:id/logic")
      .delete(authMiddleware, this.productTypeController.deleteProductTypeAdv);
  }
}

src/http/productType.http

### productType.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los tipos de producto (SIN token)
GET {{baseUrl}}/api/product-types/public

### Obtener tipo de producto por ID (SIN token)
GET {{baseUrl}}/api/product-types/public/1

### Crear un nuevo tipo de producto (SIN token)
POST {{baseUrl}}/api/product-types/public
Content-Type: application/json

{
  "name": "Electrónicos",
  "description": "Productos electrónicos y tecnológicos",
  "status": "ACTIVE"
}

### Actualizar un tipo de producto (SIN token)
PATCH {{baseUrl}}/api/product-types/public/1
Content-Type: application/json

{
  "name": "Electrónicos Actualizados",
  "description": "Productos electrónicos y tecnológicos actualizados",
  "status": "ACTIVE"
}

### Eliminar un tipo de producto físicamente (SIN token)
DELETE {{baseUrl}}/api/product-types/public/1

### Eliminar un tipo de producto lógicamente (SIN token)
DELETE {{baseUrl}}/api/product-types/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los tipos de producto (CON token)
GET {{baseUrl}}/api/product-types
Authorization: Bearer {{token}}

### Obtener tipo de producto por ID (CON token)
GET {{baseUrl}}/api/product-types/1
Authorization: Bearer {{token}}

### Crear un nuevo tipo de producto (CON token)
POST {{baseUrl}}/api/product-types
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "Ropa y Accesorios",
  "description": "Prendas de vestir y accesorios de moda",
  "status": "ACTIVE"
}

### Actualizar un tipo de producto (CON token)
PATCH {{baseUrl}}/api/product-types/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "Ropa y Accesorios Premium",
  "description": "Prendas de vestir y accesorios de moda premium",
  "status": "ACTIVE"
}

### Eliminar un tipo de producto físicamente (CON token)
DELETE {{baseUrl}}/api/product-types/1
Authorization: Bearer {{token}}

### Eliminar un tipo de producto lógicamente (CON token)
DELETE {{baseUrl}}/api/product-types/1/logic
Authorization: Bearer {{token}}

6.5.3 Ruta y Http Product

src/routes/product.ts

import { Application } from "express";
import { ProductController } from "../controllers/product.controller";
import { authMiddleware } from "../middleware/auth";

export class ProductRoutes {
  public productController: ProductController = new ProductController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/products/public")
      .get(this.productController.getAllProducts)
      .post(this.productController.createProduct);

    app.route("/api/products/public/:id")
      .get(this.productController.getProductById)
      .patch(this.productController.updateProduct)
      .delete(this.productController.deleteProduct);

    app.route("/api/products/public/:id/logic")
      .delete(this.productController.deleteProductAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/products")
      .get(authMiddleware, this.productController.getAllProducts)
      .post(authMiddleware, this.productController.createProduct);

    app.route("/api/products/:id")
      .get(authMiddleware, this.productController.getProductById)
      .patch(authMiddleware, this.productController.updateProduct)
      .delete(authMiddleware, this.productController.deleteProduct);

    app.route("/api/products/:id/logic")
      .delete(authMiddleware, this.productController.deleteProductAdv);
  }
}

src/http/product.http

### product.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todos los productos (SIN token)
GET {{baseUrl}}/api/products/public

### Obtener producto por ID (SIN token)
GET {{baseUrl}}/api/products/public/1

### Crear un nuevo producto (SIN token)
POST {{baseUrl}}/api/products/public
Content-Type: application/json

{
  "name": "Laptop HP",
  "brand": "HP",
  "price": 1500000,
  "min_stock": 5,
  "quantity": 10,
  "product_type_id": 1,
  "status": "ACTIVE"
}

### Actualizar un producto (SIN token)
PATCH {{baseUrl}}/api/products/public/1
Content-Type: application/json

{
  "name": "Laptop HP Actualizada",
  "brand": "HP",
  "price": 1600000,
  "min_stock": 3,
  "quantity": 8,
  "product_type_id": 1,
  "status": "ACTIVE"
}

### Eliminar un producto físicamente (SIN token)
DELETE {{baseUrl}}/api/products/public/1

### Eliminar un producto lógicamente (SIN token)
DELETE {{baseUrl}}/api/products/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todos los productos (CON token)
GET {{baseUrl}}/api/products
Authorization: Bearer {{token}}

### Obtener producto por ID (CON token)
GET {{baseUrl}}/api/products/1
Authorization: Bearer {{token}}

### Crear un nuevo producto (CON token)
POST {{baseUrl}}/api/products
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "MacBook Air M2",
  "brand": "Apple",
  "price": 3500000,
  "min_stock": 2,
  "quantity": 5,
  "product_type_id": 1,
  "status": "ACTIVE"
}

### Actualizar un producto (CON token)
PATCH {{baseUrl}}/api/products/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "name": "MacBook Air M2 Updated",
  "brand": "Apple",
  "price": 3600000,
  "min_stock": 3,
  "quantity": 7,
  "product_type_id": 1,
  "status": "ACTIVE"
}

### Eliminar un producto físicamente (CON token)
DELETE {{baseUrl}}/api/products/1
Authorization: Bearer {{token}}

### Eliminar un producto lógicamente (CON token)
DELETE {{baseUrl}}/api/products/1/logic
Authorization: Bearer {{token}}

6.5.4 Ruta y Http Sale

src/routes/sale.ts

import { Application } from "express";
import { SaleController } from "../controllers/sale.controller";
import { authMiddleware } from "../middleware/auth";

export class SaleRoutes {
  public saleController: SaleController = new SaleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/sales/public")
      .get(this.saleController.getAllSales)
      .post(this.saleController.createSale);

    app.route("/api/sales/public/:id")
      .get(this.saleController.getSaleById)
      .patch(this.saleController.updateSale)
      .delete(this.saleController.deleteSale);

    app.route("/api/sales/public/:id/logic")
      .delete(this.saleController.deleteSaleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/sales")
      .get(authMiddleware, this.saleController.getAllSales)
      .post(authMiddleware, this.saleController.createSale);

    app.route("/api/sales/:id")
      .get(authMiddleware, this.saleController.getSaleById)
      .patch(authMiddleware, this.saleController.updateSale)
      .delete(authMiddleware, this.saleController.deleteSale);

    app.route("/api/sales/:id/logic")
      .delete(authMiddleware, this.saleController.deleteSaleAdv);

    // ================== RUTAS LEGACY (mantener compatibilidad) ==================
    app.route("/ventas")
      .get(this.saleController.getAllSales)
      .post(this.saleController.createSale);

    app.route("/ventas/:id")
      .get(this.saleController.getSaleById)
      .patch(this.saleController.updateSale)
      .delete(this.saleController.deleteSale);

    app.route("/ventas/:id/logic")
      .delete(this.saleController.deleteSaleAdv);
  }
}

src/http/sale.http

### sale.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todas las ventas (SIN token)
GET {{baseUrl}}/api/sales/public

### Obtener venta por ID (SIN token)
GET {{baseUrl}}/api/sales/public/1

### Crear una nueva venta (SIN token)
POST {{baseUrl}}/api/sales/public
Content-Type: application/json

{
  "sale_date": "2024-01-15T10:30:00.000Z",
  "subtotal": 100000,
  "tax": 19000,
  "discounts": 5000,
  "total": 114000,
  "status": "ACTIVE",
  "client_id": 1
}

### Actualizar una venta (SIN token)
PATCH {{baseUrl}}/api/sales/public/1
Content-Type: application/json

{
  "sale_date": "2024-01-15T10:30:00.000Z",
  "subtotal": 120000,
  "tax": 22800,
  "discounts": 10000,
  "total": 132800,
  "status": "ACTIVE",
  "client_id": 1
}

### Eliminar una venta físicamente (SIN token)
DELETE {{baseUrl}}/api/sales/public/1

### Eliminar una venta lógicamente (SIN token)
DELETE {{baseUrl}}/api/sales/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todas las ventas (CON token)
GET {{baseUrl}}/api/sales
Authorization: Bearer {{token}}

### Obtener venta por ID (CON token)
GET {{baseUrl}}/api/sales/1
Authorization: Bearer {{token}}

### Crear una nueva venta (CON token)
POST {{baseUrl}}/api/sales
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "sale_date": "2024-02-20T14:45:00.000Z",
  "subtotal": 250000,
  "tax": 47500,
  "discounts": 15000,
  "total": 282500,
  "status": "ACTIVE",
  "client_id": 2
}

### Actualizar una venta (CON token)
PATCH {{baseUrl}}/api/sales/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "sale_date": "2024-02-20T15:00:00.000Z",
  "subtotal": 275000,
  "tax": 52250,
  "discounts": 20000,
  "total": 307250,
  "status": "ACTIVE",
  "client_id": 2
}

### Eliminar una venta físicamente (CON token)
DELETE {{baseUrl}}/api/sales/1
Authorization: Bearer {{token}}

### Eliminar una venta lógicamente (CON token)
DELETE {{baseUrl}}/api/sales/1/logic
Authorization: Bearer {{token}}

### ================== RUTAS LEGACY (mantener compatibilidad) ==================

### Obtener todas las ventas (legacy)
GET {{baseUrl}}/ventas

### Obtener venta por ID (legacy)
GET {{baseUrl}}/ventas/1

### Crear una nueva venta (legacy)
POST {{baseUrl}}/ventas
Content-Type: application/json

{
  "sale_date": "2024-01-15T10:30:00.000Z",
  "subtotal": 100000,
  "tax": 19000,
  "discounts": 5000,
  "total": 114000,
  "status": "ACTIVE",
  "client_id": 1
}

### Actualizar una venta (legacy)
PATCH {{baseUrl}}/ventas/1
Content-Type: application/json

{
  "sale_date": "2024-01-15T10:30:00.000Z",
  "subtotal": 120000,
  "tax": 22800,
  "discounts": 10000,
  "total": 132800,
  "status": "ACTIVE",
  "client_id": 1
}

### Eliminar una venta físicamente (legacy)
DELETE {{baseUrl}}/ventas/1

### Eliminar una venta lógicamente (legacy)
DELETE {{baseUrl}}/ventas/1/logic

6.5.5 Ruta y Http ProductSale

src/routes/productSale.ts

import { Application } from "express";
import { ProductSaleController } from "../controllers/productSale.controller";
import { authMiddleware } from "../middleware/auth";

export class ProductSaleRoutes {
  public productSaleController: ProductSaleController = new ProductSaleController();

  public routes(app: Application): void {
    // ================== RUTAS SIN AUTENTICACIÓN ==================
    app.route("/api/product-sales/public")
      .get(this.productSaleController.getAllProductSales)
      .post(this.productSaleController.createProductSale);

    app.route("/api/product-sales/public/:id")
      .get(this.productSaleController.getProductSaleById)
      .patch(this.productSaleController.updateProductSale)
      .delete(this.productSaleController.deleteProductSale);

    app.route("/api/product-sales/public/:id/logic")
      .delete(this.productSaleController.deleteProductSaleAdv);

    // ================== RUTAS CON AUTENTICACIÓN ==================
    app.route("/api/product-sales")
      .get(authMiddleware, this.productSaleController.getAllProductSales)
      .post(authMiddleware, this.productSaleController.createProductSale);

    app.route("/api/product-sales/:id")
      .get(authMiddleware, this.productSaleController.getProductSaleById)
      .patch(authMiddleware, this.productSaleController.updateProductSale)
      .delete(authMiddleware, this.productSaleController.deleteProductSale);

    app.route("/api/product-sales/:id/logic")
      .delete(authMiddleware, this.productSaleController.deleteProductSaleAdv);

    // ================== RUTAS LEGACY (mantener compatibilidad) ==================
    app.route("/product-sales")
      .get(this.productSaleController.getAllProductSales)
      .post(this.productSaleController.createProductSale);

    app.route("/product-sales/:id")
      .get(this.productSaleController.getProductSaleById)
      .patch(this.productSaleController.updateProductSale)
      .delete(this.productSaleController.deleteProductSale);

    app.route("/product-sales/:id/logic")
      .delete(this.productSaleController.deleteProductSaleAdv);
  }
}

src/http/productSale.http

### productSale.http

@baseUrl = http://localhost:4000
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTMsImlhdCI6MTc2MDA2MDU1NywiZXhwIjoxNzYwMDYxMTU3fQ.l9kxHGraKffDkcOLHMmaVa12iOQKdrhZHrNnP6wb6dA

### ================== RUTAS SIN AUTENTICACIÓN ==================

### Obtener todas las ventas de productos (SIN token)
GET {{baseUrl}}/api/product-sales/public

### Obtener venta de producto por ID (SIN token)
GET {{baseUrl}}/api/product-sales/public/1

### Crear una nueva venta de producto (SIN token)
POST {{baseUrl}}/api/product-sales/public
Content-Type: application/json

{
  "total": 1500000,
  "sale_id": 1,
  "product_id": 1
}

### Actualizar una venta de producto (SIN token)
PATCH {{baseUrl}}/api/product-sales/public/1
Content-Type: application/json

{
  "total": 1600000,
  "sale_id": 1,
  "product_id": 1
}

### Eliminar una venta de producto físicamente (SIN token)
DELETE {{baseUrl}}/api/product-sales/public/1

### Eliminar una venta de producto lógicamente (SIN token)
DELETE {{baseUrl}}/api/product-sales/public/2/logic

### ================== RUTAS CON AUTENTICACIÓN ==================

### Obtener todas las ventas de productos (CON token)
GET {{baseUrl}}/api/product-sales
Authorization: Bearer {{token}}

### Obtener venta de producto por ID (CON token)
GET {{baseUrl}}/api/product-sales/1
Authorization: Bearer {{token}}

### Crear una nueva venta de producto (CON token)
POST {{baseUrl}}/api/product-sales
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "total": 2500000,
  "sale_id": 2,
  "product_id": 2
}

### Actualizar una venta de producto (CON token)
PATCH {{baseUrl}}/api/product-sales/1
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "total": 2700000,
  "sale_id": 2,
  "product_id": 2
}

### Eliminar una venta de producto físicamente (CON token)
DELETE {{baseUrl}}/api/product-sales/1
Authorization: Bearer {{token}}

### Eliminar una venta de producto lógicamente (CON token)
DELETE {{baseUrl}}/api/product-sales/1/logic
Authorization: Bearer {{token}}

### ================== RUTAS LEGACY (mantener compatibilidad) ==================

### Obtener todas las ventas de productos (legacy)
GET {{baseUrl}}/product-sales

### Obtener venta de producto por ID (legacy)
GET {{baseUrl}}/product-sales/1

### Crear una nueva venta de producto (legacy)
POST {{baseUrl}}/product-sales
Content-Type: application/json

{
  "total": 1500000,
  "sale_id": 1,
  "product_id": 1
}

### Actualizar una venta de producto (legacy)
PATCH {{baseUrl}}/product-sales/1
Content-Type: application/json

{
  "total": 1600000,
  "sale_id": 1,
  "product_id": 1
}

### Eliminar una venta de producto físicamente (legacy)
DELETE {{baseUrl}}/product-sales/1

### Eliminar una venta de producto lógicamente (legacy)
DELETE {{baseUrl}}/product-sales/1/logic

6.5 Implementar la Autorizacion

Para ello debemos insertar los registros a todas las tablas de los modelos de Athorization.

Luego, implementar el auth del middleware de la siguiente manera:

src/middleware/auth.ts

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { User } from '../models/authorization/User';
import { Role } from '../models/authorization/Role';
import { ResourceRole } from '../models/authorization/ResourceRole';
import { Resource } from '../models/authorization/Resource';
import { RoleUser } from '../models/authorization/RoleUser';
import { pathToRegexp } from 'path-to-regexp'; // Importar path-to-regexp
import { addEmitHelper } from 'typescript';


export const authMiddleware = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  const currentRoute = req.originalUrl;
  const currentMethod = req.method;

  if (!token) {
    res.status(401).json({ error: 'Acceso denegado: No se proporcionó el token principal.' });
    return;
  }

  try {
    // Verificar si el token principal es válido
    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret') as jwt.JwtPayload;

    // Buscar el usuario en la base de datos
    const user: User | null = await User.findOne({ where: { id: decoded.id, is_active: 'ACTIVE' } });
    if (!user) {
      res.status(401).json({ error: 'Usuario no encontrado o inactivo.' });
      return;
    }

    // Validar autorización
    const isAuthorized = await validateAuthorization(decoded.id, currentRoute, currentMethod);
    if (!isAuthorized) {
      res.status(403).json({ error: 'No está autorizado para ejecutar esta petición.' });
      return;
    }

    // Continuar con la solicitud
    next();
  } catch (error: any) {
    if (error.name === 'TokenExpiredError') {
      res.status(401).json({ error: 'El token principal ha expirado.' });
    } else if (error.name === 'JsonWebTokenError') {
      res.status(401).json({ error: 'Token inválido.' });
    } else {
      res.status(500).json({ error: 'Error interno del servidor.', details: error.message });
    }
  }
};

export const validateAuthorization = async (userId: number, resourcePath: string, resourceMethod: string): Promise<boolean> => {
  try {
    // Obtener todos los recursos activos que coincidan con el método
    const resources = await Resource.findAll({
      where: { method: resourceMethod, is_active: "ACTIVE" },
    });

    // Convertir las rutas dinámicas a expresiones regulares y buscar coincidencias
    const matchingResource = resources.find((resource) => {
      const regex = pathToRegexp(resource.path).regexp.test(resourcePath);
      return regex;
    });

    if (!matchingResource) {
      return false; // No hay coincidencias para la ruta y el método
    }

    // Verificar si existe una relación válida entre el usuario, su rol y el recurso solicitado
    const resourceRole = await ResourceRole.findOne({
      include: [
        {
          model: Role,
          include: [
            {
              model: RoleUser,
              where: { user_id: userId, is_active: "ACTIVE" }, // Validar que el usuario esté asociado al rol
            },
          ],
          where: { is_active: "ACTIVE" }, // Validar que el rol esté activo
        },
      ],
      where: { resource_id: matchingResource.id, is_active: "ACTIVE" }, // Validar que la relación resource_role esté activa
    });

    return !!resourceRole; // Retorna true si se encuentra un registro coincidente
  } catch (error) {
    console.error('Error al validar la autorización:', error);
    return false;
  }
};