diff --git a/modules/catalogs/package.json b/modules/catalogs/package.json index f3efad40b..a74f0c02 100644 --- a/modules/catalogs/package.json +++ b/modules/catalogs/package.json @@ -33,8 +33,8 @@ "typescript": "^6.0.2" }, "dependencies": { - "@erp/auth": "workspace:*", "@erp/core": "workspace:*", + "@erp/identity": "workspace:*", "@repo/i18next": "workspace:*", "@repo/rdx-criteria": "workspace:*", "@repo/rdx-ddd": "workspace:*", @@ -52,4 +52,4 @@ "use-debounce": "^10.1.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/catalogs/src/api/index.ts b/modules/catalogs/src/api/index.ts index ee067789..b11a763e 100644 --- a/modules/catalogs/src/api/index.ts +++ b/modules/catalogs/src/api/index.ts @@ -27,7 +27,7 @@ export type CatalogsPublicServicesType = ReturnType { // ---------------------------------------------- - if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { - // 🔐 Autenticación + Tenancy para TODO el router - router.use( - (req: Request, res: Response, next: NextFunction) => - mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas - ); - } - - router.use([ - (req: Request, res: Response, next: NextFunction) => - requireAuthenticated()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas - - (req: Request, res: Response, next: NextFunction) => - requireCompanyContext()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas - ]); + router.use(...requireIdentityTenant(params)); // ---------------------------------------------- diff --git a/modules/catalogs/src/api/infrastructure/payment-terms/express/payment-terms.routes.ts b/modules/catalogs/src/api/infrastructure/payment-terms/express/payment-terms.routes.ts index 024e8a07..19e78a2d 100644 --- a/modules/catalogs/src/api/infrastructure/payment-terms/express/payment-terms.routes.ts +++ b/modules/catalogs/src/api/infrastructure/payment-terms/express/payment-terms.routes.ts @@ -1,5 +1,5 @@ -import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api"; -import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api"; +import { type StartParams, validateRequest } from "@erp/core/api"; +import { requireIdentityTenant } from "@erp/identity/api"; import type { NextFunction, Request, Response } from "express"; import { Router } from "express"; @@ -29,18 +29,7 @@ export const paymentTermsRouter = (params: StartParams) => { const router = Router({ mergeParams: true }); - if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { - router.use((req: Request, res: Response, next: NextFunction) => - mockUser(req as RequestWithAuth, res, next) - ); - } - - router.use([ - (req: Request, res: Response, next: NextFunction) => - requireAuthenticated()(req as RequestWithAuth, res, next), - (req: Request, res: Response, next: NextFunction) => - requireCompanyContext()(req as RequestWithAuth, res, next), - ]); + router.use(...requireIdentityTenant(params)); router.get( "/", diff --git a/modules/catalogs/src/api/infrastructure/tax-definitions/express/tax-definitions.routes.ts b/modules/catalogs/src/api/infrastructure/tax-definitions/express/tax-definitions.routes.ts index 329678d1..72db0d2b 100644 --- a/modules/catalogs/src/api/infrastructure/tax-definitions/express/tax-definitions.routes.ts +++ b/modules/catalogs/src/api/infrastructure/tax-definitions/express/tax-definitions.routes.ts @@ -1,5 +1,5 @@ -import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api"; -import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api"; +import { type StartParams, validateRequest } from "@erp/core/api"; +import { requireIdentityTenant } from "@erp/identity/api"; import type { NextFunction, Request, Response } from "express"; import { Router } from "express"; @@ -29,18 +29,7 @@ export const taxDefinitionsRouter = (params: StartParams) => { const router = Router({ mergeParams: true }); - if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { - router.use((req: Request, res: Response, next: NextFunction) => - mockUser(req as RequestWithAuth, res, next) - ); - } - - router.use([ - (req: Request, res: Response, next: NextFunction) => - requireAuthenticated()(req as RequestWithAuth, res, next), - (req: Request, res: Response, next: NextFunction) => - requireCompanyContext()(req as RequestWithAuth, res, next), - ]); + router.use(...requireIdentityTenant(params)); router.get( "/", diff --git a/modules/catalogs/src/api/infrastructure/tax-regimes/express/tax-regimes.routes.ts b/modules/catalogs/src/api/infrastructure/tax-regimes/express/tax-regimes.routes.ts index f1f4f577..4b0833ad 100644 --- a/modules/catalogs/src/api/infrastructure/tax-regimes/express/tax-regimes.routes.ts +++ b/modules/catalogs/src/api/infrastructure/tax-regimes/express/tax-regimes.routes.ts @@ -1,5 +1,5 @@ -import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api"; -import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api"; +import { type StartParams, validateRequest } from "@erp/core/api"; +import { requireIdentityTenant } from "@erp/identity/api"; import type { NextFunction, Request, Response } from "express"; import { Router } from "express"; @@ -29,18 +29,7 @@ export const taxRegimesRouter = (params: StartParams) => { const router = Router({ mergeParams: true }); - if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { - router.use((req: Request, res: Response, next: NextFunction) => - mockUser(req as RequestWithAuth, res, next) - ); - } - - router.use([ - (req: Request, res: Response, next: NextFunction) => - requireAuthenticated()(req as RequestWithAuth, res, next), - (req: Request, res: Response, next: NextFunction) => - requireCompanyContext()(req as RequestWithAuth, res, next), - ]); + router.use(...requireIdentityTenant(params)); router.get( "/", diff --git a/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts b/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts index eeaefc99..84c3802c 100644 --- a/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts +++ b/modules/customer-invoices/src/web/proformas/create/controllers/use-create-proforma-controller.ts @@ -81,11 +81,23 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle const setCustomer = (customer: CustomerSelectionOption) => { setSelectedCustomer(customer); + + // Necesitamos consultar todos los datos del cliente para + // poder rellenar los campos de la proforma + form.setValue("customerId", customer.id, { shouldDirty: true, shouldTouch: true, shouldValidate: true, }); + + /*form.setValue("") + + form.setValue("taxRegimeCode", customer.taxRegimeCode ?? "", { + shouldDirty: true, + shouldTouch: true, + shouldValidate: true, + });*/ }; const clearCustomer = () => { diff --git a/modules/customers/package.json b/modules/customers/package.json index 3525684e..be585d99 100644 --- a/modules/customers/package.json +++ b/modules/customers/package.json @@ -34,9 +34,9 @@ "typescript": "^6.0.2" }, "dependencies": { - "@erp/auth": "workspace:*", - "@erp/core": "workspace:*", "@erp/catalogs": "workspace:*", + "@erp/core": "workspace:*", + "@erp/identity": "workspace:*", "@hookform/resolvers": "^5.2.2", "@repo/i18next": "workspace:*", "@repo/rdx-criteria": "workspace:*", @@ -56,4 +56,4 @@ "use-debounce": "^10.1.1", "zod": "^4.3.6" } -} \ No newline at end of file +} diff --git a/modules/customers/src/api/index.ts b/modules/customers/src/api/index.ts index b16ce9b9..207b6e24 100644 --- a/modules/customers/src/api/index.ts +++ b/modules/customers/src/api/index.ts @@ -10,7 +10,7 @@ export * from "./infrastructure/persistence/sequelize"; export const customersAPIModule: IModuleServer = { name: "customers", version: "1.0.0", - dependencies: ["catalogs"], + dependencies: ["catalogs", "identity"], /** * Fase de SETUP diff --git a/modules/customers/src/api/infrastructure/express/customers.routes.ts b/modules/customers/src/api/infrastructure/express/customers.routes.ts index c10224d0..bcde567e 100644 --- a/modules/customers/src/api/infrastructure/express/customers.routes.ts +++ b/modules/customers/src/api/infrastructure/express/customers.routes.ts @@ -1,5 +1,5 @@ -import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api"; -import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api"; +import { type StartParams, validateRequest } from "@erp/core/api"; +import { requireIdentityTenant } from "@erp/identity/api"; import { type NextFunction, type Request, type Response, Router } from "express"; import { @@ -29,21 +29,7 @@ export const customersRouter = (params: StartParams) => { // ---------------------------------------------- // 🔐 Autenticación + Tenancy para TODO el router - if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "production") { - router.use( - (req: Request, res: Response, next: NextFunction) => - mockUser(req as RequestWithAuth, res, next) // Debe ir antes de las rutas protegidas - ); - } - - //router.use(/*authenticateJWT(),*/ enforceTenant() /*checkTabContext*/); - router.use([ - (req: Request, res: Response, next: NextFunction) => - requireAuthenticated()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas - - (req: Request, res: Response, next: NextFunction) => - requireCompanyContext()(req as RequestWithAuth, res, next), // Debe ir antes de las rutas protegidas - ]); + router.use(...requireIdentityTenant(params)); // ---------------------------------------------- diff --git a/modules/customers/src/common/features/get-customer/utils/index.ts b/modules/customers/src/common/features/get-customer/utils/index.ts index 42234997..c4e6b393 100644 --- a/modules/customers/src/common/features/get-customer/utils/index.ts +++ b/modules/customers/src/common/features/get-customer/utils/index.ts @@ -1 +1 @@ -export * from './build-customer-selection-option'; +export * from "./build-customer-data"; diff --git a/modules/customers/tsconfig.json b/modules/customers/tsconfig.json index dcaa2f64..36c3f7b5 100644 --- a/modules/customers/tsconfig.json +++ b/modules/customers/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "paths": { - "@erp/customers/*": ["./src/*"] + "@erp/customers/*": ["./src/*"], + "@erp/identity/*": ["../identity/src/*"] }, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", diff --git a/modules/identity/package.json b/modules/identity/package.json index 8388a8d6..bb41e809 100644 --- a/modules/identity/package.json +++ b/modules/identity/package.json @@ -34,7 +34,9 @@ "zod": "^4.3.6" }, "devDependencies": { + "@types/bcrypt": "^6.0.0", "@types/express": "^4.17.21", + "@types/jsonwebtoken": "^9.0.10", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/react-router-dom": "^5.3.3", diff --git a/modules/identity/src/api/application/index.ts b/modules/identity/src/api/application/index.ts index ed2ba9b4..4b395629 100644 --- a/modules/identity/src/api/application/index.ts +++ b/modules/identity/src/api/application/index.ts @@ -4,3 +4,4 @@ export * from "./companies"; export * from "./company-memberships"; export * from "./permissions"; export * from "./roles"; +export * from "./services"; diff --git a/modules/identity/src/api/application/services/identity-auth-public-services.interface.ts b/modules/identity/src/api/application/services/identity-auth-public-services.interface.ts new file mode 100644 index 00000000..f818f197 --- /dev/null +++ b/modules/identity/src/api/application/services/identity-auth-public-services.interface.ts @@ -0,0 +1,10 @@ +import type { RequestHandler } from "express"; + +export type AuthenticatedOnlyMiddlewares = readonly [RequestHandler, RequestHandler]; + +export type TenantRequiredMiddlewares = readonly [RequestHandler, RequestHandler, RequestHandler]; + +export interface IIdentityAuthPublicServices { + authenticatedOnly(): AuthenticatedOnlyMiddlewares; + tenantRequired(): TenantRequiredMiddlewares; +} diff --git a/modules/identity/src/api/application/services/identity-public-services.interface.ts b/modules/identity/src/api/application/services/identity-public-services.interface.ts new file mode 100644 index 00000000..67546e78 --- /dev/null +++ b/modules/identity/src/api/application/services/identity-public-services.interface.ts @@ -0,0 +1,5 @@ +import type { IIdentityAuthPublicServices } from "./identity-auth-public-services.interface"; + +export interface IIdentityPublicServices { + auth: IIdentityAuthPublicServices; +} diff --git a/modules/identity/src/api/application/services/index.ts b/modules/identity/src/api/application/services/index.ts new file mode 100644 index 00000000..c6b0192b --- /dev/null +++ b/modules/identity/src/api/application/services/index.ts @@ -0,0 +1,2 @@ +export * from "./identity-auth-public-services.interface"; +export * from "./identity-public-services.interface"; diff --git a/modules/identity/src/api/index.ts b/modules/identity/src/api/index.ts index a09bdb3c..2a5a5519 100644 --- a/modules/identity/src/api/index.ts +++ b/modules/identity/src/api/index.ts @@ -1,13 +1,16 @@ import type { IModuleServer } from "@erp/core/api"; +import type { IIdentityPublicServices } from "./application"; export * from "./application"; export * from "./domain"; export * from "./infrastructure"; import { identityAuthRouter, models } from "./infrastructure"; -import { buildIdentityDependencies } from "./infrastructure/di"; +import { buildIdentityDependencies, buildIdentityPublicServices } from "./infrastructure/di"; export * from "./infrastructure/persistence/sequelize"; +export type { IIdentityAuthPublicServices, IIdentityPublicServices } from "./application"; +export type IdentityPublicServicesType = IIdentityPublicServices; export const identityAPIModule: IModuleServer = { name: "identity", @@ -28,8 +31,7 @@ export const identityAPIModule: IModuleServer = { const internal = buildIdentityDependencies(params); // 2) Servicios públicos (Application Services) - const identityServices = {}; - //const identityServices: IIdentityPublicServices = {}; //buildIdentityPublicServices(params, internal); + const identityServices: IIdentityPublicServices = buildIdentityPublicServices(internal); logger.info("🚀 Identity module dependencies registered", { label: this.name, diff --git a/modules/identity/src/api/infrastructure/di/identity-public-services.ts b/modules/identity/src/api/infrastructure/di/identity-public-services.ts new file mode 100644 index 00000000..fc0483e3 --- /dev/null +++ b/modules/identity/src/api/infrastructure/di/identity-public-services.ts @@ -0,0 +1,35 @@ +import type { + IIdentityAuthPublicServices, + IIdentityPublicServices, +} from "../../application"; +import { + authenticateUser, + requireAuthenticated, + requireCompanyContext, +} from "../express/middlewares"; + +import type { IdentityInternalDeps } from "./identity.di"; + +class IdentityAuthPublicServices implements IIdentityAuthPublicServices { + public constructor( + private readonly deps: IdentityInternalDeps["auth"]["authenticateUserDependencies"] + ) {} + + public authenticatedOnly() { + return [authenticateUser(this.deps), requireAuthenticated()] as const; + } + + public tenantRequired() { + return [ + authenticateUser(this.deps), + requireAuthenticated(), + requireCompanyContext(), + ] as const; + } +} + +export function buildIdentityPublicServices(deps: IdentityInternalDeps): IIdentityPublicServices { + return { + auth: new IdentityAuthPublicServices(deps.auth.authenticateUserDependencies), + }; +} diff --git a/modules/identity/src/api/infrastructure/di/identity.di.ts b/modules/identity/src/api/infrastructure/di/identity.di.ts index f5881cf2..f970cf7b 100644 --- a/modules/identity/src/api/infrastructure/di/identity.di.ts +++ b/modules/identity/src/api/infrastructure/di/identity.di.ts @@ -2,6 +2,7 @@ import { type ModuleParams, buildTransactionManager } from "@erp/core/api"; import { AccountAuthenticator, + type IAccessTokenVerifier, type GetCurrentSessionUseCase, GetCurrentSessionUseCase as GetCurrentSessionUseCaseImpl, type LoginWithPasswordUseCase, @@ -12,6 +13,7 @@ import { RefreshSessionUseCase as RefreshSessionUseCaseImpl, SessionIssuer, } from "../../application/authentication"; +import type { IAccountRepository } from "../../application/accounts"; import { identityAuthenticateRequest } from "../express/middlewares"; import { SequelizeAccountDomainMapper, @@ -30,6 +32,10 @@ import { export type IdentityInternalDeps = { auth: { refreshTokenExpiration: string; + authenticateUserDependencies: { + accessTokenVerifier: IAccessTokenVerifier; + accountRepository: IAccountRepository; + }; authenticateRequest: ReturnType; useCases: { loginWithPassword: () => LoginWithPasswordUseCase; @@ -79,6 +85,10 @@ export const buildIdentityDependencies = (params: ModuleParams): IdentityInterna return { auth: { refreshTokenExpiration: config.auth.jwt.refreshExpiration, + authenticateUserDependencies: { + accessTokenVerifier, + accountRepository, + }, authenticateRequest: identityAuthenticateRequest({ accessTokenVerifier, accountRepository, diff --git a/modules/identity/src/api/infrastructure/di/index.ts b/modules/identity/src/api/infrastructure/di/index.ts index 28482fa9..5ffa356f 100644 --- a/modules/identity/src/api/infrastructure/di/index.ts +++ b/modules/identity/src/api/infrastructure/di/index.ts @@ -1 +1,2 @@ export * from "./identity.di"; +export * from "./identity-public-services"; diff --git a/modules/identity/src/api/infrastructure/express/middlewares/index.ts b/modules/identity/src/api/infrastructure/express/middlewares/index.ts index 0e4db0eb..c9ed6cd8 100644 --- a/modules/identity/src/api/infrastructure/express/middlewares/index.ts +++ b/modules/identity/src/api/infrastructure/express/middlewares/index.ts @@ -1,4 +1,5 @@ export * from "./authenticate-user.middleware"; export * from "./require-authenticated.middleware"; export * from "./require-company-context.middleware"; +export * from "./require-identity-middlewares"; export * from "./identity-authenticate-request"; diff --git a/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts b/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts index 65c44050..04ec8ba7 100644 --- a/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts +++ b/modules/identity/src/api/infrastructure/express/middlewares/require-authenticated.middleware.ts @@ -1,13 +1,12 @@ import { ExpressController, UnauthorizedApiError, type RequestWithAuth } from "@erp/core/api"; -import type { NextFunction, Response } from "express"; +import type { NextFunction, Request, Response } from "express"; export function requireAuthenticated() { - return (req: RequestWithAuth, res: Response, next: NextFunction) => { - if (!req.user) { + return (req: Request, res: Response, next: NextFunction) => { + if (!(req as RequestWithAuth).user) { return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res); } next(); }; } - diff --git a/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts b/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts index 49b6fd75..887fc44d 100644 --- a/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts +++ b/modules/identity/src/api/infrastructure/express/middlewares/require-company-context.middleware.ts @@ -1,12 +1,12 @@ import { ExpressController, ForbiddenApiError, type RequestWithAuth } from "@erp/core/api"; -import type { NextFunction, Response } from "express"; +import type { NextFunction, Request, Response } from "express"; export function requireCompanyContext() { - return (req: RequestWithAuth, res: Response, next: NextFunction) => { + return (req: Request, res: Response, next: NextFunction) => { /** * TODO: validar membership real cuando exista persistencia de CompanyMembership. */ - if (!req.user?.companyId) { + if (!(req as RequestWithAuth).user?.companyId) { return ExpressController.errorResponse( new ForbiddenApiError("Company context required"), req, @@ -17,4 +17,3 @@ export function requireCompanyContext() { next(); }; } - diff --git a/modules/identity/src/api/infrastructure/express/middlewares/require-identity-middlewares.ts b/modules/identity/src/api/infrastructure/express/middlewares/require-identity-middlewares.ts new file mode 100644 index 00000000..54922b43 --- /dev/null +++ b/modules/identity/src/api/infrastructure/express/middlewares/require-identity-middlewares.ts @@ -0,0 +1,14 @@ +import type { StartParams } from "@erp/core/api"; +import type { RequestHandler } from "express"; + +import type { IIdentityPublicServices } from "../../../application"; + +export function requireIdentityAuthenticated(params: StartParams): RequestHandler[] { + const identityServices = params.getService("identity:general"); + return [...identityServices.auth.authenticatedOnly()]; +} + +export function requireIdentityTenant(params: StartParams): RequestHandler[] { + const identityServices = params.getService("identity:general"); + return [...identityServices.auth.tenantRequired()]; +}