Compare commits

..

No commits in common. "55b8bb847316a5dd63ec720a8090979b8ce247d3" and "6187a77dd920ba3725ddf0e44acd33b79805156e" have entirely different histories.

36 changed files with 106 additions and 306 deletions

View File

@ -33,8 +33,8 @@
"typescript": "^6.0.2" "typescript": "^6.0.2"
}, },
"dependencies": { "dependencies": {
"@erp/auth": "workspace:*",
"@erp/core": "workspace:*", "@erp/core": "workspace:*",
"@erp/identity": "workspace:*",
"@repo/i18next": "workspace:*", "@repo/i18next": "workspace:*",
"@repo/rdx-criteria": "workspace:*", "@repo/rdx-criteria": "workspace:*",
"@repo/rdx-ddd": "workspace:*", "@repo/rdx-ddd": "workspace:*",
@ -52,4 +52,4 @@
"use-debounce": "^10.1.1", "use-debounce": "^10.1.1",
"zod": "^4.3.6" "zod": "^4.3.6"
} }
} }

View File

@ -27,7 +27,7 @@ export type CatalogsPublicServicesType = ReturnType<typeof buildCatalogsPublicSe
export const catalogsAPIModule: IModuleServer = { export const catalogsAPIModule: IModuleServer = {
name: "catalogs", name: "catalogs",
version: "1.0.0", version: "1.0.0",
dependencies: ["identity"], dependencies: [],
/** /**
* Fase de SETUP * Fase de SETUP

View File

@ -1,4 +1,3 @@
export * from "./express";
export * from "./payment-methods"; export * from "./payment-methods";
export * from "./payment-terms"; export * from "./payment-terms";
export * from "./tax-definitions"; export * from "./tax-definitions";

View File

@ -1,5 +1,5 @@
import { type StartParams, validateRequest } from "@erp/core/api"; import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
import { requireIdentityTenant } from "@erp/identity/api"; import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api";
import { type NextFunction, type Request, type Response, Router } from "express"; import { type NextFunction, type Request, type Response, Router } from "express";
import { import {
@ -30,7 +30,21 @@ export const paymentMethodsRouter = (params: StartParams) => {
// ---------------------------------------------- // ----------------------------------------------
router.use(...requireIdentityTenant(params)); 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
]);
// ---------------------------------------------- // ----------------------------------------------

View File

@ -1,5 +1,5 @@
import { type StartParams, validateRequest } from "@erp/core/api"; import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
import { requireIdentityTenant } from "@erp/identity/api"; import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api";
import type { NextFunction, Request, Response } from "express"; import type { NextFunction, Request, Response } from "express";
import { Router } from "express"; import { Router } from "express";
@ -29,7 +29,18 @@ export const paymentTermsRouter = (params: StartParams) => {
const router = Router({ mergeParams: true }); const router = Router({ mergeParams: true });
router.use(...requireIdentityTenant(params)); 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.get( router.get(
"/", "/",

View File

@ -1,5 +1,5 @@
import { type StartParams, validateRequest } from "@erp/core/api"; import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
import { requireIdentityTenant } from "@erp/identity/api"; import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api";
import type { NextFunction, Request, Response } from "express"; import type { NextFunction, Request, Response } from "express";
import { Router } from "express"; import { Router } from "express";
@ -29,7 +29,18 @@ export const taxDefinitionsRouter = (params: StartParams) => {
const router = Router({ mergeParams: true }); const router = Router({ mergeParams: true });
router.use(...requireIdentityTenant(params)); 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.get( router.get(
"/", "/",

View File

@ -1,5 +1,5 @@
import { type StartParams, validateRequest } from "@erp/core/api"; import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
import { requireIdentityTenant } from "@erp/identity/api"; import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api";
import type { NextFunction, Request, Response } from "express"; import type { NextFunction, Request, Response } from "express";
import { Router } from "express"; import { Router } from "express";
@ -29,7 +29,18 @@ export const taxRegimesRouter = (params: StartParams) => {
const router = Router({ mergeParams: true }); const router = Router({ mergeParams: true });
router.use(...requireIdentityTenant(params)); 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.get( router.get(
"/", "/",

View File

@ -81,23 +81,11 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle
const setCustomer = (customer: CustomerSelectionOption) => { const setCustomer = (customer: CustomerSelectionOption) => {
setSelectedCustomer(customer); setSelectedCustomer(customer);
// Necesitamos consultar todos los datos del cliente para
// poder rellenar los campos de la proforma
form.setValue("customerId", customer.id, { form.setValue("customerId", customer.id, {
shouldDirty: true, shouldDirty: true,
shouldTouch: true, shouldTouch: true,
shouldValidate: true, shouldValidate: true,
}); });
/*form.setValue("")
form.setValue("taxRegimeCode", customer.taxRegimeCode ?? "", {
shouldDirty: true,
shouldTouch: true,
shouldValidate: true,
});*/
}; };
const clearCustomer = () => { const clearCustomer = () => {

View File

@ -6,7 +6,7 @@ export const buildProformaCreateDefault = (): ProformaCreateForm => {
return { return {
customerId: "", customerId: "",
invoiceDate: DateHelper.buildTodayIsoDate(), invoiceDate: DateHelper.buildTodayIsoDate(),
series: "F26", series: "",
languageCode: "es", languageCode: "es",
currencyCode: "EUR", currencyCode: "EUR",
}; };

View File

@ -34,9 +34,9 @@
"typescript": "^6.0.2" "typescript": "^6.0.2"
}, },
"dependencies": { "dependencies": {
"@erp/catalogs": "workspace:*", "@erp/auth": "workspace:*",
"@erp/core": "workspace:*", "@erp/core": "workspace:*",
"@erp/identity": "workspace:*", "@erp/catalogs": "workspace:*",
"@hookform/resolvers": "^5.2.2", "@hookform/resolvers": "^5.2.2",
"@repo/i18next": "workspace:*", "@repo/i18next": "workspace:*",
"@repo/rdx-criteria": "workspace:*", "@repo/rdx-criteria": "workspace:*",
@ -56,4 +56,4 @@
"use-debounce": "^10.1.1", "use-debounce": "^10.1.1",
"zod": "^4.3.6" "zod": "^4.3.6"
} }
} }

View File

@ -10,7 +10,7 @@ export * from "./infrastructure/persistence/sequelize";
export const customersAPIModule: IModuleServer = { export const customersAPIModule: IModuleServer = {
name: "customers", name: "customers",
version: "1.0.0", version: "1.0.0",
dependencies: ["catalogs", "identity"], dependencies: ["catalogs"],
/** /**
* Fase de SETUP * Fase de SETUP

View File

@ -1,5 +1,5 @@
import { type StartParams, validateRequest } from "@erp/core/api"; import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth/api";
import { requireIdentityTenant } from "@erp/identity/api"; import { type RequestWithAuth, type StartParams, validateRequest } from "@erp/core/api";
import { type NextFunction, type Request, type Response, Router } from "express"; import { type NextFunction, type Request, type Response, Router } from "express";
import { import {
@ -29,7 +29,21 @@ export const customersRouter = (params: StartParams) => {
// ---------------------------------------------- // ----------------------------------------------
// 🔐 Autenticación + Tenancy para TODO el router // 🔐 Autenticación + Tenancy para TODO el router
router.use(...requireIdentityTenant(params)); 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
]);
// ---------------------------------------------- // ----------------------------------------------

View File

@ -2,9 +2,6 @@
* Representa un cliente seleccionable desde cualquier módulo. * Representa un cliente seleccionable desde cualquier módulo.
* Debe ser lo suficientemente rico para mostrar en UI, * Debe ser lo suficientemente rico para mostrar en UI,
* pero sin arrastrar todo el shape de Customer. * pero sin arrastrar todo el shape de Customer.
*
* No usar objetos de dominio, todo tipos primitivos y strings,
* para desacoplar de la capa de dominio.
*/ */
export interface CustomerSelectionOption { export interface CustomerSelectionOption {
id: string; id: string;

View File

@ -1 +0,0 @@
export * from "./use-get-customer-controller";

View File

@ -1,26 +0,0 @@
import { useCustomerGetQuery } from "../../../../web/shared";
import { buildCustomerData } from "../utils";
export const useGetCustomerController = (customerId: string, options?: { enabled?: boolean }) => {
const {
data,
isLoading,
isError: isLoadError,
error: loadError,
refetch,
} = useCustomerGetQuery({
id: customerId,
enabled: options?.enabled ?? true,
});
const customerData = data ? buildCustomerData(data) : undefined;
return {
customerData,
isLoading,
isLoadError,
loadError,
refetch,
};
};

View File

@ -1,64 +0,0 @@
/**
* Representa un cliente con todo el shape de Customer.
*
* No usar objetos de dominio, todo tipos primitivos y strings,
* para desacoplar de la capa de dominio.
*/
export interface CustomerData {
id: string;
status: string;
reference: string | null;
isCompany: boolean;
name: string;
tradeName: string | null;
tin: string | null;
address: {
street: string | null;
street2: string | null;
city: string | null;
province: string | null;
postalCode: string | null;
country: string | null;
};
contact: {
emailPrimary: string | null;
emailSecondary: string | null;
phonePrimary: string | null;
phoneSecondary: string | null;
mobilePrimary: string | null;
mobileSecondary: string | null;
fax: string | null;
website: string | null;
};
legalRecord: string | null;
paymentMethod: {
id: string;
name: string;
description: string;
} | null;
paymentTerm: {
id: string;
description: string;
} | null;
taxRegime: {
code: string;
description: string;
} | null;
fiscalDefaults: {
usesEquivalenceSurcharge: boolean;
usesRetention: boolean;
};
languageCode: string;
currencyCode: string;
}

View File

@ -1 +0,0 @@
export * from './customer-data.entity';

View File

@ -1 +0,0 @@
export * from "./controllers";

View File

@ -1,71 +0,0 @@
import type { Customer } from "../../../../web/shared";
import type { CustomerData } from "../entities";
/**
* Mapper explícito para desacoplar UI del shape de customers/shared.
*/
export const buildCustomerData = (customer: Customer): CustomerData => {
return {
id: customer.id,
status: customer.status,
reference: customer.reference,
isCompany: customer.isCompany,
name: customer.name,
tradeName: customer.tradeName,
tin: customer.tin,
address: {
street: customer.address.street,
street2: null,
city: customer.address.city,
province: customer.address.province,
postalCode: customer.address.postalCode,
country: customer.address.country,
},
contact: {
emailPrimary: customer.contact.primaryEmail,
emailSecondary: customer.contact.secondaryEmail,
phonePrimary: customer.contact.primaryPhone,
phoneSecondary: customer.contact.secondaryPhone,
mobilePrimary: customer.contact.primaryMobile,
mobileSecondary: customer.contact.secondaryMobile,
fax: customer.contact.fax,
website: customer.contact.website,
},
legalRecord: customer.legalRecord,
paymentMethod: customer.paymentMethod
? {
id: customer.paymentMethod.id,
name: customer.paymentMethod.name,
description: customer.paymentMethod.description,
}
: null,
paymentTerm: customer.paymentTerm
? {
id: customer.paymentTerm.id,
description: customer.paymentTerm.description,
}
: null,
taxRegime: customer.taxRegime
? {
code: customer.taxRegime.code,
description: customer.taxRegime.description,
}
: null,
fiscalDefaults: {
usesEquivalenceSurcharge: customer.fiscalDefaults.usesEquivalenceSurcharge,
usesRetention: customer.fiscalDefaults.usesRetention,
},
languageCode: customer.languageCode,
currencyCode: customer.currencyCode,
};
};

View File

@ -1 +0,0 @@
export * from "./build-customer-data";

View File

@ -2,8 +2,7 @@
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"paths": { "paths": {
"@erp/customers/*": ["./src/*"], "@erp/customers/*": ["./src/*"]
"@erp/identity/*": ["../identity/src/*"]
}, },
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",

View File

@ -34,9 +34,7 @@
"zod": "^4.3.6" "zod": "^4.3.6"
}, },
"devDependencies": { "devDependencies": {
"@types/bcrypt": "^6.0.0",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.10",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",

View File

@ -4,4 +4,3 @@ export * from "./companies";
export * from "./company-memberships"; export * from "./company-memberships";
export * from "./permissions"; export * from "./permissions";
export * from "./roles"; export * from "./roles";
export * from "./services";

View File

@ -1,10 +0,0 @@
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;
}

View File

@ -1,5 +0,0 @@
import type { IIdentityAuthPublicServices } from "./identity-auth-public-services.interface";
export interface IIdentityPublicServices {
auth: IIdentityAuthPublicServices;
}

View File

@ -1,2 +0,0 @@
export * from "./identity-auth-public-services.interface";
export * from "./identity-public-services.interface";

View File

@ -1,16 +1,13 @@
import type { IModuleServer } from "@erp/core/api"; import type { IModuleServer } from "@erp/core/api";
import type { IIdentityPublicServices } from "./application";
export * from "./application"; export * from "./application";
export * from "./domain"; export * from "./domain";
export * from "./infrastructure"; export * from "./infrastructure";
import { identityAuthRouter, models } from "./infrastructure"; import { identityAuthRouter, models } from "./infrastructure";
import { buildIdentityDependencies, buildIdentityPublicServices } from "./infrastructure/di"; import { buildIdentityDependencies } from "./infrastructure/di";
export * from "./infrastructure/persistence/sequelize"; export * from "./infrastructure/persistence/sequelize";
export type { IIdentityAuthPublicServices, IIdentityPublicServices } from "./application";
export type IdentityPublicServicesType = IIdentityPublicServices;
export const identityAPIModule: IModuleServer = { export const identityAPIModule: IModuleServer = {
name: "identity", name: "identity",
@ -31,7 +28,8 @@ export const identityAPIModule: IModuleServer = {
const internal = buildIdentityDependencies(params); const internal = buildIdentityDependencies(params);
// 2) Servicios públicos (Application Services) // 2) Servicios públicos (Application Services)
const identityServices: IIdentityPublicServices = buildIdentityPublicServices(internal); const identityServices = {};
//const identityServices: IIdentityPublicServices = {}; //buildIdentityPublicServices(params, internal);
logger.info("🚀 Identity module dependencies registered", { logger.info("🚀 Identity module dependencies registered", {
label: this.name, label: this.name,

View File

@ -1,35 +0,0 @@
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),
};
}

View File

@ -2,7 +2,6 @@ import { type ModuleParams, buildTransactionManager } from "@erp/core/api";
import { import {
AccountAuthenticator, AccountAuthenticator,
type IAccessTokenVerifier,
type GetCurrentSessionUseCase, type GetCurrentSessionUseCase,
GetCurrentSessionUseCase as GetCurrentSessionUseCaseImpl, GetCurrentSessionUseCase as GetCurrentSessionUseCaseImpl,
type LoginWithPasswordUseCase, type LoginWithPasswordUseCase,
@ -13,7 +12,6 @@ import {
RefreshSessionUseCase as RefreshSessionUseCaseImpl, RefreshSessionUseCase as RefreshSessionUseCaseImpl,
SessionIssuer, SessionIssuer,
} from "../../application/authentication"; } from "../../application/authentication";
import type { IAccountRepository } from "../../application/accounts";
import { identityAuthenticateRequest } from "../express/middlewares"; import { identityAuthenticateRequest } from "../express/middlewares";
import { import {
SequelizeAccountDomainMapper, SequelizeAccountDomainMapper,
@ -32,10 +30,6 @@ import {
export type IdentityInternalDeps = { export type IdentityInternalDeps = {
auth: { auth: {
refreshTokenExpiration: string; refreshTokenExpiration: string;
authenticateUserDependencies: {
accessTokenVerifier: IAccessTokenVerifier;
accountRepository: IAccountRepository;
};
authenticateRequest: ReturnType<typeof identityAuthenticateRequest>; authenticateRequest: ReturnType<typeof identityAuthenticateRequest>;
useCases: { useCases: {
loginWithPassword: () => LoginWithPasswordUseCase; loginWithPassword: () => LoginWithPasswordUseCase;
@ -85,10 +79,6 @@ export const buildIdentityDependencies = (params: ModuleParams): IdentityInterna
return { return {
auth: { auth: {
refreshTokenExpiration: config.auth.jwt.refreshExpiration, refreshTokenExpiration: config.auth.jwt.refreshExpiration,
authenticateUserDependencies: {
accessTokenVerifier,
accountRepository,
},
authenticateRequest: identityAuthenticateRequest({ authenticateRequest: identityAuthenticateRequest({
accessTokenVerifier, accessTokenVerifier,
accountRepository, accountRepository,

View File

@ -1,2 +1 @@
export * from "./identity.di"; export * from "./identity.di";
export * from "./identity-public-services";

View File

@ -1,5 +1,4 @@
export * from "./authenticate-user.middleware"; export * from "./authenticate-user.middleware";
export * from "./require-authenticated.middleware"; export * from "./require-authenticated.middleware";
export * from "./require-company-context.middleware"; export * from "./require-company-context.middleware";
export * from "./require-identity-middlewares";
export * from "./identity-authenticate-request"; export * from "./identity-authenticate-request";

View File

@ -1,12 +1,13 @@
import { ExpressController, UnauthorizedApiError, type RequestWithAuth } from "@erp/core/api"; import { ExpressController, UnauthorizedApiError, type RequestWithAuth } from "@erp/core/api";
import type { NextFunction, Request, Response } from "express"; import type { NextFunction, Response } from "express";
export function requireAuthenticated() { export function requireAuthenticated() {
return (req: Request, res: Response, next: NextFunction) => { return (req: RequestWithAuth, res: Response, next: NextFunction) => {
if (!(req as RequestWithAuth).user) { if (!req.user) {
return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res); return ExpressController.errorResponse(new UnauthorizedApiError("Unauthorized"), req, res);
} }
next(); next();
}; };
} }

View File

@ -1,12 +1,12 @@
import { ExpressController, ForbiddenApiError, type RequestWithAuth } from "@erp/core/api"; import { ExpressController, ForbiddenApiError, type RequestWithAuth } from "@erp/core/api";
import type { NextFunction, Request, Response } from "express"; import type { NextFunction, Response } from "express";
export function requireCompanyContext() { export function requireCompanyContext() {
return (req: Request, res: Response, next: NextFunction) => { return (req: RequestWithAuth, res: Response, next: NextFunction) => {
/** /**
* TODO: validar membership real cuando exista persistencia de CompanyMembership. * TODO: validar membership real cuando exista persistencia de CompanyMembership.
*/ */
if (!(req as RequestWithAuth).user?.companyId) { if (!req.user?.companyId) {
return ExpressController.errorResponse( return ExpressController.errorResponse(
new ForbiddenApiError("Company context required"), new ForbiddenApiError("Company context required"),
req, req,
@ -17,3 +17,4 @@ export function requireCompanyContext() {
next(); next();
}; };
} }

View File

@ -1,14 +0,0 @@
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<IIdentityPublicServices>("identity:general");
return [...identityServices.auth.authenticatedOnly()];
}
export function requireIdentityTenant(params: StartParams): RequestHandler[] {
const identityServices = params.getService<IIdentityPublicServices>("identity:general");
return [...identityServices.auth.tenantRequired()];
}

View File

@ -1,7 +1,7 @@
import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api"; import { type MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
import { import {
UniqueID, UniqueID,
UtcDateTime, UtcDate,
ValidationErrorCollection, ValidationErrorCollection,
type ValidationErrorDetail, type ValidationErrorDetail,
extractOrPushError, extractOrPushError,
@ -10,7 +10,11 @@ import {
} from "@repo/rdx-ddd"; } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils"; import { Result } from "@repo/rdx-utils";
import { RefreshToken, RefreshTokenHash, type RefreshTokenInternalProps } from "../../../../domain"; import {
RefreshToken,
RefreshTokenHash,
type RefreshTokenInternalProps,
} from "../../../../domain";
import type { RefreshTokenCreationAttributes, RefreshTokenModel } from "../models"; import type { RefreshTokenCreationAttributes, RefreshTokenModel } from "../models";
function toUtcIsoString(value: Date | string): string { function toUtcIsoString(value: Date | string): string {
@ -41,13 +45,13 @@ export class SequelizeRefreshTokenDomainMapper extends SequelizeDomainMapper<
errors errors
); );
const expiresAt = extractOrPushError( const expiresAt = extractOrPushError(
UtcDateTime.createFromISO(toUtcIsoString(source.expires_at)), UtcDate.createFromISO(toUtcIsoString(source.expires_at)),
"expires_at", "expires_at",
errors errors
); );
const revokedAt = extractOrPushError( const revokedAt = extractOrPushError(
maybeFromNullableResult(source.revoked_at, (value) => maybeFromNullableResult(source.revoked_at, (value) =>
UtcDateTime.createFromISO(toUtcIsoString(value)) UtcDate.createFromISO(toUtcIsoString(value))
), ),
"revoked_at", "revoked_at",
errors errors
@ -58,7 +62,7 @@ export class SequelizeRefreshTokenDomainMapper extends SequelizeDomainMapper<
errors errors
); );
const createdAt = extractOrPushError( const createdAt = extractOrPushError(
UtcDateTime.createFromISO(toUtcIsoString(source.created_at)), UtcDate.createFromISO(toUtcIsoString(source.created_at)),
"created_at", "created_at",
errors errors
); );
@ -94,9 +98,7 @@ export class SequelizeRefreshTokenDomainMapper extends SequelizeDomainMapper<
token_hash: source.tokenHash.toPrimitive(), token_hash: source.tokenHash.toPrimitive(),
expires_at: source.expiresAt.toISOString(), expires_at: source.expiresAt.toISOString(),
revoked_at: maybeToNullable(source.revokedAt, (revokedAt) => revokedAt.toISOString()), revoked_at: maybeToNullable(source.revokedAt, (revokedAt) => revokedAt.toISOString()),
replaced_by_token_id: maybeToNullable(source.replacedByTokenId, (value) => replaced_by_token_id: maybeToNullable(source.replacedByTokenId, (value) => value.toPrimitive()),
value.toPrimitive()
),
created_at: source.createdAt.toISOString(), created_at: source.createdAt.toISOString(),
}); });
} }