diff --git a/apps/bff/src/infra/utils/user-mapper.util.ts b/apps/bff/src/infra/utils/user-mapper.util.ts index ea8db105..2bc81869 100644 --- a/apps/bff/src/infra/utils/user-mapper.util.ts +++ b/apps/bff/src/infra/utils/user-mapper.util.ts @@ -1,4 +1,4 @@ -import type { AuthenticatedUser, User } from "@customer-portal/domain"; +import type { AuthenticatedUser, User } from "@customer-portal/domain/auth"; import type { User as PrismaUser } from "@prisma/client"; export function mapPrismaUserToSharedUser(user: PrismaUser): User { diff --git a/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts b/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts deleted file mode 100644 index 87fb4c9d..00000000 --- a/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts +++ /dev/null @@ -1,330 +0,0 @@ -// Freebit API Type Definitions (cleaned) - -export interface FreebitAuthRequest { - oemId: string; // 4-char alphanumeric ISP identifier - oemKey: string; // 32-char auth key -} - -export interface FreebitAuthResponse { - resultCode: string; - status: { - message: string; - statusCode: string | number; - }; - authKey: string; // Token for subsequent API calls -} - -export interface FreebitAccountDetailsRequest { - authKey: string; - version?: string | number; // Docs recommend "2" - requestDatas: Array<{ - kind: "MASTER" | "MVNO"; - account?: string | number; - }>; -} - -export interface FreebitAccountDetail { - kind: "MASTER" | "MVNO"; - account: string | number; - state: "active" | "suspended" | "temporary" | "waiting" | "obsolete"; - status?: "active" | "suspended" | "temporary" | "waiting" | "obsolete"; - startDate?: string | number; - relationCode?: string; - resultCode?: string | number; - planCode?: string; - planName?: string; - iccid?: string | number; - imsi?: string | number; - eid?: string; - contractLine?: string; - size?: "standard" | "nano" | "micro" | "esim"; - simSize?: "standard" | "nano" | "micro" | "esim"; - msisdn?: string | number; - sms?: number; // 10=active, 20=inactive - talk?: number; // 10=active, 20=inactive - ipv4?: string; - ipv6?: string; - quota?: number; // Remaining quota - remainingQuotaMb?: string | number | null; - remainingQuotaKb?: string | number | null; - voicemail?: "10" | "20" | number | null; - voiceMail?: "10" | "20" | number | null; - callwaiting?: "10" | "20" | number | null; - callWaiting?: "10" | "20" | number | null; - worldwing?: "10" | "20" | number | null; - worldWing?: "10" | "20" | number | null; - networkType?: string; - async?: { func: string; date: string | number }; -} - -export interface FreebitAccountDetailsResponse { - resultCode: string; - status: { - message: string; - statusCode: string | number; - }; - masterAccount?: string; - responseDatas: FreebitAccountDetail[]; -} - -export interface FreebitTrafficInfoRequest { - authKey: string; - account: string; -} - -export interface FreebitTrafficInfoResponse { - resultCode: string; - status: { - message: string; - statusCode: string | number; - }; - account: string; - traffic: { - today: string; // Today's usage in KB - inRecentDays: string; // Comma-separated recent days usage - blackList: string; // 10=blacklisted, 20=not blacklisted - }; -} - -export interface FreebitTopUpRequest { - authKey: string; - account: string; - quota: number; // KB units (e.g., 102400 for 100MB) - quotaCode?: string; // Campaign code - expire?: string; // YYYYMMDD format - runTime?: string; // Scheduled execution time (YYYYMMDDHHmm) -} - -export interface FreebitTopUpResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; -} - -// AddSpec request for updating SIM options/features immediately -export interface FreebitAddSpecRequest { - authKey: string; - account: string; - kind?: string; // e.g. 'MVNO' - // Feature flags: 10 = enabled, 20 = disabled - voiceMail?: "10" | "20"; - voicemail?: "10" | "20"; - callWaiting?: "10" | "20"; - callwaiting?: "10" | "20"; - worldWing?: "10" | "20"; - worldwing?: "10" | "20"; - contractLine?: string; // '4G' or '5G' -} - -export interface FreebitAddSpecResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; -} - -export interface FreebitQuotaHistoryRequest { - authKey: string; - account: string; - fromDate: string; - toDate: string; -} - -export interface FreebitQuotaHistoryItem { - quota: string; // KB as string - date: string; - expire: string; - quotaCode: string; -} - -export interface FreebitQuotaHistoryResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; - total: string | number; - count: string | number; - quotaHistory: FreebitQuotaHistoryItem[]; -} - -export interface FreebitPlanChangeRequest { - authKey: string; - account: string; - plancode: string; - globalip?: "0" | "1"; // 0=no IP, 1=assign global IP - runTime?: string; // YYYYMMDD - optional -} - -export interface FreebitPlanChangeResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; - ipv4?: string; - ipv6?: string; -} - -export interface FreebitPlanChangePayload { - requestDatas: Array<{ - kind: "MVNO"; - account: string; - newPlanCode: string; - assignGlobalIp: boolean; - scheduledAt?: string; - }>; -} - -export interface FreebitAddSpecPayload { - requestDatas: Array<{ - kind: "MVNO"; - account: string; - specCode: string; - enabled?: boolean; - networkType?: "4G" | "5G"; - }>; -} - -export interface FreebitCancelPlanPayload { - requestDatas: Array<{ - kind: "MVNO"; - account: string; - runDate: string; - }>; -} - -export interface FreebitEsimReissuePayload { - requestDatas: Array<{ - kind: "MVNO"; - account: string; - newEid: string; - oldEid?: string; - planCode?: string; - }>; -} - -export interface FreebitContractLineChangeRequest { - authKey: string; - account: string; - contractLine: "4G" | "5G"; - productNumber?: string; - eid?: string; -} - -export interface FreebitContractLineChangeResponse { - resultCode: string | number; - status?: { message?: string; statusCode?: string | number }; - statusCode?: string | number; - message?: string; -} - -export interface FreebitCancelPlanRequest { - authKey: string; - account: string; - runTime?: string; // YYYYMMDD - optional -} - -export interface FreebitCancelPlanResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; -} - -// PA02-04: Account Cancellation (master/cnclAcnt) -export interface FreebitCancelAccountRequest { - authKey: string; - kind: string; // e.g., 'MVNO' - account: string; - runDate?: string; // YYYYMMDD -} - -export interface FreebitCancelAccountResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; -} - -export interface FreebitEsimReissueRequest { - authKey: string; - requestDatas: Array<{ - kind: "MVNO"; - account: string; - newEid?: string; - oldEid?: string; - planCode?: string; - }>; -} - -export interface FreebitEsimReissueResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; -} - -export interface FreebitEsimAddAccountRequest { - authKey: string; - aladinOperated: string; // '10' for issue, '20' for no-issue - account: string; - eid: string; - addKind: "N" | "R"; // N = new, R = reissue - shipDate?: string; - planCode?: string; - contractLine?: string; - mnp?: { - reserveNumber: string; - reserveExpireDate: string; - }; -} - -export interface FreebitEsimAddAccountResponse { - resultCode: string; - status: { message: string; statusCode: string | number }; -} - -// PA05-41 eSIM Account Activation (addAcct) -export interface FreebitEsimAccountActivationRequest { - authKey: string; - aladinOperated: string; // '10' issue, '20' no-issue - masterAccount?: string; - masterPassword?: string; - createType: string; - eid?: string; // required for reissue/exchange per business rules - account: string; // MSISDN - simkind: string; - repAccount?: string; - size?: string; - addKind?: string; // e.g., 'R' for reissue - oldEid?: string; - oldProductNumber?: string; - mnp?: { reserveNumber: string; reserveExpireDate: string }; - firstnameKanji?: string; - lastnameKanji?: string; - firstnameZenKana?: string; - lastnameZenKana?: string; - gender?: string; // 'M' | 'F' | etc - birthday?: string; // YYYYMMDD - shipDate?: string; // YYYYMMDD - planCode?: string; - deliveryCode?: string; - globalIp?: string; // '10' none, '20' with global IP (env-specific mapping) - contractLine?: string; // '4G' | '5G' -} - -export interface FreebitEsimAccountActivationResponse { - resultCode: string; - status?: { - message?: string; - statusCode?: string | number; - }; - statusCode?: string | number; - message?: string; -} - -// Portal-specific types for SIM management -export type { SimDetails, SimUsage, SimTopUpHistory } from "@customer-portal/domain/sim"; - -// Error handling -export interface FreebitError extends Error { - resultCode: string; - statusCode: string | number; - freebititMessage: string; -} - -// Configuration -export interface FreebitConfig { - baseUrl: string; - oemId: string; - oemKey: string; - timeout: number; - retryAttempts: number; - detailsEndpoint?: string; -} diff --git a/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts b/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts index 1e496877..3d3b118e 100644 --- a/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts +++ b/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts @@ -3,7 +3,7 @@ import type { FreebitAccountDetailsResponse, FreebitTrafficInfoResponse, FreebitQuotaHistoryResponse, -} from "../interfaces/freebit.types"; +} from "@customer-portal/domain/sim/providers/freebit"; import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; import { Providers } from "@customer-portal/domain/sim"; diff --git a/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts b/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts index dd3017fd..a53fe05e 100644 --- a/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts +++ b/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts @@ -15,17 +15,22 @@ import type { FreebitEsimReissueResponse, FreebitEsimAddAccountResponse, FreebitEsimAccountActivationResponse, -} from "../interfaces/freebit.types"; +} from "@customer-portal/domain/sim/providers/freebit"; +import type { + FreebitTopUpRequest, + FreebitPlanChangeRequest, + FreebitCancelPlanRequest, + FreebitEsimReissueRequest, + FreebitEsimActivationRequest, + FreebitEsimActivationParams, + FreebitAccountDetailsRequest, + FreebitTrafficInfoRequest, + FreebitQuotaHistoryRequest, + FreebitQuotaHistoryResponse, + FreebitEsimAddAccountRequest, +} from "@customer-portal/domain/sim/providers/freebit"; import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; -import { - freebitAccountDetailsRequestSchema, - freebitAddSpecRequestSchema, - freebitCancelPlanRequestSchema, - freebitEsimReissueRequestSchema, - freebitPlanChangeRequestSchema, - freebitTopUpRequestPayloadSchema, - freebitTrafficInfoRequestSchema, -} from "@customer-portal/domain/sim"; +import { Providers } from "@customer-portal/domain/sim"; @Injectable() export class FreebitOperationsService { @@ -41,7 +46,7 @@ export class FreebitOperationsService { */ async getSimDetails(account: string): Promise { try { - const request = freebitAccountDetailsRequestSchema.parse({ + const request: FreebitAccountDetailsRequest = Providers.Freebit.schemas.accountDetails.parse({ version: "2", requestDatas: [{ kind: "MVNO", account }], }); @@ -113,7 +118,7 @@ export class FreebitOperationsService { */ async getSimUsage(account: string): Promise { try { - const request = freebitTrafficInfoRequestSchema.parse({ account }); + const request: FreebitTrafficInfoRequest = Providers.Freebit.schemas.trafficInfo.parse({ account }); const response = await this.client.makeAuthenticatedRequest< FreebitTrafficInfoResponse, @@ -140,7 +145,7 @@ export class FreebitOperationsService { options: { campaignCode?: string; expiryDate?: string; scheduledAt?: string } = {} ): Promise { try { - const payload = freebitTopUpRequestPayloadSchema.parse({ account, quotaMb, options }); + const payload: FreebitTopUpRequest = Providers.Freebit.schemas.topUp.parse({ account, quotaMb, options }); const quotaKb = Math.round(payload.quotaMb * 1024); const baseRequest = { account: payload.account, @@ -187,11 +192,11 @@ export class FreebitOperationsService { toDate: string ): Promise { try { - const request: Omit = { + const request: FreebitQuotaHistoryRequest = Providers.Freebit.schemas.quotaHistory.parse({ account, fromDate, toDate, - }; + }); const response = await this.client.makeAuthenticatedRequest< FreebitQuotaHistoryResponse, @@ -220,18 +225,14 @@ export class FreebitOperationsService { options: { assignGlobalIp?: boolean; scheduledAt?: string } = {} ): Promise<{ ipv4?: string; ipv6?: string }> { // Import and validate with the schema - const { freebitPlanChangeRequestSchema } = await import( - "@customer-portal/domain/sim/providers/freebit/requests" - ); + const parsed: FreebitPlanChangeRequest = Providers.Freebit.schemas.planChange.parse({ + account, + newPlanCode, + assignGlobalIp: options.assignGlobalIp, + scheduledAt: options.scheduledAt, + }); try { - const parsed = freebitPlanChangeRequestSchema.parse({ - account, - newPlanCode, - assignGlobalIp: options.assignGlobalIp, - scheduledAt: options.scheduledAt, - }); - const request = { account: parsed.account, plancode: parsed.newPlanCode, @@ -280,11 +281,7 @@ export class FreebitOperationsService { ): Promise { try { // Import and validate with the new schema - const { freebitSimFeaturesRequestSchema } = await import( - "@customer-portal/domain/sim/providers/freebit/requests" - ); - - const validatedFeatures = freebitSimFeaturesRequestSchema.parse({ + const parsed = Providers.Freebit.schemas.simFeatures.parse({ account, voiceMailEnabled: features.voiceMailEnabled, callWaitingEnabled: features.callWaitingEnabled, @@ -293,17 +290,17 @@ export class FreebitOperationsService { }); const payload: Record = { - account: validatedFeatures.account, + account: parsed.account, }; - if (typeof validatedFeatures.voiceMailEnabled === "boolean") { - const flag = validatedFeatures.voiceMailEnabled ? "10" : "20"; + if (typeof parsed.voiceMailEnabled === "boolean") { + const flag = parsed.voiceMailEnabled ? "10" : "20"; payload.voiceMail = flag; payload.voicemail = flag; } - if (typeof validatedFeatures.callWaitingEnabled === "boolean") { - const flag = validatedFeatures.callWaitingEnabled ? "10" : "20"; + if (typeof parsed.callWaitingEnabled === "boolean") { + const flag = parsed.callWaitingEnabled ? "10" : "20"; payload.callWaiting = flag; payload.callwaiting = flag; } @@ -346,7 +343,7 @@ export class FreebitOperationsService { */ async cancelSim(account: string, scheduledAt?: string): Promise { try { - const parsed = freebitCancelPlanRequestSchema.parse({ + const parsed: FreebitCancelPlanRequest = Providers.Freebit.schemas.cancelPlan.parse({ account, runDate: scheduledAt, }); @@ -410,7 +407,7 @@ export class FreebitOperationsService { options: { oldProductNumber?: string; oldEid?: string; planCode?: string } = {} ): Promise { try { - const parsed = freebitEsimReissueRequestSchema.parse({ + const parsed: FreebitEsimReissueRequest = Providers.Freebit.schemas.esimReissue.parse({ account, newEid, oldEid: options.oldEid, @@ -418,15 +415,20 @@ export class FreebitOperationsService { oldProductNumber: options.oldProductNumber, }); - const request: Omit = { + const requestPayload = Providers.Freebit.schemas.esimAddAccount.parse({ aladinOperated: "20", account: parsed.account, eid: parsed.newEid, addKind: "R", planCode: parsed.planCode, + }); + + const request: FreebitEsimAddAccountRequest = { + ...requestPayload, + authKey: await this.auth.getAuthKey(), }; - await this.client.makeAuthenticatedRequest( + await this.client.makeAuthenticatedRequest( "/mvno/esim/addAcnt/", request ); @@ -480,12 +482,7 @@ export class FreebitOperationsService { } = params; // Import schemas dynamically to avoid circular dependencies - const { freebitEsimActivationParamsSchema, freebitEsimActivationRequestSchema } = await import( - "@customer-portal/domain/sim/providers/freebit/requests" - ); - - // Validate input parameters - const validatedParams = freebitEsimActivationParamsSchema.parse({ + const validatedParams: FreebitEsimActivationParams = Providers.Freebit.schemas.esimActivationParams.parse({ account, eid, planCode, @@ -501,12 +498,12 @@ export class FreebitOperationsService { } try { - const payload: FreebitEsimAccountActivationRequest = { + const payload: FreebitEsimActivationRequest = { authKey: await this.auth.getAuthKey(), aladinOperated: validatedParams.aladinOperated, createType: "new", - eid: validatedParams.eid, account: validatedParams.account, + eid: validatedParams.eid, simkind: "esim", planCode: validatedParams.planCode, contractLine: validatedParams.contractLine, @@ -516,12 +513,12 @@ export class FreebitOperationsService { }; // Validate the full API request payload - freebitEsimActivationRequestSchema.parse(payload); + Providers.Freebit.schemas.esimActivationRequest.parse(payload); // Use JSON request for PA05-41 await this.client.makeAuthenticatedJsonRequest< FreebitEsimAccountActivationResponse, - FreebitEsimAccountActivationRequest + typeof payload >("/mvno/esim/addAcct/", payload); this.logger.log("Successfully activated new eSIM account via PA05-41", { diff --git a/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts b/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts index 1b1a10e6..e34eb654 100644 --- a/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts +++ b/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts @@ -1,7 +1,7 @@ import { Injectable } from "@nestjs/common"; import { FreebitOperationsService } from "./freebit-operations.service"; import { FreebitMapperService } from "./freebit-mapper.service"; -import type { SimDetails, SimUsage, SimTopUpHistory } from "../interfaces/freebit.types"; +import type { SimDetails, SimUsage, SimTopUpHistory } from "@customer-portal/domain/sim"; @Injectable() export class FreebitOrchestratorService { diff --git a/apps/bff/src/modules/auth/application/auth.facade.ts b/apps/bff/src/modules/auth/application/auth.facade.ts index 46fb5fb1..def67481 100644 --- a/apps/bff/src/modules/auth/application/auth.facade.ts +++ b/apps/bff/src/modules/auth/application/auth.facade.ts @@ -11,11 +11,17 @@ import { getErrorMessage } from "@bff/core/utils/error.util"; import { Logger } from "nestjs-pino"; import { sanitizeWhmcsRedirectPath } from "@bff/core/utils/sso.util"; import { - type SignupRequestInput, - type ValidateSignupRequestInput, - type LinkWhmcsRequestInput, - type SetPasswordRequestInput, -} from "@customer-portal/domain"; + type SignupRequest, + type ValidateSignupRequest, + type LinkWhmcsRequest, + type SetPasswordRequest, + type ChangePasswordRequest, + signupRequestSchema, + validateSignupRequestSchema, + linkWhmcsRequestSchema, + setPasswordRequestSchema, + changePasswordRequestSchema, +} from "@customer-portal/domain/auth"; import type { User as PrismaUser } from "@prisma/client"; import type { Request } from "express"; @@ -96,11 +102,11 @@ export class AuthFacade { }; } - async validateSignup(validateData: ValidateSignupRequestInput, request?: Request) { + async validateSignup(validateData: ValidateSignupRequest, request?: Request) { return this.signupWorkflow.validateSignup(validateData, request); } - async signup(signupData: SignupRequestInput, request?: Request) { + async signup(signupData: SignupRequest, request?: Request) { return this.signupWorkflow.signup(signupData, request); } @@ -161,7 +167,7 @@ export class AuthFacade { }; } - async linkWhmcsUser(linkData: LinkWhmcsRequestInput) { + async linkWhmcsUser(linkData: LinkWhmcsRequest) { return this.whmcsLinkWorkflow.linkWhmcsUser(linkData.email, linkData.password); } @@ -169,7 +175,7 @@ export class AuthFacade { return this.passwordWorkflow.checkPasswordNeeded(email); } - async setPassword(setPasswordData: SetPasswordRequestInput) { + async setPassword(setPasswordData: SetPasswordRequest) { return this.passwordWorkflow.setPassword(setPasswordData.email, setPasswordData.password); } @@ -436,13 +442,8 @@ export class AuthFacade { }; } - async changePassword( - userId: string, - currentPassword: string, - newPassword: string, - request?: Request - ) { - return this.passwordWorkflow.changePassword(userId, currentPassword, newPassword, request); + async changePassword(userId: string, data: ChangePasswordRequest, request?: Request) { + return this.passwordWorkflow.changePassword(userId, data, request); } async refreshTokens( @@ -464,7 +465,7 @@ export class AuthFacade { * Preflight validation for signup. No side effects. * Returns a clear nextAction for the UI and detailed flags. */ - async signupPreflight(signupData: SignupRequestInput) { + async signupPreflight(signupData: SignupRequest) { return this.signupWorkflow.signupPreflight(signupData); } } diff --git a/apps/bff/src/modules/auth/auth.types.ts b/apps/bff/src/modules/auth/auth.types.ts index 9e5fa9bd..a5d88fb2 100644 --- a/apps/bff/src/modules/auth/auth.types.ts +++ b/apps/bff/src/modules/auth/auth.types.ts @@ -1,4 +1,4 @@ -import type { AuthenticatedUser } from "@customer-portal/domain"; +import type { AuthenticatedUser } from "@customer-portal/domain/auth"; import type { Request } from "express"; export type RequestWithUser = Request & { user: AuthenticatedUser }; diff --git a/apps/bff/src/modules/auth/infra/token/token.service.ts b/apps/bff/src/modules/auth/infra/token/token.service.ts index 97f25470..fddf5f8d 100644 --- a/apps/bff/src/modules/auth/infra/token/token.service.ts +++ b/apps/bff/src/modules/auth/infra/token/token.service.ts @@ -9,7 +9,7 @@ import { ConfigService } from "@nestjs/config"; import { Redis } from "ioredis"; import { Logger } from "nestjs-pino"; import { randomBytes, createHash } from "crypto"; -import type { AuthTokens, AuthenticatedUser } from "@customer-portal/domain"; +import type { AuthTokens, AuthenticatedUser } from "@customer-portal/domain/auth"; import { UsersService } from "@bff/modules/users/users.service"; import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util"; diff --git a/apps/bff/src/modules/auth/infra/workflows/workflows/password-workflow.service.ts b/apps/bff/src/modules/auth/infra/workflows/workflows/password-workflow.service.ts index cd4132e2..463df8eb 100644 --- a/apps/bff/src/modules/auth/infra/workflows/workflows/password-workflow.service.ts +++ b/apps/bff/src/modules/auth/infra/workflows/workflows/password-workflow.service.ts @@ -10,7 +10,12 @@ import { EmailService } from "@bff/infra/email/email.service"; import { getErrorMessage } from "@bff/core/utils/error.util"; import { AuthTokenService } from "../../token/token.service"; import { AuthRateLimitService } from "../../rate-limiting/auth-rate-limit.service"; -import { type AuthTokens, type UserProfile } from "@customer-portal/domain"; +import { + type AuthTokens, + type UserProfile, + type ChangePasswordRequest, + changePasswordRequestSchema, +} from "@customer-portal/domain/auth"; import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util"; export interface PasswordChangeResult { @@ -152,8 +157,7 @@ export class PasswordWorkflowService { async changePassword( userId: string, - currentPassword: string, - newPassword: string, + data: ChangePasswordRequest, request?: Request ): Promise { const user = await this.usersService.findByIdInternal(userId); @@ -166,6 +170,9 @@ export class PasswordWorkflowService { throw new BadRequestException("No password set. Please set a password first."); } + const parsed = changePasswordRequestSchema.parse(data); + const { currentPassword, newPassword } = parsed; + const isCurrentValid = await bcrypt.compare(currentPassword, user.passwordHash); if (!isCurrentValid) { await this.auditService.logAuthEvent( diff --git a/apps/bff/src/modules/auth/infra/workflows/workflows/signup-workflow.service.ts b/apps/bff/src/modules/auth/infra/workflows/workflows/signup-workflow.service.ts index 66a57e36..7f6f8ef8 100644 --- a/apps/bff/src/modules/auth/infra/workflows/workflows/signup-workflow.service.ts +++ b/apps/bff/src/modules/auth/infra/workflows/workflows/signup-workflow.service.ts @@ -20,11 +20,11 @@ import { AuthRateLimitService } from "../../rate-limiting/auth-rate-limit.servic import { getErrorMessage } from "@bff/core/utils/error.util"; import { signupRequestSchema, - type SignupRequestInput, - type ValidateSignupRequestInput, + type SignupRequest, + type ValidateSignupRequest, type AuthTokens, type UserProfile, -} from "@customer-portal/domain"; +} from "@customer-portal/domain/auth"; import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util"; import type { User as PrismaUser } from "@prisma/client"; @@ -53,7 +53,7 @@ export class SignupWorkflowService { @Inject(Logger) private readonly logger: Logger ) {} - async validateSignup(validateData: ValidateSignupRequestInput, request?: Request) { + async validateSignup(validateData: ValidateSignupRequest, request?: Request) { const { sfNumber } = validateData; try { @@ -137,7 +137,7 @@ export class SignupWorkflowService { } } - async signup(signupData: SignupRequestInput, request?: Request): Promise { + async signup(signupData: SignupRequest, request?: Request): Promise { if (request) { await this.authRateLimitService.consumeSignupAttempt(request); } @@ -379,7 +379,7 @@ export class SignupWorkflowService { } } - async signupPreflight(signupData: SignupRequestInput) { + async signupPreflight(signupData: SignupRequest) { const { email, sfNumber } = signupData; const normalizedEmail = email.toLowerCase().trim(); @@ -477,7 +477,7 @@ export class SignupWorkflowService { return result; } - private validateSignupData(signupData: SignupRequestInput) { + private validateSignupData(signupData: SignupRequest) { const validation = signupRequestSchema.safeParse(signupData); if (!validation.success) { const message = diff --git a/apps/bff/src/modules/auth/infra/workflows/workflows/whmcs-link-workflow.service.ts b/apps/bff/src/modules/auth/infra/workflows/workflows/whmcs-link-workflow.service.ts index 675e413b..891801f4 100644 --- a/apps/bff/src/modules/auth/infra/workflows/workflows/whmcs-link-workflow.service.ts +++ b/apps/bff/src/modules/auth/infra/workflows/workflows/whmcs-link-workflow.service.ts @@ -12,7 +12,7 @@ import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service"; import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service"; import { getErrorMessage } from "@bff/core/utils/error.util"; import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util"; -import type { UserProfile } from "@customer-portal/domain"; +import type { UserProfile } from "@customer-portal/domain/auth"; import type { WhmcsClientResponse } from "@bff/integrations/whmcs/types/whmcs-api.types"; @Injectable() diff --git a/apps/bff/src/modules/auth/presentation/http/auth.controller.ts b/apps/bff/src/modules/auth/presentation/http/auth.controller.ts index d6ae3d92..0c465e94 100644 --- a/apps/bff/src/modules/auth/presentation/http/auth.controller.ts +++ b/apps/bff/src/modules/auth/presentation/http/auth.controller.ts @@ -32,20 +32,20 @@ import { accountStatusRequestSchema, ssoLinkRequestSchema, checkPasswordNeededRequestSchema, - type SignupRequestInput, - type PasswordResetRequestInput, - type PasswordResetInput, - type SetPasswordRequestInput, - type LinkWhmcsRequestInput, - type ChangePasswordRequestInput, - type ValidateSignupRequestInput, - type AccountStatusRequestInput, - type SsoLinkRequestInput, - type CheckPasswordNeededRequestInput, refreshTokenRequestSchema, - type RefreshTokenRequestInput, -} from "@customer-portal/domain"; -import type { AuthTokens } from "@customer-portal/domain"; + type SignupRequest, + type PasswordResetRequest, + type PasswordReset, + type SetPasswordRequest, + type LinkWhmcsRequest, + type ChangePasswordRequest, + type ValidateSignupRequest, + type AccountStatusRequest, + type SsoLinkRequest, + type CheckPasswordNeededRequest, + type RefreshTokenRequest, + type AuthTokens, +} from "@customer-portal/domain/auth"; type RequestWithCookies = Request & { cookies: Record; @@ -123,7 +123,7 @@ export class AuthController { @UseGuards(AuthThrottleGuard) @Throttle({ default: { limit: 20, ttl: 600000 } }) // 20 validations per 10 minutes per IP @UsePipes(new ZodValidationPipe(validateSignupRequestSchema)) - async validateSignup(@Body() validateData: ValidateSignupRequestInput, @Req() req: Request) { + async validateSignup(@Body() validateData: ValidateSignupRequest, @Req() req: Request) { return this.authFacade.validateSignup(validateData, req); } @@ -139,14 +139,14 @@ export class AuthController { @Throttle({ default: { limit: 20, ttl: 600000 } }) // 20 validations per 10 minutes per IP @UsePipes(new ZodValidationPipe(signupRequestSchema)) @HttpCode(200) - async signupPreflight(@Body() signupData: SignupRequestInput) { + async signupPreflight(@Body() signupData: SignupRequest) { return this.authFacade.signupPreflight(signupData); } @Public() @Post("account-status") @UsePipes(new ZodValidationPipe(accountStatusRequestSchema)) - async accountStatus(@Body() body: AccountStatusRequestInput) { + async accountStatus(@Body() body: AccountStatusRequest) { return this.authFacade.getAccountStatus(body.email); } @@ -156,7 +156,7 @@ export class AuthController { @Throttle({ default: { limit: 5, ttl: 900000 } }) // 5 signups per 15 minutes per IP (reasonable for account creation) @UsePipes(new ZodValidationPipe(signupRequestSchema)) async signup( - @Body() signupData: SignupRequestInput, + @Body() signupData: SignupRequest, @Req() req: Request, @Res({ passthrough: true }) res: Response ) { @@ -194,7 +194,7 @@ export class AuthController { @Throttle({ default: { limit: 10, ttl: 300000 } }) // 10 attempts per 5 minutes per IP @UsePipes(new ZodValidationPipe(refreshTokenRequestSchema)) async refreshToken( - @Body() body: RefreshTokenRequestInput, + @Body() body: RefreshTokenRequest, @Req() req: RequestWithCookies, @Res({ passthrough: true }) res: Response ) { @@ -214,7 +214,7 @@ export class AuthController { @UseGuards(AuthThrottleGuard) @Throttle({ default: { limit: 5, ttl: 600000 } }) // 5 attempts per 10 minutes per IP (industry standard) @UsePipes(new ZodValidationPipe(linkWhmcsRequestSchema)) - async linkWhmcs(@Body() linkData: LinkWhmcsRequestInput, @Req() _req: Request) { + async linkWhmcs(@Body() linkData: LinkWhmcsRequest, @Req() _req: Request) { return this.authFacade.linkWhmcsUser(linkData); } @@ -224,7 +224,7 @@ export class AuthController { @Throttle({ default: { limit: 5, ttl: 600000 } }) // 5 attempts per 10 minutes per IP+UA (industry standard) @UsePipes(new ZodValidationPipe(setPasswordRequestSchema)) async setPassword( - @Body() setPasswordData: SetPasswordRequestInput, + @Body() setPasswordData: SetPasswordRequest, @Req() _req: Request, @Res({ passthrough: true }) res: Response ) { @@ -237,7 +237,7 @@ export class AuthController { @Post("check-password-needed") @UsePipes(new ZodValidationPipe(checkPasswordNeededRequestSchema)) @HttpCode(200) - async checkPasswordNeeded(@Body() data: CheckPasswordNeededRequestInput) { + async checkPasswordNeeded(@Body() data: CheckPasswordNeededRequest) { return this.authFacade.checkPasswordNeeded(data.email); } @@ -245,7 +245,7 @@ export class AuthController { @Post("request-password-reset") @Throttle({ default: { limit: 5, ttl: 900000 } }) // 5 attempts per 15 minutes (standard for password operations) @UsePipes(new ZodValidationPipe(passwordResetRequestSchema)) - async requestPasswordReset(@Body() body: PasswordResetRequestInput, @Req() req: Request) { + async requestPasswordReset(@Body() body: PasswordResetRequest, @Req() req: Request) { await this.authFacade.requestPasswordReset(body.email, req); return { message: "If an account exists, a reset email has been sent" }; } @@ -265,7 +265,7 @@ export class AuthController { @UsePipes(new ZodValidationPipe(changePasswordRequestSchema)) async changePassword( @Req() req: Request & { user: { id: string } }, - @Body() body: ChangePasswordRequestInput, + @Body() body: ChangePasswordRequest, @Res({ passthrough: true }) res: Response ) { const result = await this.authFacade.changePassword( @@ -295,7 +295,7 @@ export class AuthController { @UsePipes(new ZodValidationPipe(ssoLinkRequestSchema)) async createSsoLink( @Req() req: Request & { user: { id: string } }, - @Body() body: SsoLinkRequestInput + @Body() body: SsoLinkRequest ) { const destination = body?.destination; return this.authFacade.createSsoLink(req.user.id, destination); diff --git a/apps/bff/src/modules/auth/presentation/strategies/jwt.strategy.ts b/apps/bff/src/modules/auth/presentation/strategies/jwt.strategy.ts index 272f09b9..bfaf204c 100644 --- a/apps/bff/src/modules/auth/presentation/strategies/jwt.strategy.ts +++ b/apps/bff/src/modules/auth/presentation/strategies/jwt.strategy.ts @@ -2,7 +2,7 @@ import { Injectable, UnauthorizedException } from "@nestjs/common"; import { PassportStrategy } from "@nestjs/passport"; import { ExtractJwt, Strategy } from "passport-jwt"; import { ConfigService } from "@nestjs/config"; -import type { AuthenticatedUser } from "@customer-portal/domain"; +import type { AuthenticatedUser } from "@customer-portal/domain/auth"; import { UsersService } from "@bff/modules/users/users.service"; import { mapPrismaUserToUserProfile } from "@bff/infra/utils/user-mapper.util"; import type { Request } from "express"; diff --git a/apps/bff/src/modules/subscriptions/subscriptions.controller.ts b/apps/bff/src/modules/subscriptions/subscriptions.controller.ts index 24cc1184..40c7519d 100644 --- a/apps/bff/src/modules/subscriptions/subscriptions.controller.ts +++ b/apps/bff/src/modules/subscriptions/subscriptions.controller.ts @@ -16,18 +16,24 @@ import { SimManagementService } from "./sim-management.service"; import { Subscription, SubscriptionList, + subscriptionQuerySchema, + type SubscriptionQuery, +} from "@customer-portal/domain/subscriptions"; +import { InvoiceList, + invoiceListQuerySchema, + type InvoiceListQuery, +} from "@customer-portal/domain/billing"; +import { simTopupRequestSchema, simChangePlanRequestSchema, simCancelRequestSchema, simFeaturesRequestSchema, - subscriptionQuerySchema, - invoiceListQuerySchema, type SimTopupRequest, type SimChangePlanRequest, type SimCancelRequest, type SimFeaturesRequest, - type SubscriptionQuery, +} from "@customer-portal/domain/sim"; import { ZodValidationPipe } from "@bff/core/validation"; import type { RequestWithUser } from "@bff/modules/auth/auth.types"; diff --git a/apps/bff/src/modules/users/users.service.ts b/apps/bff/src/modules/users/users.service.ts index 3e3741d0..1ce1de16 100644 --- a/apps/bff/src/modules/users/users.service.ts +++ b/apps/bff/src/modules/users/users.service.ts @@ -1,11 +1,17 @@ import { getErrorMessage } from "@bff/core/utils/error.util"; import { normalizeAndValidateEmail, validateUuidV4OrThrow } from "@bff/core/utils/validation.util"; -import type { UpdateAddressRequest } from "@customer-portal/domain"; +import type { UpdateAddressRequest } from "@customer-portal/domain/auth"; import { Injectable, Inject, NotFoundException, BadRequestException } from "@nestjs/common"; import { Logger } from "nestjs-pino"; import { PrismaService } from "@bff/infra/database/prisma.service"; -import { User, Activity, Address, type AuthenticatedUser } from "@customer-portal/domain"; -import type { Subscription, Invoice } from "@customer-portal/domain"; +import { + User, + Activity, + Address, + type AuthenticatedUser, +} from "@customer-portal/domain/auth"; +import type { Subscription } from "@customer-portal/domain/subscriptions"; +import type { Invoice } from "@customer-portal/domain/billing"; import type { User as PrismaUser } from "@prisma/client"; import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service"; import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service"; diff --git a/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx b/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx index c718090f..979c6d13 100644 --- a/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx +++ b/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx @@ -4,11 +4,8 @@ import { useCallback } from "react"; import { Button, Input, ErrorMessage } from "@/components/atoms"; import { FormField } from "@/components/molecules/FormField/FormField"; import { useWhmcsLink } from "@/features/auth/hooks"; -import { - linkWhmcsRequestSchema, - type LinkWhmcsFormData, - type LinkWhmcsRequestInput, -} from "@customer-portal/domain"; +import { linkWhmcsRequestSchema, type LinkWhmcsFormData } from "@customer-portal/domain/validation"; +import type { LinkWhmcsRequest } from "@customer-portal/domain/auth"; import { useZodForm } from "@customer-portal/validation"; interface LinkWhmcsFormProps { @@ -22,7 +19,7 @@ export function LinkWhmcsForm({ onTransferred, className = "" }: LinkWhmcsFormPr const handleLink = useCallback( async (formData: LinkWhmcsFormData) => { clearError(); - const payload: LinkWhmcsRequestInput = { + const payload: LinkWhmcsRequest = { email: formData.email, password: formData.password, }; diff --git a/apps/portal/src/features/auth/components/SignupForm/SignupForm.tsx b/apps/portal/src/features/auth/components/SignupForm/SignupForm.tsx index 005a796c..5c2ea38a 100644 --- a/apps/portal/src/features/auth/components/SignupForm/SignupForm.tsx +++ b/apps/portal/src/features/auth/components/SignupForm/SignupForm.tsx @@ -12,8 +12,8 @@ import { useSignup } from "../../hooks/use-auth"; import { signupFormSchema, signupFormToRequest, - type SignupRequestInput, -} from "@customer-portal/domain"; +} from "@customer-portal/domain/validation"; +import type { SignupRequest } from "@customer-portal/domain/auth"; import { useZodForm } from "@customer-portal/validation"; import { z } from "zod"; @@ -62,7 +62,7 @@ export function SignupForm({ async ({ confirmPassword: _confirm, ...formData }: SignupFormValues) => { clearError(); try { - const request: SignupRequestInput = signupFormToRequest(formData); + const request: SignupRequest = signupFormToRequest(formData); await signup(request); onSuccess?.(); } catch (err) { diff --git a/apps/portal/src/features/auth/hooks/use-auth.ts b/apps/portal/src/features/auth/hooks/use-auth.ts index 5940bfc5..92e213bd 100644 --- a/apps/portal/src/features/auth/hooks/use-auth.ts +++ b/apps/portal/src/features/auth/hooks/use-auth.ts @@ -9,7 +9,7 @@ import { useCallback, useEffect } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { useAuthStore } from "../services/auth.store"; import { getPostLoginRedirect } from "@/features/auth/utils/route-protection"; -import type { SignupRequestInput, LoginRequestInput } from "@customer-portal/domain"; +import type { SignupRequest, LoginRequest } from "@customer-portal/domain/auth"; /** * Main authentication hook @@ -40,7 +40,7 @@ export function useAuth() { // Enhanced login with redirect handling const login = useCallback( - async (credentials: LoginRequestInput) => { + async (credentials: LoginRequest) => { await loginAction(credentials); // Keep loading state active during redirect const redirectTo = getPostLoginRedirect(searchParams); @@ -52,7 +52,7 @@ export function useAuth() { // Enhanced signup with redirect handling const signup = useCallback( - async (data: SignupRequestInput) => { + async (data: SignupRequest) => { await signupAction(data); const redirectTo = getPostLoginRedirect(searchParams); router.push(redirectTo); diff --git a/apps/portal/src/features/auth/services/auth.store.ts b/apps/portal/src/features/auth/services/auth.store.ts index 86732b59..3747416b 100644 --- a/apps/portal/src/features/auth/services/auth.store.ts +++ b/apps/portal/src/features/auth/services/auth.store.ts @@ -8,13 +8,13 @@ import { apiClient, getNullableData } from "@/lib/api"; import { getErrorInfo, handleAuthError } from "@/lib/utils/error-handling"; import logger from "@customer-portal/logging"; import type { - AuthTokensSchema, + AuthTokens, AuthenticatedUser, - LinkWhmcsRequestInput, - LoginRequestInput, - SignupRequestInput, -} from "@customer-portal/domain"; -import { authResponseSchema } from "@customer-portal/domain/validation"; + LinkWhmcsRequest, + LoginRequest, + SignupRequest, +} from "@customer-portal/domain/auth"; +import { authResponseSchema } from "@customer-portal/domain/auth"; interface SessionState { accessExpiresAt?: string; @@ -29,16 +29,14 @@ export interface AuthState { error: string | null; hasCheckedAuth: boolean; - login: (credentials: LoginRequestInput) => Promise; - signup: (data: SignupRequestInput) => Promise; + login: (credentials: LoginRequest) => Promise; + signup: (data: SignupRequest) => Promise; logout: () => Promise; requestPasswordReset: (email: string) => Promise; resetPassword: (token: string, password: string) => Promise; changePassword: (currentPassword: string, newPassword: string) => Promise; checkPasswordNeeded: (email: string) => Promise<{ needsPasswordSet: boolean }>; - linkWhmcs: ( - request: LinkWhmcsRequestInput - ) => Promise<{ needsPasswordSet: boolean; email: string }>; + linkWhmcs: (request: LinkWhmcsRequest) => Promise<{ needsPasswordSet: boolean; email: string }>; setPassword: (email: string, password: string) => Promise; refreshUser: () => Promise; refreshSession: () => Promise; @@ -50,7 +48,7 @@ export interface AuthState { type AuthResponseData = { user: AuthenticatedUser; - tokens: AuthTokensSchema; + tokens: AuthTokens; }; export const useAuthStore = create()((set, get) => { @@ -159,7 +157,7 @@ export const useAuthStore = create()((set, get) => { } }, - changePassword: async (currentPassword: string, newPassword: string) => { + changePassword: async (currentPassword: string, newPassword: string) => { set({ loading: true, error: null }); try { const response = await apiClient.POST("/api/auth/change-password", { @@ -201,7 +199,7 @@ export const useAuthStore = create()((set, get) => { } }, - linkWhmcs: async ({ email, password }: LinkWhmcsRequestInput) => { + linkWhmcs: async ({ email, password }: LinkWhmcsRequest) => { set({ loading: true, error: null }); try { const response = await apiClient.POST("/api/auth/link-whmcs", { diff --git a/packages/domain/auth/contract.ts b/packages/domain/auth/contract.ts new file mode 100644 index 00000000..16d6448a --- /dev/null +++ b/packages/domain/auth/contract.ts @@ -0,0 +1,107 @@ +/** + * Auth Domain - Contract + * + * Canonical authentication types shared across applications. + */ + +import type { IsoDateTimeString, Address } from "../common/types"; + +export type UserRole = "USER" | "ADMIN"; + +export interface UserProfile { + id: string; + email: string; + firstName?: string; + lastName?: string; + company?: string; + phone?: string; + address?: Address; + avatar?: string; + preferences?: Record; + emailVerified: boolean; + mfaEnabled: boolean; + lastLoginAt?: IsoDateTimeString; + createdAt?: IsoDateTimeString; + updatedAt?: IsoDateTimeString; +} + +export interface AuthenticatedUser extends UserProfile { + role: UserRole; +} + +export interface AuthTokens { + accessToken: string; + refreshToken: string; + expiresAt: IsoDateTimeString; + refreshExpiresAt: IsoDateTimeString; + tokenType: "Bearer"; +} + +export interface AuthResponse { + user: AuthenticatedUser; + tokens: AuthTokens; +} + +export interface LoginRequest { + email: string; + password: string; + mfaCode?: string; + rememberMe?: boolean; +} + +export interface SignupRequest { + email: string; + password: string; + firstName: string; + lastName: string; + phone?: string; + company?: string; + sfNumber: string; + address?: Address; + nationality?: string; + dateOfBirth?: string; + gender?: "male" | "female" | "other"; + acceptTerms: boolean; + marketingConsent?: boolean; +} + +export interface ForgotPasswordRequest { + email: string; +} + +export interface ResetPasswordRequest { + token: string; + password: string; + confirmPassword?: string; +} + +export interface ChangePasswordRequest { + currentPassword: string; + newPassword: string; +} + +export interface LinkWhmcsRequest { + email: string; + password: string; +} + +export interface SetPasswordRequest { + email: string; + password: string; +} + +export interface AuthError { + code: + | "INVALID_CREDENTIALS" + | "USER_NOT_FOUND" + | "EMAIL_ALREADY_EXISTS" + | "EMAIL_NOT_VERIFIED" + | "INVALID_TOKEN" + | "TOKEN_EXPIRED" + | "ACCOUNT_LOCKED" + | "RATE_LIMITED" + | "NETWORK_ERROR"; + message: string; + details?: Record; +} + diff --git a/packages/domain/auth/index.ts b/packages/domain/auth/index.ts new file mode 100644 index 00000000..094ee325 --- /dev/null +++ b/packages/domain/auth/index.ts @@ -0,0 +1,3 @@ +export * from "./contract"; +export * from "./schema"; + diff --git a/packages/domain/auth/schema.ts b/packages/domain/auth/schema.ts new file mode 100644 index 00000000..47c93cc8 --- /dev/null +++ b/packages/domain/auth/schema.ts @@ -0,0 +1,102 @@ +/** + * Auth Domain - Schemas + */ + +import { z } from "zod"; + +import { + addressSchema, + emailSchema, + nameSchema, + passwordSchema, + phoneSchema, +} from "../common/schema"; + +const genderEnum = z.enum(["male", "female", "other"]); + +export const loginRequestSchema = z.object({ + email: emailSchema, + password: z.string().min(1, "Password is required"), +}); + +export const signupRequestSchema = z.object({ + email: emailSchema, + password: passwordSchema, + firstName: nameSchema, + lastName: nameSchema, + company: z.string().optional(), + phone: phoneSchema, + sfNumber: z.string().min(6, "Customer number must be at least 6 characters"), + address: addressSchema.optional(), + nationality: z.string().optional(), + dateOfBirth: z.string().optional(), + gender: genderEnum.optional(), + acceptTerms: z.boolean(), + marketingConsent: z.boolean().optional(), +}); + +export const passwordResetRequestSchema = z.object({ email: emailSchema }); + +export const passwordResetSchema = z.object({ + token: z.string().min(1, "Reset token is required"), + password: passwordSchema, +}); + +export const setPasswordRequestSchema = z.object({ + email: emailSchema, + password: passwordSchema, +}); + +export const changePasswordRequestSchema = z.object({ + currentPassword: z.string().min(1, "Current password is required"), + newPassword: passwordSchema, +}); + +export const linkWhmcsRequestSchema = z.object({ + email: emailSchema, + password: z.string().min(1, "Password is required"), +}); + +export const validateSignupRequestSchema = z.object({ + sfNumber: z.string().min(1, "Customer number is required"), +}); + +export const accountStatusRequestSchema = z.object({ + email: emailSchema, +}); + +export const ssoLinkRequestSchema = z.object({ + destination: z.string().optional(), +}); + +export const checkPasswordNeededRequestSchema = z.object({ + email: emailSchema, +}); + +export const refreshTokenRequestSchema = z.object({ + refreshToken: z.string().min(1, "Refresh token is required").optional(), + deviceId: z.string().optional(), +}); + +export const authTokensSchema = z.object({ + accessToken: z.string().min(1, "Access token is required"), + refreshToken: z.string().min(1, "Refresh token is required"), + expiresAt: z.string().min(1, "Access token expiry required"), + refreshExpiresAt: z.string().min(1, "Refresh token expiry required"), + tokenType: z.literal("Bearer"), +}); + +export const authResponseSchema = z.object({ + user: z.unknown(), + tokens: authTokensSchema, +}); + +export const validateSignupRequestSchema = signupRequestSchema.pick({ sfNumber: true }); +export const accountStatusRequestSchema = z.object({ email: emailSchema }); +export const ssoLinkRequestSchema = z.object({ destination: z.string().optional() }); +export const checkPasswordNeededRequestSchema = z.object({ email: emailSchema }); +export const refreshTokenRequestSchema = z.object({ + refreshToken: z.string().min(1, "Refresh token is required").optional(), + deviceId: z.string().optional(), +}); + diff --git a/packages/domain/common/index.ts b/packages/domain/common/index.ts index 5d412621..b21bd4af 100644 --- a/packages/domain/common/index.ts +++ b/packages/domain/common/index.ts @@ -5,4 +5,5 @@ */ export * from "./types"; +export * from "./schema"; diff --git a/packages/domain/common/schema.ts b/packages/domain/common/schema.ts new file mode 100644 index 00000000..b2e65543 --- /dev/null +++ b/packages/domain/common/schema.ts @@ -0,0 +1,76 @@ +/** + * Common Domain - Schemas + * + * Shared validation primitives used across all domains. + */ + +import { z } from "zod"; + +export const emailSchema = z + .string() + .email("Please enter a valid email address") + .toLowerCase() + .trim(); + +export const passwordSchema = z + .string() + .min(8, "Password must be at least 8 characters") + .regex(/[A-Z]/, "Password must contain at least one uppercase letter") + .regex(/[a-z]/, "Password must contain at least one lowercase letter") + .regex(/[0-9]/, "Password must contain at least one number") + .regex(/[^A-Za-z0-9]/, "Password must contain at least one special character"); + +export const nameSchema = z.string().min(1, "Name is required").max(100, "Name must be less than 100 characters").trim(); + +export const phoneSchema = z + .string() + .regex(/^[+]?[0-9\s\-()]{7,20}$/u, "Please enter a valid phone number") + .trim(); + +export const addressSchema = z.object({ + street: z.string().max(200, "Street address is too long").nullable(), + streetLine2: z.string().max(200, "Street address line 2 is too long").nullable(), + city: z.string().max(100, "City name is too long").nullable(), + state: z.string().max(100, "State/Prefecture name is too long").nullable(), + postalCode: z.string().max(20, "Postal code is too long").nullable(), + country: z.string().max(100, "Country name is too long").nullable(), +}); + +export const requiredAddressSchema = z.object({ + street: z.string().min(1, "Street address is required").max(200, "Street address is too long").trim(), + streetLine2: z.string().max(200, "Street address line 2 is too long").optional(), + city: z.string().min(1, "City is required").max(100, "City name is too long").trim(), + state: z + .string() + .min(1, "State/Prefecture is required") + .max(100, "State/Prefecture name is too long") + .trim(), + postalCode: z.string().min(1, "Postal code is required").max(20, "Postal code is too long").trim(), + country: z.string().min(1, "Country is required").max(100, "Country name is too long").trim(), +}); + +export const countryCodeSchema = z.string().length(2, "Country code must be 2 characters"); +export const currencyCodeSchema = z.string().length(3, "Currency code must be 3 characters"); + +export const timestampSchema = z.string().datetime("Invalid timestamp format"); +export const dateSchema = z.string().date("Invalid date format"); + +export const moneyAmountSchema = z.number().int().nonnegative("Amount must be non-negative"); +export const percentageSchema = z.number().min(0).max(100, "Percentage must be between 0 and 100"); + +export const genderEnum = z.enum(["male", "female", "other"]); +export const statusEnum = z.enum(["active", "inactive", "pending", "suspended"]); +export const priorityEnum = z.enum(["low", "medium", "high", "urgent"]); +export const categoryEnum = z.enum(["technical", "billing", "account", "general"]); + +export const billingCycleEnum = z.enum(["Monthly", "Quarterly", "Annually", "Onetime", "Free"]); +export const subscriptionBillingCycleEnum = z.enum([ + "Monthly", + "Quarterly", + "Semi-Annually", + "Annually", + "Biennially", + "Triennially", + "One-time", + "Free", +]); diff --git a/packages/domain/common/types.ts b/packages/domain/common/types.ts index 4d8e2919..16de2859 100644 --- a/packages/domain/common/types.ts +++ b/packages/domain/common/types.ts @@ -21,17 +21,20 @@ export type Currency = "JPY" | "USD" | "EUR"; // ============================================================================ export type UserId = string & { readonly __brand: "UserId" }; - export type AccountId = string & { readonly __brand: "AccountId" }; - export type OrderId = string & { readonly __brand: "OrderId" }; - export type InvoiceId = string & { readonly __brand: "InvoiceId" }; - export type ProductId = string & { readonly __brand: "ProductId" }; - export type SimId = string & { readonly __brand: "SimId" }; +export type WhmcsClientId = number & { readonly __brand: "WhmcsClientId" }; +export type WhmcsInvoiceId = number & { readonly __brand: "WhmcsInvoiceId" }; +export type WhmcsProductId = number & { readonly __brand: "WhmcsProductId" }; + +export type SalesforceContactId = string & { readonly __brand: "SalesforceContactId" }; +export type SalesforceAccountId = string & { readonly __brand: "SalesforceAccountId" }; +export type SalesforceCaseId = string & { readonly __brand: "SalesforceCaseId" }; + // ============================================================================ // Address // ============================================================================ diff --git a/packages/domain/index.ts b/packages/domain/index.ts index 37adf13e..720a99b5 100644 --- a/packages/domain/index.ts +++ b/packages/domain/index.ts @@ -12,4 +12,5 @@ export * as Orders from "./orders"; export * as Catalog from "./catalog"; export * as Common from "./common"; export * as Toolkit from "./toolkit"; +export * as Auth from "./auth"; diff --git a/packages/domain/package.json b/packages/domain/package.json index 5d4e25e5..987b67e3 100644 --- a/packages/domain/package.json +++ b/packages/domain/package.json @@ -20,6 +20,8 @@ "./catalog/*": "./dist/catalog/*", "./common": "./dist/common/index.js", "./common/*": "./dist/common/*", + "./auth": "./dist/auth/index.js", + "./auth/*": "./dist/auth/*", "./toolkit": "./dist/toolkit/index.js", "./toolkit/*": "./dist/toolkit/*" }, diff --git a/packages/domain/sim/index.ts b/packages/domain/sim/index.ts index 9b63c318..e142c467 100644 --- a/packages/domain/sim/index.ts +++ b/packages/domain/sim/index.ts @@ -7,7 +7,3 @@ export * from "./schema"; // Provider adapters export * as Providers from "./providers"; - -// Re-export provider types and schemas for convenience -export * from "./providers/freebit/raw.types"; -export * from "./providers/freebit/requests"; diff --git a/packages/domain/sim/providers/freebit/index.ts b/packages/domain/sim/providers/freebit/index.ts index c95a1ab4..191ad6ec 100644 --- a/packages/domain/sim/providers/freebit/index.ts +++ b/packages/domain/sim/providers/freebit/index.ts @@ -1,2 +1,53 @@ +import * as Mapper from "./mapper"; +import * as RawTypes from "./raw.types"; +import * as Requests from "./requests"; + +export const schemas = { + accountDetails: Requests.freebitAccountDetailsRequestSchema, + trafficInfo: Requests.freebitTrafficInfoRequestSchema, + topUp: Requests.freebitTopUpRequestPayloadSchema, + topUpApi: Requests.freebitTopUpApiRequestSchema, + planChange: Requests.freebitPlanChangeRequestSchema, + planChangeApi: Requests.freebitPlanChangeApiRequestSchema, + addSpec: Requests.freebitAddSpecRequestSchema, + cancelPlan: Requests.freebitCancelPlanRequestSchema, + cancelPlanApi: Requests.freebitCancelPlanApiRequestSchema, + quotaHistory: Requests.freebitQuotaHistoryRequestSchema, + esimReissue: Requests.freebitEsimReissueRequestSchema, + simFeatures: Requests.freebitSimFeaturesRequestSchema, + globalIp: Requests.freebitGlobalIpRequestSchema, + esimActivationParams: Requests.freebitEsimActivationParamsSchema, + esimActivationRequest: Requests.freebitEsimActivationRequestSchema, + esimAddAccount: Requests.freebitEsimAddAccountRequestSchema, + auth: Requests.freebitAuthRequestSchema, + cancelAccount: Requests.freebitCancelAccountRequestSchema, +}; + +export const raw = RawTypes; +export const mapper = Mapper; + +export type EsimAccountActivationRequest = Requests.FreebitEsimActivationRequest; +export type TopUpRequest = Requests.FreebitTopUpRequest; +export type TopUpApiRequest = Requests.FreebitTopUpApiRequest; +export type PlanChangeRequest = Requests.FreebitPlanChangeRequest; +export type PlanChangeApiRequest = Requests.FreebitPlanChangeApiRequest; +export type SimFeaturesRequest = Requests.FreebitSimFeaturesRequest; +export type QuotaHistoryRequest = Requests.FreebitQuotaHistoryRequest; +export type EsimAddAccountRequest = Requests.FreebitEsimAddAccountRequest; +export type TrafficInfoRequest = Requests.FreebitTrafficInfoRequest; +export type CancelPlanRequest = Requests.FreebitCancelPlanRequest; +export type CancelPlanApiRequest = Requests.FreebitCancelPlanApiRequest; +export type CancelAccountRequest = Requests.FreebitCancelAccountRequest; +export type AuthRequest = Requests.FreebitAuthRequest; + export * from "./mapper"; export * from "./raw.types"; +export * from "./requests"; + +export const Freebit = { + ...Mapper, + raw: RawTypes, + schemas, +}; + +export const normalizeAccount = Mapper.normalizeAccount; diff --git a/packages/domain/sim/providers/freebit/mapper.ts b/packages/domain/sim/providers/freebit/mapper.ts index 86d66bc9..7387bb1a 100644 --- a/packages/domain/sim/providers/freebit/mapper.ts +++ b/packages/domain/sim/providers/freebit/mapper.ts @@ -8,9 +8,23 @@ import { type FreebitAccountDetailsRaw, type FreebitTrafficInfoRaw, type FreebitQuotaHistoryRaw, + type FreebitTopUpRaw, + type FreebitAddSpecRaw, + type FreebitPlanChangeRaw, + type FreebitCancelPlanRaw, + type FreebitCancelAccountRaw, + type FreebitEsimReissueRaw, + type FreebitEsimAddAccountRaw, freebitAccountDetailsRawSchema, freebitTrafficInfoRawSchema, freebitQuotaHistoryRawSchema, + freebitTopUpRawSchema, + freebitAddSpecRawSchema, + freebitPlanChangeRawSchema, + freebitCancelPlanRawSchema, + freebitCancelAccountRawSchema, + freebitEsimReissueRawSchema, + freebitEsimAddAccountRawSchema, } from "./raw.types"; function asString(value: unknown): string { @@ -150,3 +164,31 @@ export function normalizeAccount(account: string): string { return account.replace(/\D/g, ""); } +export function transformFreebitTopUpResponse(raw: unknown) { + return freebitTopUpRawSchema.parse(raw); +} + +export function transformFreebitAddSpecResponse(raw: unknown) { + return freebitAddSpecRawSchema.parse(raw); +} + +export function transformFreebitPlanChangeResponse(raw: unknown) { + return freebitPlanChangeRawSchema.parse(raw); +} + +export function transformFreebitCancelPlanResponse(raw: unknown) { + return freebitCancelPlanRawSchema.parse(raw); +} + +export function transformFreebitCancelAccountResponse(raw: unknown) { + return freebitCancelAccountRawSchema.parse(raw); +} + +export function transformFreebitEsimReissueResponse(raw: unknown) { + return freebitEsimReissueRawSchema.parse(raw); +} + +export function transformFreebitEsimAddAccountResponse(raw: unknown) { + return freebitEsimAddAccountRawSchema.parse(raw); +} + diff --git a/packages/domain/sim/providers/freebit/raw.types.ts b/packages/domain/sim/providers/freebit/raw.types.ts index 968f34aa..44b288dc 100644 --- a/packages/domain/sim/providers/freebit/raw.types.ts +++ b/packages/domain/sim/providers/freebit/raw.types.ts @@ -41,19 +41,104 @@ export type FreebitAccountDetailsRaw = z.infer; + +export const freebitAddSpecRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), +}); + +export type FreebitAddSpecRaw = z.infer; + +export const freebitPlanChangeRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), + ipv4: z.string().optional(), + ipv6: z.string().optional(), +}); + +export type FreebitPlanChangeRaw = z.infer; + +export const freebitCancelPlanRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), +}); + +export type FreebitCancelPlanRaw = z.infer; + +export const freebitCancelAccountRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), +}); + +export type FreebitCancelAccountRaw = z.infer; + +export const freebitEsimReissueRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), +}); + +export type FreebitEsimReissueRaw = z.infer; + +export const freebitEsimAddAccountRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), +}); + +export type FreebitEsimAddAccountRaw = z.infer; export type FreebitTrafficInfoRaw = z.infer; diff --git a/packages/domain/sim/providers/freebit/requests.ts b/packages/domain/sim/providers/freebit/requests.ts index 9bbbb117..f2801783 100644 --- a/packages/domain/sim/providers/freebit/requests.ts +++ b/packages/domain/sim/providers/freebit/requests.ts @@ -42,6 +42,14 @@ export const freebitTopUpRequestPayloadSchema = z.object({ options: freebitTopUpOptionsSchema.optional(), }); +export const freebitTopUpApiRequestSchema = z.object({ + account: z.string().min(1, "Account is required"), + quota: z.number().positive("Quota must be positive"), + quotaCode: z.string().optional(), + expire: z.string().optional(), + runTime: z.string().optional(), +}); + // ============================================================================ // Plan Change & Cancellation // ============================================================================ @@ -53,6 +61,13 @@ export const freebitPlanChangeRequestSchema = z.object({ scheduledAt: z.string().optional(), }); +export const freebitPlanChangeApiRequestSchema = z.object({ + account: z.string().min(1, "Account is required"), + plancode: z.string().min(1, "Plan code is required"), + globalip: z.enum(["0", "1"]).optional(), + runTime: z.string().optional(), +}); + export const freebitAddSpecRequestSchema = z.object({ account: z.string().min(1, "Account is required"), specCode: z.string().min(1, "Spec code is required"), @@ -70,6 +85,11 @@ export const freebitCancelPlanRequestSchema = z.object({ runDate: z.string().optional(), }); +export const freebitCancelPlanApiRequestSchema = z.object({ + account: z.string().min(1, "Account is required"), + runTime: z.string().optional(), +}); + export const freebitEsimReissueRequestSchema = z.object({ account: z.string().min(1, "Account is required"), newEid: z.string().min(1, "New EID is required"), @@ -104,6 +124,16 @@ export const freebitEsimMnpSchema = z.object({ reserveExpireDate: z.string().regex(/^\d{8}$/, "Reserve expire date must be in YYYYMMDD format"), }); +export const freebitAuthRequestSchema = z.object({ + oemId: z.string().min(1), + oemKey: z.string().min(1), +}); + +export const freebitCancelAccountRequestSchema = z.object({ + account: z.string().min(1), + runDate: z.string().optional(), +}); + export const freebitEsimIdentitySchema = z.object({ firstnameKanji: z.string().optional(), lastnameKanji: z.string().optional(), @@ -180,14 +210,22 @@ export const freebitEsimActivationParamsSchema = z.object({ export type FreebitAccountDetailsRequest = z.infer; export type FreebitTrafficInfoRequest = z.infer; export type FreebitTopUpRequest = z.infer; +export type FreebitTopUpApiRequest = z.infer; export type FreebitPlanChangeRequest = z.infer; +export type FreebitPlanChangeApiRequest = z.infer; export type FreebitAddSpecRequest = z.infer; export type FreebitRemoveSpecRequest = z.infer; export type FreebitCancelPlanRequest = z.infer; +export type FreebitCancelPlanApiRequest = z.infer; export type FreebitSimFeaturesRequest = z.infer; export type FreebitGlobalIpRequest = z.infer; export type FreebitEsimActivationRequest = z.infer; export type FreebitEsimActivationResponse = z.infer; export type FreebitEsimActivationParams = z.infer; export type FreebitEsimReissueRequest = z.infer; +export type FreebitQuotaHistoryRequest = z.infer; +export type FreebitQuotaHistoryResponse = z.infer; +export type FreebitEsimAddAccountRequest = z.infer; +export type FreebitAuthRequest = z.infer; +export type FreebitCancelAccountRequest = z.infer; diff --git a/packages/domain/sim/providers/index.ts b/packages/domain/sim/providers/index.ts index 66be285c..1ddf20b8 100644 --- a/packages/domain/sim/providers/index.ts +++ b/packages/domain/sim/providers/index.ts @@ -2,12 +2,4 @@ * SIM Domain - Providers */ -import * as FreebitMapper from "./freebit/mapper"; - -export const Freebit = { - ...FreebitMapper, -}; - -// Re-export raw types and request schemas for convenience -export * from "./freebit/raw.types"; -export * from "./freebit/requests"; +export * as Freebit from "./freebit"; diff --git a/packages/domain/src/entities/index.d.ts b/packages/domain/src/entities/index.d.ts index 6acf644d..7ac6a206 100644 --- a/packages/domain/src/entities/index.d.ts +++ b/packages/domain/src/entities/index.d.ts @@ -1,4 +1,3 @@ -export * from "./user"; export * from "./invoice"; export * from "./subscription"; export * from "./payment"; diff --git a/packages/domain/src/entities/index.js b/packages/domain/src/entities/index.js index 7897de7e..3454351c 100644 --- a/packages/domain/src/entities/index.js +++ b/packages/domain/src/entities/index.js @@ -14,7 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("./user"), exports); __exportStar(require("./invoice"), exports); __exportStar(require("./subscription"), exports); __exportStar(require("./payment"), exports); diff --git a/packages/domain/src/entities/index.js.map b/packages/domain/src/entities/index.js.map index b8bc4a47..8f555c79 100644 --- a/packages/domain/src/entities/index.js.map +++ b/packages/domain/src/entities/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,yCAAuB;AACvB,4CAA0B;AAC1B,iDAA+B;AAC/B,4CAA0B;AAC1B,yCAAuB;AACvB,8CAA4B;AAC5B,4CAA0B;AAC1B,yCAAuB"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,4CAA0B;AAC1B,iDAA+B;AAC/B,4CAA0B;AAC1B,yCAAuB;AACvB,8CAA4B;AAC5B,4CAA0B;AAC1B,yCAAuB"} \ No newline at end of file diff --git a/packages/domain/src/entities/index.ts b/packages/domain/src/entities/index.ts index b25a481c..5481daf8 100644 --- a/packages/domain/src/entities/index.ts +++ b/packages/domain/src/entities/index.ts @@ -1,5 +1,4 @@ // Export all entities - resolving conflicts explicitly -export * from "./user"; export * from "./invoice"; export * from "./subscription"; export * from "./payment"; diff --git a/packages/domain/src/entities/user.d.ts b/packages/domain/src/entities/user.d.ts deleted file mode 100644 index e9c0ca6d..00000000 --- a/packages/domain/src/entities/user.d.ts +++ /dev/null @@ -1,104 +0,0 @@ -import type { BaseEntity, Address, IsoDateTimeString } from "../common"; -import type { InvoiceList } from "./invoice"; -export interface User extends BaseEntity { - email: string; - firstName?: string; - lastName?: string; - company?: string; - phone?: string; - address?: Address; - mfaEnabled: boolean; - emailVerified: boolean; -} -export interface UserSummary { - user: User; - stats: { - activeSubscriptions: number; - unpaidInvoices: number; - openCases: number; - totalSpent: number; - currency: string; - }; - nextInvoice?: InvoiceList["invoices"][number]; - recentActivity: Activity[]; -} -export interface Activity { - id: string; - type: "invoice_created" | "invoice_paid" | "service_activated" | "case_created" | "case_closed"; - title: string; - description?: string; - date: string; - relatedId?: number; - metadata?: Record; -} -export interface AuthTokens { - accessToken: string; - refreshToken: string; - expiresAt: IsoDateTimeString; - refreshExpiresAt: IsoDateTimeString; - tokenType: "Bearer"; -} -export interface LoginRequest { - email: string; - password: string; - mfaCode?: string; - rememberMe?: boolean; -} -export interface SignupRequest { - email: string; - password: string; - confirmPassword?: string; - firstName: string; - lastName: string; - company?: string; - phone?: string; - sfNumber: string; - address?: Address; - nationality?: string; - dateOfBirth?: string; - gender?: "male" | "female" | "other"; - acceptTerms: boolean; - marketingConsent?: boolean; -} -export interface ForgotPasswordRequest { - email: string; -} -export interface ResetPasswordRequest { - token: string; - password: string; - confirmPassword?: string; -} -export interface ChangePasswordRequest { - currentPassword: string; - newPassword: string; - confirmPassword?: string; -} -export interface AuthError { - code: string; - message: string; - details?: Record; -} -export type AuthErrorCode = "INVALID_CREDENTIALS" | "USER_NOT_FOUND" | "EMAIL_ALREADY_EXISTS" | "EMAIL_NOT_VERIFIED" | "INVALID_TOKEN" | "TOKEN_EXPIRED" | "ACCOUNT_LOCKED" | "RATE_LIMITED" | "NETWORK_ERROR"; -export interface UserProfile extends User { - avatar?: string; - preferences?: Record; - lastLoginAt?: string; -} -export type UserRole = "USER" | "ADMIN"; -export interface AuthenticatedUser extends UserProfile { - role: UserRole; -} -export interface MnpDetails { - currentProvider: string; - phoneNumber: string; - accountNumber?: string; - pin?: string; -} -export interface LinkWhmcsRequest { - email: string; - whmcsToken?: string; -} -export interface SetPasswordRequest { - password: string; - confirmPassword: string; -} diff --git a/packages/domain/src/entities/user.ts b/packages/domain/src/entities/user.ts deleted file mode 100644 index a2ba8c31..00000000 --- a/packages/domain/src/entities/user.ts +++ /dev/null @@ -1,134 +0,0 @@ -// User and authentication types -import type { BaseEntity, Address, IsoDateTimeString } from "../common"; -import type { InvoiceList } from "./invoice"; - -export interface User extends BaseEntity { - email: string; - firstName?: string; - lastName?: string; - company?: string; - phone?: string; - address?: Address; - mfaEnabled: boolean; - emailVerified: boolean; -} - -export interface UserSummary { - user: User; - stats: { - activeSubscriptions: number; - unpaidInvoices: number; - openCases: number; - totalSpent: number; - currency: string; - }; - nextInvoice?: InvoiceList["invoices"][number]; - recentActivity: Activity[]; -} - -export interface Activity { - id: string; - type: "invoice_created" | "invoice_paid" | "service_activated" | "case_created" | "case_closed"; - title: string; - description?: string; - date: string; // ISO - relatedId?: number; // ID of related invoice, subscription, etc. - metadata?: Record; -} - -// Complete auth interfaces (business logic) -export interface AuthTokens { - accessToken: string; - refreshToken: string; - expiresAt: IsoDateTimeString; - refreshExpiresAt: IsoDateTimeString; - tokenType: "Bearer"; -} - -export interface LoginRequest { - email: string; - password: string; - mfaCode?: string; - rememberMe?: boolean; -} - -export interface SignupRequest { - email: string; - password: string; - confirmPassword?: string; - firstName: string; - lastName: string; - company?: string; - phone?: string; - sfNumber: string; // Customer Number - address?: Address; - nationality?: string; - dateOfBirth?: string; // ISO or locale string per frontend validation - gender?: "male" | "female" | "other"; - acceptTerms: boolean; - marketingConsent?: boolean; -} - -export interface ForgotPasswordRequest { - email: string; -} - -export interface ResetPasswordRequest { - token: string; - password: string; - confirmPassword?: string; -} - -export interface ChangePasswordRequest { - currentPassword: string; - newPassword: string; - confirmPassword?: string; -} - -export interface AuthError { - code: string; - message: string; - details?: Record; -} - -export type AuthErrorCode = - | "INVALID_CREDENTIALS" - | "USER_NOT_FOUND" - | "EMAIL_ALREADY_EXISTS" - | "EMAIL_NOT_VERIFIED" - | "INVALID_TOKEN" - | "TOKEN_EXPIRED" - | "ACCOUNT_LOCKED" - | "RATE_LIMITED" - | "NETWORK_ERROR"; - -// Enhanced user profile exposed to clients -export interface UserProfile extends User { - avatar?: string; - preferences?: Record; - lastLoginAt?: string; -} - -export type UserRole = "USER" | "ADMIN"; - -export interface AuthenticatedUser extends UserProfile { - role: UserRole; -} - -// MNP (Mobile Number Portability) business entity -export interface MnpDetails { - currentProvider: string; - phoneNumber: string; - accountNumber?: string; - pin?: string; -} - -export interface LinkWhmcsRequest { - email: string; - whmcsToken?: string; // From OIDC or ValidateLogin -} - -export interface SetPasswordRequest { - password: string; - confirmPassword: string; -} diff --git a/packages/domain/src/validation/api/requests.d.ts b/packages/domain/src/validation/api/requests.d.ts deleted file mode 100644 index bd6ddf3c..00000000 --- a/packages/domain/src/validation/api/requests.d.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { z } from "zod"; -export declare const paginationQuerySchema: z.ZodObject<{ - page: z.ZodDefault>; - limit: z.ZodDefault>; -}, z.core.$strip>; -export type PaginationQuery = z.infer; -export declare const auditLogQuerySchema: z.ZodObject<{ - page: z.ZodDefault>; - limit: z.ZodDefault>; - action: z.ZodOptional; - userId: z.ZodOptional; -}, z.core.$strip>; -export type AuditLogQuery = z.infer; -export declare const dryRunQuerySchema: z.ZodObject<{ - dryRun: z.ZodOptional>; -}, z.core.$strip>; -export type DryRunQuery = z.infer; -export declare const invoiceListQuerySchema: z.ZodObject<{ - page: z.ZodDefault>; - limit: z.ZodDefault>; - status: z.ZodOptional>; -}, z.core.$strip>; -export type InvoiceListQuery = z.infer; -export declare const subscriptionQuerySchema: z.ZodObject<{ - status: z.ZodOptional>; -}, z.core.$strip>; -export type SubscriptionQuery = z.infer; -export declare const loginRequestSchema: z.ZodObject<{ - email: z.ZodString; - password: z.ZodString; -}, z.core.$strip>; -export declare const signupRequestSchema: z.ZodObject<{ - email: z.ZodString; - password: z.ZodString; - firstName: z.ZodString; - lastName: z.ZodString; - company: z.ZodOptional; - phone: z.ZodString; - sfNumber: z.ZodString; - address: z.ZodObject<{ - street: z.ZodString; - streetLine2: z.ZodOptional; - city: z.ZodString; - state: z.ZodString; - postalCode: z.ZodString; - country: z.ZodString; - }, z.core.$strip>; - nationality: z.ZodOptional; - dateOfBirth: z.ZodOptional; - gender: z.ZodOptional>; -}, z.core.$strip>; -export declare const passwordResetRequestSchema: z.ZodObject<{ - email: z.ZodString; -}, z.core.$strip>; -export declare const passwordResetSchema: z.ZodObject<{ - token: z.ZodString; - password: z.ZodString; -}, z.core.$strip>; -export declare const setPasswordRequestSchema: z.ZodObject<{ - email: z.ZodString; - password: z.ZodString; -}, z.core.$strip>; -export declare const changePasswordRequestSchema: z.ZodObject<{ - currentPassword: z.ZodString; - newPassword: z.ZodString; -}, z.core.$strip>; -export declare const linkWhmcsRequestSchema: z.ZodObject<{ - email: z.ZodString; - password: z.ZodString; -}, z.core.$strip>; -export declare const validateSignupRequestSchema: z.ZodObject<{ - sfNumber: z.ZodString; -}, z.core.$strip>; -export declare const accountStatusRequestSchema: z.ZodObject<{ - email: z.ZodString; -}, z.core.$strip>; -export declare const ssoLinkRequestSchema: z.ZodObject<{ - destination: z.ZodOptional; -}, z.core.$strip>; -export declare const checkPasswordNeededRequestSchema: z.ZodObject<{ - email: z.ZodString; -}, z.core.$strip>; -export declare const refreshTokenRequestSchema: z.ZodObject<{ - refreshToken: z.ZodOptional; - deviceId: z.ZodOptional; -}, z.core.$strip>; -export type LoginRequestInput = z.infer; -export type SignupRequestInput = z.infer; -export type PasswordResetRequestInput = z.infer; -export type PasswordResetInput = z.infer; -export type SetPasswordRequestInput = z.infer; -export type ChangePasswordRequestInput = z.infer; -export type LinkWhmcsRequestInput = z.infer; -export type ValidateSignupRequestInput = z.infer; -export type AccountStatusRequestInput = z.infer; -export type SsoLinkRequestInput = z.infer; -export type CheckPasswordNeededRequestInput = z.infer; -export type RefreshTokenRequestInput = z.infer; -export declare const updateProfileRequestSchema: z.ZodObject<{ - firstName: z.ZodOptional; - lastName: z.ZodOptional; - phone: z.ZodOptional; - company: z.ZodOptional; -}, z.core.$strip>; -export declare const updateAddressRequestSchema: z.ZodObject<{ - street: z.ZodNullable; - streetLine2: z.ZodNullable; - city: z.ZodNullable; - state: z.ZodNullable; - postalCode: z.ZodNullable; - country: z.ZodNullable; -}, z.core.$strip>; -export declare const orderConfigurationsSchema: z.ZodObject<{ - activationType: z.ZodOptional>; - scheduledAt: z.ZodOptional; - accessMode: z.ZodOptional>; - simType: z.ZodOptional>; - eid: z.ZodOptional; - isMnp: z.ZodOptional; - mnpNumber: z.ZodOptional; - mnpExpiry: z.ZodOptional; - mnpPhone: z.ZodOptional; - mvnoAccountNumber: z.ZodOptional; - portingLastName: z.ZodOptional; - portingFirstName: z.ZodOptional; - portingLastNameKatakana: z.ZodOptional; - portingFirstNameKatakana: z.ZodOptional; - portingGender: z.ZodOptional>; - portingDateOfBirth: z.ZodOptional; - address: z.ZodOptional; - streetLine2: z.ZodNullable; - city: z.ZodNullable; - state: z.ZodNullable; - postalCode: z.ZodNullable; - country: z.ZodNullable; - }, z.core.$strip>>; -}, z.core.$strip>; -export declare const createOrderRequestSchema: z.ZodObject<{ - orderType: z.ZodEnum<{ - SIM: "SIM"; - Internet: "Internet"; - VPN: "VPN"; - Other: "Other"; - }>; - skus: z.ZodArray; - configurations: z.ZodOptional>; - scheduledAt: z.ZodOptional; - accessMode: z.ZodOptional>; - simType: z.ZodOptional>; - eid: z.ZodOptional; - isMnp: z.ZodOptional; - mnpNumber: z.ZodOptional; - mnpExpiry: z.ZodOptional; - mnpPhone: z.ZodOptional; - mvnoAccountNumber: z.ZodOptional; - portingLastName: z.ZodOptional; - portingFirstName: z.ZodOptional; - portingLastNameKatakana: z.ZodOptional; - portingFirstNameKatakana: z.ZodOptional; - portingGender: z.ZodOptional>; - portingDateOfBirth: z.ZodOptional; - address: z.ZodOptional; - streetLine2: z.ZodNullable; - city: z.ZodNullable; - state: z.ZodNullable; - postalCode: z.ZodNullable; - country: z.ZodNullable; - }, z.core.$strip>>; - }, z.core.$strip>>; -}, z.core.$strip>; -export declare const orderIdParamSchema: z.ZodObject<{ - id: z.ZodCoercedNumber; -}, z.core.$strip>; -export declare const simTopupRequestSchema: z.ZodObject<{ - amount: z.ZodNumber; - currency: z.ZodDefault; - quotaMb: z.ZodNumber; -}, z.core.$strip>; -export declare const simCancelRequestSchema: z.ZodObject<{ - reason: z.ZodOptional; - scheduledAt: z.ZodOptional; -}, z.core.$strip>; -export declare const simChangePlanRequestSchema: z.ZodObject<{ - newPlanSku: z.ZodString; - newPlanCode: z.ZodString; - effectiveDate: z.ZodOptional; -}, z.core.$strip>; -export declare const simFeaturesRequestSchema: z.ZodObject<{ - voiceMailEnabled: z.ZodOptional; - callWaitingEnabled: z.ZodOptional; - internationalRoamingEnabled: z.ZodOptional; - networkType: z.ZodOptional>; -}, z.core.$strip>; -export declare const contactRequestSchema: z.ZodObject<{ - subject: z.ZodString; - message: z.ZodString; - category: z.ZodEnum<{ - billing: "billing"; - technical: "technical"; - account: "account"; - general: "general"; - }>; - priority: z.ZodDefault>; -}, z.core.$strip>; -export type UpdateProfileRequest = z.infer; -export type UpdateAddressRequest = z.infer; -export type OrderConfigurations = z.infer; -export type CreateOrderRequest = z.infer; -export type SimTopupRequest = z.infer; -export type SimCancelRequest = z.infer; -export type SimChangePlanRequest = z.infer; -export type SimFeaturesRequest = z.infer; -export type ContactRequest = z.infer; -export declare const invoiceItemSchema: z.ZodObject<{ - id: z.ZodNumber; - description: z.ZodString; - amount: z.ZodNumber; - quantity: z.ZodDefault>; - type: z.ZodString; - serviceId: z.ZodOptional; -}, z.core.$strip>; -export declare const invoiceSchema: z.ZodObject<{ - id: z.ZodNumber; - number: z.ZodString; - status: z.ZodEnum<{ - Draft: "Draft"; - Pending: "Pending"; - Paid: "Paid"; - Unpaid: "Unpaid"; - Overdue: "Overdue"; - Cancelled: "Cancelled"; - Refunded: "Refunded"; - Collections: "Collections"; - }>; - currency: z.ZodString; - currencySymbol: z.ZodOptional; - total: z.ZodNumber; - subtotal: z.ZodNumber; - tax: z.ZodNumber; - issuedAt: z.ZodOptional; - dueDate: z.ZodOptional; - paidDate: z.ZodOptional; - pdfUrl: z.ZodOptional; - paymentUrl: z.ZodOptional; - description: z.ZodOptional; - items: z.ZodOptional>; - type: z.ZodString; - serviceId: z.ZodOptional; - }, z.core.$strip>>>; -}, z.core.$strip>; -export declare const paginationSchema: z.ZodObject<{ - page: z.ZodNumber; - totalPages: z.ZodNumber; - totalItems: z.ZodNumber; - nextCursor: z.ZodOptional; -}, z.core.$strip>; -export declare const invoiceListSchema: z.ZodObject<{ - invoices: z.ZodArray; - currency: z.ZodString; - currencySymbol: z.ZodOptional; - total: z.ZodNumber; - subtotal: z.ZodNumber; - tax: z.ZodNumber; - issuedAt: z.ZodOptional; - dueDate: z.ZodOptional; - paidDate: z.ZodOptional; - pdfUrl: z.ZodOptional; - paymentUrl: z.ZodOptional; - description: z.ZodOptional; - items: z.ZodOptional>; - type: z.ZodString; - serviceId: z.ZodOptional; - }, z.core.$strip>>>; - }, z.core.$strip>>; - pagination: z.ZodObject<{ - page: z.ZodNumber; - totalPages: z.ZodNumber; - totalItems: z.ZodNumber; - nextCursor: z.ZodOptional; - }, z.core.$strip>; -}, z.core.$strip>; -export type InvoiceItem = z.infer; -export type Invoice = z.infer; -export type Pagination = z.infer; -export type InvoiceList = z.infer; -export declare const invoicePaymentLinkSchema: z.ZodObject<{ - paymentMethodId: z.ZodOptional>; - gatewayName: z.ZodOptional; -}, z.core.$strip>; -export type InvoicePaymentLinkInput = z.infer; -export declare const sfOrderIdParamSchema: z.ZodObject<{ - sfOrderId: z.ZodString; -}, z.core.$strip>; -export type SfOrderIdParam = z.infer; -export declare const createMappingRequestSchema: z.ZodObject<{ - userId: z.ZodString; - whmcsClientId: z.ZodNumber; - sfAccountId: z.ZodOptional; -}, z.core.$strip>; -export declare const updateMappingRequestSchema: z.ZodObject<{ - whmcsClientId: z.ZodOptional; - sfAccountId: z.ZodOptional; -}, z.core.$strip>; -export declare const userIdMappingSchema: z.ZodObject<{ - userId: z.ZodString; - whmcsClientId: z.ZodNumber; - sfAccountId: z.ZodOptional; - createdAt: z.ZodOptional; - updatedAt: z.ZodOptional; -}, z.core.$strip>; -export type CreateMappingRequest = z.infer; -export type UpdateMappingRequest = z.infer; -export type UserIdMapping = z.infer; -export type UpdateProfileRequestInput = z.infer; -export type UpdateAddressRequestInput = z.infer; -export type ContactRequestInput = z.infer; diff --git a/packages/domain/src/validation/api/requests.js b/packages/domain/src/validation/api/requests.js deleted file mode 100644 index f113e82c..00000000 --- a/packages/domain/src/validation/api/requests.js +++ /dev/null @@ -1,222 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.userIdMappingSchema = exports.updateMappingRequestSchema = exports.createMappingRequestSchema = exports.sfOrderIdParamSchema = exports.invoicePaymentLinkSchema = exports.invoiceListSchema = exports.paginationSchema = exports.invoiceSchema = exports.invoiceItemSchema = exports.contactRequestSchema = exports.simFeaturesRequestSchema = exports.simChangePlanRequestSchema = exports.simCancelRequestSchema = exports.simTopupRequestSchema = exports.orderIdParamSchema = exports.createOrderRequestSchema = exports.orderConfigurationsSchema = exports.updateAddressRequestSchema = exports.updateProfileRequestSchema = exports.refreshTokenRequestSchema = exports.checkPasswordNeededRequestSchema = exports.ssoLinkRequestSchema = exports.accountStatusRequestSchema = exports.validateSignupRequestSchema = exports.linkWhmcsRequestSchema = exports.changePasswordRequestSchema = exports.setPasswordRequestSchema = exports.passwordResetSchema = exports.passwordResetRequestSchema = exports.signupRequestSchema = exports.loginRequestSchema = exports.subscriptionQuerySchema = exports.invoiceListQuerySchema = exports.dryRunQuerySchema = exports.auditLogQuerySchema = exports.paginationQuerySchema = void 0; -const zod_1 = require("zod"); -const primitives_1 = require("../shared/primitives"); -const entities_1 = require("../shared/entities"); -const invoiceStatusEnum = zod_1.z.enum(["Paid", "Unpaid", "Overdue", "Cancelled", "Collections"]); -const subscriptionStatusEnum = zod_1.z.enum([ - "Active", - "Suspended", - "Terminated", - "Cancelled", - "Pending", -]); -exports.paginationQuerySchema = zod_1.z.object({ - page: zod_1.z.coerce.number().int().min(1).default(1), - limit: zod_1.z.coerce.number().int().min(1).max(100).default(10), -}); -exports.auditLogQuerySchema = exports.paginationQuerySchema.extend({ - action: zod_1.z.string().optional(), - userId: zod_1.z.string().uuid().optional(), -}); -exports.dryRunQuerySchema = zod_1.z.object({ - dryRun: zod_1.z.coerce.boolean().optional(), -}); -exports.invoiceListQuerySchema = exports.paginationQuerySchema.extend({ - status: invoiceStatusEnum.optional(), -}); -exports.subscriptionQuerySchema = zod_1.z.object({ - status: subscriptionStatusEnum.optional(), -}); -exports.loginRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, - password: zod_1.z.string().min(1, "Password is required"), -}); -exports.signupRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, - password: primitives_1.passwordSchema, - firstName: primitives_1.nameSchema, - lastName: primitives_1.nameSchema, - company: zod_1.z.string().optional(), - phone: primitives_1.phoneSchema, - sfNumber: zod_1.z.string().min(6, "Customer number must be at least 6 characters"), - address: primitives_1.requiredAddressSchema, - nationality: zod_1.z.string().optional(), - dateOfBirth: zod_1.z.string().optional(), - gender: primitives_1.genderEnum.optional(), -}); -exports.passwordResetRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, -}); -exports.passwordResetSchema = zod_1.z.object({ - token: zod_1.z.string().min(1, "Reset token is required"), - password: primitives_1.passwordSchema, -}); -exports.setPasswordRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, - password: primitives_1.passwordSchema, -}); -exports.changePasswordRequestSchema = zod_1.z.object({ - currentPassword: zod_1.z.string().min(1, "Current password is required"), - newPassword: primitives_1.passwordSchema, -}); -exports.linkWhmcsRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, - password: zod_1.z.string().min(1, "Password is required"), -}); -exports.validateSignupRequestSchema = zod_1.z.object({ - sfNumber: zod_1.z.string().min(1, "Customer number is required"), -}); -exports.accountStatusRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, -}); -exports.ssoLinkRequestSchema = zod_1.z.object({ - destination: zod_1.z.string().optional(), -}); -exports.checkPasswordNeededRequestSchema = zod_1.z.object({ - email: primitives_1.emailSchema, -}); -exports.refreshTokenRequestSchema = zod_1.z.object({ - refreshToken: zod_1.z.string().min(1, "Refresh token is required").optional(), - deviceId: zod_1.z.string().optional(), -}); -exports.updateProfileRequestSchema = zod_1.z.object({ - firstName: primitives_1.nameSchema.optional(), - lastName: primitives_1.nameSchema.optional(), - phone: primitives_1.phoneSchema.optional(), - company: zod_1.z.string().max(200).optional(), -}); -exports.updateAddressRequestSchema = primitives_1.addressSchema; -exports.orderConfigurationsSchema = zod_1.z.object({ - activationType: zod_1.z.enum(["Immediate", "Scheduled"]).optional(), - scheduledAt: zod_1.z.string().datetime().optional(), - accessMode: zod_1.z.enum(["IPoE-BYOR", "IPoE-HGW", "PPPoE"]).optional(), - simType: zod_1.z.enum(["eSIM", "Physical SIM"]).optional(), - eid: zod_1.z.string().optional(), - isMnp: zod_1.z.string().optional(), - mnpNumber: zod_1.z.string().optional(), - mnpExpiry: zod_1.z.string().optional(), - mnpPhone: zod_1.z.string().optional(), - mvnoAccountNumber: zod_1.z.string().optional(), - portingLastName: zod_1.z.string().optional(), - portingFirstName: zod_1.z.string().optional(), - portingLastNameKatakana: zod_1.z.string().optional(), - portingFirstNameKatakana: zod_1.z.string().optional(), - portingGender: zod_1.z.enum(["Male", "Female", "Corporate/Other"]).optional(), - portingDateOfBirth: zod_1.z.string().date().optional(), - address: primitives_1.addressSchema.optional(), -}); -exports.createOrderRequestSchema = zod_1.z.object({ - orderType: zod_1.z.enum(["Internet", "SIM", "VPN", "Other"]), - skus: zod_1.z.array(zod_1.z.string().min(1, "SKU cannot be empty")), - configurations: exports.orderConfigurationsSchema.optional(), -}); -exports.orderIdParamSchema = zod_1.z.object({ - id: zod_1.z.coerce.number().int().positive("Order ID must be positive"), -}); -exports.simTopupRequestSchema = zod_1.z.object({ - amount: zod_1.z.number().positive("Amount must be positive"), - currency: zod_1.z.string().length(3, "Currency must be 3 characters").default("JPY"), - quotaMb: zod_1.z.number().positive("Quota in MB must be positive"), -}); -exports.simCancelRequestSchema = zod_1.z.object({ - reason: zod_1.z.string().min(1, "Cancellation reason is required").optional(), - scheduledAt: zod_1.z - .string() - .regex(/^\d{8}$/, "Scheduled date must be in YYYYMMDD format") - .optional(), -}); -exports.simChangePlanRequestSchema = zod_1.z.object({ - newPlanSku: zod_1.z.string().min(1, "New plan SKU is required"), - newPlanCode: zod_1.z.string().min(1, "New plan code is required"), - effectiveDate: zod_1.z.string().date().optional(), -}); -exports.simFeaturesRequestSchema = zod_1.z.object({ - voiceMailEnabled: zod_1.z.boolean().optional(), - callWaitingEnabled: zod_1.z.boolean().optional(), - internationalRoamingEnabled: zod_1.z.boolean().optional(), - networkType: zod_1.z.enum(["4G", "5G"]).optional(), -}); -exports.contactRequestSchema = zod_1.z.object({ - subject: zod_1.z.string().min(1, "Subject is required").max(200, "Subject is too long"), - message: zod_1.z.string().min(1, "Message is required").max(2000, "Message is too long"), - category: zod_1.z.enum(["technical", "billing", "account", "general"]), - priority: zod_1.z.enum(["low", "medium", "high", "urgent"]).default("medium"), -}); -exports.invoiceItemSchema = zod_1.z.object({ - id: zod_1.z.number().int().positive(), - description: zod_1.z.string().min(1, "Description is required"), - amount: zod_1.z.number().nonnegative("Amount must be non-negative"), - quantity: zod_1.z.number().int().positive().optional().default(1), - type: zod_1.z.string().min(1, "Type is required"), - serviceId: zod_1.z.number().int().positive().optional(), -}); -exports.invoiceSchema = zod_1.z.object({ - id: zod_1.z.number().int().positive(), - number: zod_1.z.string().min(1, "Invoice number is required"), - status: entities_1.invoiceStatusSchema, - currency: zod_1.z.string().length(3, "Currency must be 3 characters"), - currencySymbol: zod_1.z.string().optional(), - total: zod_1.z.number().nonnegative("Total must be non-negative"), - subtotal: zod_1.z.number().nonnegative("Subtotal must be non-negative"), - tax: zod_1.z.number().nonnegative("Tax must be non-negative"), - issuedAt: zod_1.z.string().datetime().optional(), - dueDate: zod_1.z.string().datetime().optional(), - paidDate: zod_1.z.string().datetime().optional(), - pdfUrl: zod_1.z.string().url().optional(), - paymentUrl: zod_1.z.string().url().optional(), - description: zod_1.z.string().optional(), - items: zod_1.z.array(exports.invoiceItemSchema).optional(), -}); -exports.paginationSchema = zod_1.z.object({ - page: zod_1.z.number().int().min(1), - totalPages: zod_1.z.number().int().min(0), - totalItems: zod_1.z.number().int().min(0), - nextCursor: zod_1.z.string().optional(), -}); -exports.invoiceListSchema = zod_1.z.object({ - invoices: zod_1.z.array(exports.invoiceSchema), - pagination: exports.paginationSchema, -}); -exports.invoicePaymentLinkSchema = zod_1.z.object({ - paymentMethodId: zod_1.z.coerce.number().int().positive().optional(), - gatewayName: zod_1.z.string().min(1).optional(), -}); -exports.sfOrderIdParamSchema = zod_1.z.object({ - sfOrderId: zod_1.z.string().min(1, "Salesforce order ID is required"), -}); -exports.createMappingRequestSchema = zod_1.z.object({ - userId: zod_1.z.string().uuid("User ID must be a valid UUID"), - whmcsClientId: zod_1.z.number().int().positive("WHMCS client ID must be a positive integer"), - sfAccountId: zod_1.z - .string() - .regex(/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, "Salesforce account ID must be a valid 15 or 18 character ID") - .optional(), -}); -exports.updateMappingRequestSchema = zod_1.z - .object({ - whmcsClientId: zod_1.z - .number() - .int() - .positive("WHMCS client ID must be a positive integer") - .optional(), - sfAccountId: zod_1.z - .string() - .regex(/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, "Salesforce account ID must be a valid 15 or 18 character ID") - .optional(), -}) - .refine(data => data.whmcsClientId !== undefined || data.sfAccountId !== undefined, { - message: "At least one field must be provided for update", -}); -exports.userIdMappingSchema = zod_1.z.object({ - userId: zod_1.z.string().uuid("User ID must be a valid UUID"), - whmcsClientId: zod_1.z.number().int().positive("WHMCS client ID must be a positive integer"), - sfAccountId: zod_1.z - .string() - .regex(/^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, "Salesforce account ID must be a valid 15 or 18 character ID") - .optional(), - createdAt: zod_1.z.string().datetime().optional(), - updatedAt: zod_1.z.string().datetime().optional(), -}); -//# sourceMappingURL=requests.js.map \ No newline at end of file diff --git a/packages/domain/src/validation/api/requests.ts b/packages/domain/src/validation/api/requests.ts deleted file mode 100644 index 5828f130..00000000 --- a/packages/domain/src/validation/api/requests.ts +++ /dev/null @@ -1,379 +0,0 @@ -/** - * API Request Schemas - * Schemas for data that the backend receives and validates - * These are the "source of truth" for business logic validation - */ - -import { z } from "zod"; -import { - emailSchema, - passwordSchema, - nameSchema, - phoneSchema, - addressSchema, - requiredAddressSchema, - genderEnum, -} from "../shared/primitives"; -import { invoiceStatusSchema } from "../shared/entities"; - -const invoiceStatusEnum = z.enum(["Paid", "Unpaid", "Overdue", "Cancelled", "Collections"]); -const subscriptionStatusEnum = z.enum([ - "Active", - "Suspended", - "Terminated", - "Cancelled", - "Pending", -]); - -export const paginationQuerySchema = z.object({ - page: z.coerce.number().int().min(1).default(1), - limit: z.coerce.number().int().min(1).max(100).default(10), -}); - -export type PaginationQuery = z.infer; - -export const auditLogQuerySchema = paginationQuerySchema.extend({ - action: z.string().optional(), - userId: z.string().uuid().optional(), -}); - -export type AuditLogQuery = z.infer; - -export const dryRunQuerySchema = z.object({ - dryRun: z.coerce.boolean().optional(), -}); - -export type DryRunQuery = z.infer; - -export const invoiceListQuerySchema = paginationQuerySchema.extend({ - status: invoiceStatusEnum.optional(), -}); - -export type InvoiceListQuery = z.infer; - -export const subscriptionQuerySchema = z.object({ - status: subscriptionStatusEnum.optional(), -}); - -export type SubscriptionQuery = z.infer; - -// ===================================================== -// AUTH REQUEST SCHEMAS -// ===================================================== - -export const loginRequestSchema = z.object({ - email: emailSchema, - password: z.string().min(1, "Password is required"), -}); - -export const signupRequestSchema = z.object({ - email: emailSchema, - password: passwordSchema, - firstName: nameSchema, - lastName: nameSchema, - company: z.string().optional(), - phone: phoneSchema, - sfNumber: z.string().min(6, "Customer number must be at least 6 characters"), - address: requiredAddressSchema, - nationality: z.string().optional(), - dateOfBirth: z.string().optional(), - gender: genderEnum.optional(), -}); - -export const passwordResetRequestSchema = z.object({ - email: emailSchema, -}); - -export const passwordResetSchema = z.object({ - token: z.string().min(1, "Reset token is required"), - password: passwordSchema, -}); - -export const setPasswordRequestSchema = z.object({ - email: emailSchema, - password: passwordSchema, -}); - -export const changePasswordRequestSchema = z.object({ - currentPassword: z.string().min(1, "Current password is required"), - newPassword: passwordSchema, -}); - -export const linkWhmcsRequestSchema = z.object({ - email: emailSchema, - password: z.string().min(1, "Password is required"), -}); - -export const validateSignupRequestSchema = z.object({ - sfNumber: z.string().min(1, "Customer number is required"), -}); - -export const accountStatusRequestSchema = z.object({ - email: emailSchema, -}); - -export const ssoLinkRequestSchema = z.object({ - destination: z.string().optional(), -}); - -export const checkPasswordNeededRequestSchema = z.object({ - email: emailSchema, -}); - -export const refreshTokenRequestSchema = z.object({ - refreshToken: z.string().min(1, "Refresh token is required").optional(), - deviceId: z.string().optional(), -}); - -// ===================================================== -// TYPE EXPORTS -// ===================================================== - -export type LoginRequestInput = z.infer; -export type SignupRequestInput = z.infer; -export type PasswordResetRequestInput = z.infer; -export type PasswordResetInput = z.infer; -export type SetPasswordRequestInput = z.infer; -export type ChangePasswordRequestInput = z.infer; -export type LinkWhmcsRequestInput = z.infer; -export type ValidateSignupRequestInput = z.infer; -export type AccountStatusRequestInput = z.infer; -export type SsoLinkRequestInput = z.infer; -export type CheckPasswordNeededRequestInput = z.infer; -export type RefreshTokenRequestInput = z.infer; - -// ===================================================== -// USER MANAGEMENT REQUEST SCHEMAS -// ===================================================== - -export const updateProfileRequestSchema = z.object({ - firstName: nameSchema.optional(), - lastName: nameSchema.optional(), - phone: phoneSchema.optional(), - company: z.string().max(200).optional(), -}); - -export const updateAddressRequestSchema = addressSchema; - -// ===================================================== -// ORDER REQUEST SCHEMAS -// ===================================================== - -export const orderConfigurationsSchema = z.object({ - // Activation (All order types) - activationType: z.enum(["Immediate", "Scheduled"]).optional(), - scheduledAt: z.string().datetime().optional(), - - // Internet specific - accessMode: z.enum(["IPoE-BYOR", "IPoE-HGW", "PPPoE"]).optional(), - - // SIM specific - simType: z.enum(["eSIM", "Physical SIM"]).optional(), - eid: z.string().optional(), // Required for eSIM - - // MNP/Porting - isMnp: z.string().optional(), // "true" | "false" - mnpNumber: z.string().optional(), - mnpExpiry: z.string().optional(), - mnpPhone: z.string().optional(), - mvnoAccountNumber: z.string().optional(), - portingLastName: z.string().optional(), - portingFirstName: z.string().optional(), - portingLastNameKatakana: z.string().optional(), - portingFirstNameKatakana: z.string().optional(), - portingGender: z.enum(["Male", "Female", "Corporate/Other"]).optional(), - portingDateOfBirth: z.string().date().optional(), - - // Optional address override captured at checkout - address: addressSchema.optional(), -}); - -export const createOrderRequestSchema = z.object({ - orderType: z.enum(["Internet", "SIM", "VPN", "Other"]), - skus: z.array(z.string().min(1, "SKU cannot be empty")), - configurations: orderConfigurationsSchema.optional(), -}); - -export const orderIdParamSchema = z.object({ - id: z.coerce.number().int().positive("Order ID must be positive"), -}); - -// ===================================================== -// SUBSCRIPTION MANAGEMENT REQUEST SCHEMAS -// ===================================================== - -export const simTopupRequestSchema = z.object({ - amount: z.number().positive("Amount must be positive"), - currency: z.string().length(3, "Currency must be 3 characters").default("JPY"), - quotaMb: z.number().positive("Quota in MB must be positive"), -}); - -export const simCancelRequestSchema = z.object({ - reason: z.string().min(1, "Cancellation reason is required").optional(), - scheduledAt: z - .string() - .regex(/^\d{8}$/, "Scheduled date must be in YYYYMMDD format") - .optional(), -}); - -export const simChangePlanRequestSchema = z.object({ - newPlanSku: z.string().min(1, "New plan SKU is required"), - newPlanCode: z.string().min(1, "New plan code is required"), - effectiveDate: z.string().date().optional(), -}); - -export const simFeaturesRequestSchema = z.object({ - voiceMailEnabled: z.boolean().optional(), - callWaitingEnabled: z.boolean().optional(), - internationalRoamingEnabled: z.boolean().optional(), - networkType: z.enum(["4G", "5G"]).optional(), -}); - -// ===================================================== -// CONTACT REQUEST SCHEMAS -// ===================================================== - -export const contactRequestSchema = z.object({ - subject: z.string().min(1, "Subject is required").max(200, "Subject is too long"), - message: z.string().min(1, "Message is required").max(2000, "Message is too long"), - category: z.enum(["technical", "billing", "account", "general"]), - priority: z.enum(["low", "medium", "high", "urgent"]).default("medium"), -}); - -// ===================================================== -// TYPE EXPORTS -// ===================================================== - -export type UpdateProfileRequest = z.infer; -export type UpdateAddressRequest = z.infer; -export type OrderConfigurations = z.infer; -export type CreateOrderRequest = z.infer; -export type SimTopupRequest = z.infer; -export type SimCancelRequest = z.infer; -export type SimChangePlanRequest = z.infer; -export type SimFeaturesRequest = z.infer; -export type ContactRequest = z.infer; - -// ===================================================== -// INVOICE SCHEMAS -// ===================================================== - -export const invoiceItemSchema = z.object({ - id: z.number().int().positive(), - description: z.string().min(1, "Description is required"), - amount: z.number().nonnegative("Amount must be non-negative"), - quantity: z.number().int().positive().optional().default(1), - type: z.string().min(1, "Type is required"), - serviceId: z.number().int().positive().optional(), -}); - -export const invoiceSchema = z.object({ - id: z.number().int().positive(), - number: z.string().min(1, "Invoice number is required"), - status: invoiceStatusSchema, - currency: z.string().length(3, "Currency must be 3 characters"), - currencySymbol: z.string().optional(), - total: z.number().nonnegative("Total must be non-negative"), - subtotal: z.number().nonnegative("Subtotal must be non-negative"), - tax: z.number().nonnegative("Tax must be non-negative"), - issuedAt: z.string().datetime().optional(), - dueDate: z.string().datetime().optional(), - paidDate: z.string().datetime().optional(), - pdfUrl: z.string().url().optional(), - paymentUrl: z.string().url().optional(), - description: z.string().optional(), - items: z.array(invoiceItemSchema).optional(), -}); - -export const paginationSchema = z.object({ - page: z.number().int().min(1), - totalPages: z.number().int().min(0), - totalItems: z.number().int().min(0), - nextCursor: z.string().optional(), -}); - -export const invoiceListSchema = z.object({ - invoices: z.array(invoiceSchema), - pagination: paginationSchema, -}); - -// ===================================================== -// INVOICE TYPE EXPORTS -// ===================================================== - -export type InvoiceItem = z.infer; -export type Invoice = z.infer; -export type Pagination = z.infer; -export type InvoiceList = z.infer; - -export const invoicePaymentLinkSchema = z.object({ - paymentMethodId: z.coerce.number().int().positive().optional(), - gatewayName: z.string().min(1).optional(), -}); - -export type InvoicePaymentLinkInput = z.infer; - -export const sfOrderIdParamSchema = z.object({ - sfOrderId: z.string().min(1, "Salesforce order ID is required"), -}); - -export type SfOrderIdParam = z.infer; -// ===================================================== -// ID MAPPING SCHEMAS -// ===================================================== - -export const createMappingRequestSchema = z.object({ - userId: z.string().uuid("User ID must be a valid UUID"), - whmcsClientId: z.number().int().positive("WHMCS client ID must be a positive integer"), - sfAccountId: z - .string() - .regex( - /^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, - "Salesforce account ID must be a valid 15 or 18 character ID" - ) - .optional(), -}); - -export const updateMappingRequestSchema = z - .object({ - whmcsClientId: z - .number() - .int() - .positive("WHMCS client ID must be a positive integer") - .optional(), - sfAccountId: z - .string() - .regex( - /^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, - "Salesforce account ID must be a valid 15 or 18 character ID" - ) - .optional(), - }) - .refine(data => data.whmcsClientId !== undefined || data.sfAccountId !== undefined, { - message: "At least one field must be provided for update", - }); - -export const userIdMappingSchema = z.object({ - userId: z.string().uuid("User ID must be a valid UUID"), - whmcsClientId: z.number().int().positive("WHMCS client ID must be a positive integer"), - sfAccountId: z - .string() - .regex( - /^[a-zA-Z0-9]{15}$|^[a-zA-Z0-9]{18}$/, - "Salesforce account ID must be a valid 15 or 18 character ID" - ) - .optional(), - createdAt: z.string().datetime().optional(), - updatedAt: z.string().datetime().optional(), -}); - -// ===================================================== -// ID MAPPING TYPE EXPORTS -// ===================================================== - -export type CreateMappingRequest = z.infer; -export type UpdateMappingRequest = z.infer; -export type UserIdMapping = z.infer; -export type UpdateProfileRequestInput = z.infer; -export type UpdateAddressRequestInput = z.infer; -export type ContactRequestInput = z.infer; diff --git a/packages/domain/src/validation/api/responses.d.ts b/packages/domain/src/validation/api/responses.d.ts deleted file mode 100644 index 40ca4a16..00000000 --- a/packages/domain/src/validation/api/responses.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { z } from "zod"; -export declare const authResponseSchema: z.ZodObject<{ - user: z.ZodObject<{ - id: z.ZodString; - createdAt: z.ZodString; - updatedAt: z.ZodString; - email: z.ZodString; - firstName: z.ZodOptional; - lastName: z.ZodOptional; - company: z.ZodOptional; - phone: z.ZodOptional; - address: z.ZodOptional; - streetLine2: z.ZodNullable; - city: z.ZodNullable; - state: z.ZodNullable; - postalCode: z.ZodNullable; - country: z.ZodNullable; - }, z.core.$strip>>; - mfaEnabled: z.ZodBoolean; - emailVerified: z.ZodBoolean; - avatar: z.ZodOptional; - preferences: z.ZodOptional>; - lastLoginAt: z.ZodOptional; - role: z.ZodEnum<{ - USER: "USER"; - ADMIN: "ADMIN"; - }>; - }, z.core.$strip>; - tokens: z.ZodObject<{ - accessToken: z.ZodString; - refreshToken: z.ZodString; - expiresAt: z.ZodString; - refreshExpiresAt: z.ZodString; - tokenType: z.ZodLiteral<"Bearer">; - }, z.core.$strip>; -}, z.core.$strip>; -export type AuthResponse = z.infer; -export type AuthTokensSchema = AuthResponse["tokens"]; diff --git a/packages/domain/src/validation/api/responses.js b/packages/domain/src/validation/api/responses.js deleted file mode 100644 index 88fc0f8e..00000000 --- a/packages/domain/src/validation/api/responses.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.authResponseSchema = void 0; -const zod_1 = require("zod"); -const entities_1 = require("../shared/entities"); -exports.authResponseSchema = zod_1.z.object({ - user: entities_1.userProfileSchema, - tokens: zod_1.z.object({ - accessToken: zod_1.z.string().min(1, "Access token is required"), - refreshToken: zod_1.z.string().min(1, "Refresh token is required"), - expiresAt: zod_1.z.string().min(1, "Access token expiry required"), - refreshExpiresAt: zod_1.z.string().min(1, "Refresh token expiry required"), - tokenType: zod_1.z.literal("Bearer"), - }), -}); -//# sourceMappingURL=responses.js.map \ No newline at end of file diff --git a/packages/domain/src/validation/api/responses.ts b/packages/domain/src/validation/api/responses.ts deleted file mode 100644 index 69b04f99..00000000 --- a/packages/domain/src/validation/api/responses.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from "zod"; - -import { userProfileSchema } from "../shared/entities"; - -export const authResponseSchema = z.object({ - user: userProfileSchema, - tokens: z.object({ - accessToken: z.string().min(1, "Access token is required"), - refreshToken: z.string().min(1, "Refresh token is required"), - expiresAt: z.string().min(1, "Access token expiry required"), - refreshExpiresAt: z.string().min(1, "Refresh token expiry required"), - tokenType: z.literal("Bearer"), - }), -}); - -export type AuthResponse = z.infer; -export type AuthTokensSchema = AuthResponse["tokens"]; diff --git a/packages/domain/src/validation/business/index.js b/packages/domain/src/validation/business/index.js deleted file mode 100644 index 6573f559..00000000 --- a/packages/domain/src/validation/business/index.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("./orders"), exports); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/domain/src/validation/business/index.ts b/packages/domain/src/validation/business/index.ts deleted file mode 100644 index d0d5eb00..00000000 --- a/packages/domain/src/validation/business/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Business Validation Rules - * Centralized business logic validation - */ - -export * from "./orders"; diff --git a/packages/domain/src/validation/business/orders.d.ts b/packages/domain/src/validation/business/orders.d.ts index 563c8085..0acf20a4 100644 --- a/packages/domain/src/validation/business/orders.d.ts +++ b/packages/domain/src/validation/business/orders.d.ts @@ -1,8 +1,8 @@ import { z } from "zod"; export declare const orderBusinessValidationSchema: z.ZodObject<{ orderType: z.ZodEnum<{ - SIM: "SIM"; Internet: "Internet"; + SIM: "SIM"; VPN: "VPN"; Other: "Other"; }>; @@ -14,8 +14,8 @@ export declare const orderBusinessValidationSchema: z.ZodObject<{ }>>; scheduledAt: z.ZodOptional; accessMode: z.ZodOptional>; simType: z.ZodOptional { - if (data.orderType === "Internet") { - const mainServiceSkus = data.skus.filter(sku => { - const upperSku = sku.toUpperCase(); - return (!upperSku.includes("INSTALL") && - !upperSku.includes("ADDON") && - !upperSku.includes("ACTIVATION") && - !upperSku.includes("FEE")); - }); - return mainServiceSkus.length >= 1; - } - return true; -}, { - message: "Internet orders must have at least one main service SKU (non-installation, non-addon)", - path: ["skus"], -}) - .refine(data => { - if (data.orderType === "SIM" && data.configurations) { - return data.configurations.simType !== undefined; - } - return true; -}, { - message: "SIM orders must specify SIM type", - path: ["configurations", "simType"], -}) - .refine(data => { - if (data.configurations?.simType === "eSIM") { - return data.configurations.eid !== undefined && data.configurations.eid.length > 0; - } - return true; -}, { - message: "eSIM orders must provide EID", - path: ["configurations", "eid"], -}) - .refine(data => { - if (data.configurations?.isMnp === "true") { - const required = ["mnpNumber", "portingLastName", "portingFirstName"]; - return required.every(field => data.configurations?.[field] !== undefined); - } - return true; -}, { - message: "MNP orders must provide porting information", - path: ["configurations"], -}); -exports.skuValidationSchema = zod_1.z.object({ - sku: zod_1.z.string().min(1, "SKU is required"), - isActive: zod_1.z.boolean(), - productType: zod_1.z.enum(["Internet", "SIM", "VPN", "Addon", "Fee"]), - price: zod_1.z.number().nonnegative(), - currency: zod_1.z.string().length(3), -}); -exports.userMappingValidationSchema = zod_1.z.object({ - userId: identifiers_1.userIdSchema, - sfAccountId: zod_1.z.string().min(15, "Salesforce Account ID must be at least 15 characters"), - whmcsClientId: zod_1.z.number().int().positive("WHMCS Client ID must be positive"), -}); -exports.paymentMethodValidationSchema = zod_1.z.object({ - userId: identifiers_1.userIdSchema, - whmcsClientId: zod_1.z.number().int().positive(), - hasValidPaymentMethod: zod_1.z.boolean(), - paymentMethods: zod_1.z.array(zod_1.z.object({ - id: zod_1.z.string(), - type: zod_1.z.string(), - isDefault: zod_1.z.boolean(), - })), -}); -//# sourceMappingURL=orders.js.map \ No newline at end of file diff --git a/packages/domain/src/validation/business/orders.ts b/packages/domain/src/validation/business/orders.ts deleted file mode 100644 index 91f06333..00000000 --- a/packages/domain/src/validation/business/orders.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Order Business Validation - * Business logic validation rules for orders - */ - -import { z } from "zod"; -import { createOrderRequestSchema } from "../api/requests"; -import { userIdSchema } from "../shared/identifiers"; - -// ===================================================== -// BUSINESS VALIDATION SCHEMAS -// ===================================================== - -export const orderBusinessValidationSchema = createOrderRequestSchema - .extend({ - userId: userIdSchema, - opportunityId: z.string().optional(), - }) - .refine( - data => { - // Business rule: Internet orders can have one main service + installations + addons - if (data.orderType === "Internet") { - // Allow multiple SKUs but ensure at least one main service - // Main service SKUs don't contain "install", "addon", "activation", or "fee" - const mainServiceSkus = data.skus.filter(sku => { - const upperSku = sku.toUpperCase(); - return ( - !upperSku.includes("INSTALL") && - !upperSku.includes("ADDON") && - !upperSku.includes("ACTIVATION") && - !upperSku.includes("FEE") - ); - }); - return mainServiceSkus.length >= 1; // At least one main service required - } - return true; - }, - { - message: - "Internet orders must have at least one main service SKU (non-installation, non-addon)", - path: ["skus"], - } - ) - .refine( - data => { - // Business rule: SIM orders require SIM-specific configuration - if (data.orderType === "SIM" && data.configurations) { - return data.configurations.simType !== undefined; - } - return true; - }, - { - message: "SIM orders must specify SIM type", - path: ["configurations", "simType"], - } - ) - .refine( - data => { - // Business rule: eSIM orders require EID - if (data.configurations?.simType === "eSIM") { - return data.configurations.eid !== undefined && data.configurations.eid.length > 0; - } - return true; - }, - { - message: "eSIM orders must provide EID", - path: ["configurations", "eid"], - } - ) - .refine( - data => { - // Business rule: MNP orders require additional fields - if (data.configurations?.isMnp === "true") { - const required = ["mnpNumber", "portingLastName", "portingFirstName"]; - return required.every( - field => data.configurations?.[field as keyof typeof data.configurations] !== undefined - ); - } - return true; - }, - { - message: "MNP orders must provide porting information", - path: ["configurations"], - } - ); - -// SKU validation schema -export const skuValidationSchema = z.object({ - sku: z.string().min(1, "SKU is required"), - isActive: z.boolean(), - productType: z.enum(["Internet", "SIM", "VPN", "Addon", "Fee"]), - price: z.number().nonnegative(), - currency: z.string().length(3), -}); - -// User mapping validation schema -export const userMappingValidationSchema = z.object({ - userId: userIdSchema, - sfAccountId: z.string().min(15, "Salesforce Account ID must be at least 15 characters"), - whmcsClientId: z.number().int().positive("WHMCS Client ID must be positive"), -}); - -// Payment method validation schema -export const paymentMethodValidationSchema = z.object({ - userId: userIdSchema, - whmcsClientId: z.number().int().positive(), - hasValidPaymentMethod: z.boolean(), - paymentMethods: z.array( - z.object({ - id: z.string(), - type: z.string(), - isDefault: z.boolean(), - }) - ), -}); - -// ===================================================== -// DIRECT ZOD USAGE - NO WRAPPER FUNCTIONS NEEDED -// ===================================================== -// Use schema.safeParse(data) directly instead of wrapper functions - -// ===================================================== -// TYPE EXPORTS -// ===================================================== - -export type OrderBusinessValidation = z.infer; -export type SkuValidation = z.infer; -export type UserMappingValidation = z.infer; -export type PaymentMethodValidation = z.infer; diff --git a/packages/domain/src/validation/forms/auth.d.ts b/packages/domain/src/validation/forms/auth.d.ts deleted file mode 100644 index 1607203d..00000000 --- a/packages/domain/src/validation/forms/auth.d.ts +++ /dev/null @@ -1,68 +0,0 @@ -export declare const loginFormSchema: import("zod").ZodObject<{ - email: import("zod").ZodString; - password: import("zod").ZodString; -}, import("zod/v4/core").$strip>; -export declare const signupFormSchema: import("zod").ZodObject<{ - email: import("zod").ZodString; - password: import("zod").ZodString; - firstName: import("zod").ZodString; - lastName: import("zod").ZodString; - company: import("zod").ZodOptional; - phone: import("zod").ZodString; - sfNumber: import("zod").ZodString; - address: import("zod").ZodObject<{ - street: import("zod").ZodString; - streetLine2: import("zod").ZodOptional; - city: import("zod").ZodString; - state: import("zod").ZodString; - postalCode: import("zod").ZodString; - country: import("zod").ZodString; - }, import("zod/v4/core").$strip>; - nationality: import("zod").ZodOptional; - dateOfBirth: import("zod").ZodOptional; - gender: import("zod").ZodOptional>; -}, import("zod/v4/core").$strip>; -export declare const passwordResetRequestFormSchema: import("zod").ZodObject<{ - email: import("zod").ZodString; -}, import("zod/v4/core").$strip>; -export declare const passwordResetFormSchema: import("zod").ZodObject<{ - token: import("zod").ZodString; - password: import("zod").ZodString; -}, import("zod/v4/core").$strip>; -export declare const setPasswordFormSchema: import("zod").ZodObject<{ - email: import("zod").ZodString; - password: import("zod").ZodString; -}, import("zod/v4/core").$strip>; -export declare const changePasswordFormSchema: import("zod").ZodObject<{ - currentPassword: import("zod").ZodString; - newPassword: import("zod").ZodString; -}, import("zod/v4/core").$strip>; -export declare const linkWhmcsFormSchema: import("zod").ZodObject<{ - email: import("zod").ZodString; - password: import("zod").ZodString; -}, import("zod/v4/core").$strip>; -export declare const loginFormToRequest: (formData: LoginFormData) => LoginRequestData; -export declare const signupFormToRequest: (formData: SignupFormData) => SignupRequestData; -export declare const passwordResetFormToRequest: (formData: PasswordResetFormData) => PasswordResetData; -export declare const setPasswordFormToRequest: (formData: SetPasswordFormData) => SetPasswordRequestData; -export declare const changePasswordFormToRequest: (formData: ChangePasswordFormData) => ChangePasswordRequestData; -import type { LoginRequestInput, SignupRequestInput, PasswordResetRequestInput, PasswordResetInput, SetPasswordRequestInput, ChangePasswordRequestInput, LinkWhmcsRequestInput } from "../api/requests"; -type LoginRequestData = LoginRequestInput; -type SignupRequestData = SignupRequestInput; -type PasswordResetRequestData = PasswordResetRequestInput; -type PasswordResetData = PasswordResetInput; -type SetPasswordRequestData = SetPasswordRequestInput; -type ChangePasswordRequestData = ChangePasswordRequestInput; -type LinkWhmcsRequestData = LinkWhmcsRequestInput; -export type LoginFormData = LoginRequestInput; -export type SignupFormData = SignupRequestInput; -export type PasswordResetRequestFormData = PasswordResetRequestInput; -export type PasswordResetFormData = PasswordResetInput; -export type SetPasswordFormData = SetPasswordRequestInput; -export type ChangePasswordFormData = ChangePasswordRequestInput; -export type LinkWhmcsFormData = LinkWhmcsRequestInput; -export type { LoginRequestData, SignupRequestData, PasswordResetRequestData, PasswordResetData, SetPasswordRequestData, ChangePasswordRequestData, LinkWhmcsRequestData, }; diff --git a/packages/domain/src/validation/forms/auth.ts b/packages/domain/src/validation/forms/auth.ts deleted file mode 100644 index 5d2c9429..00000000 --- a/packages/domain/src/validation/forms/auth.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Authentication Form Schemas - * Frontend form schemas that extend API request schemas with UI-specific fields - */ - -import { - loginRequestSchema, - signupRequestSchema, - passwordResetRequestSchema, - passwordResetSchema, - setPasswordRequestSchema, - changePasswordRequestSchema, - linkWhmcsRequestSchema, -} from "../api/requests"; - -// ===================================================== -// FORM SCHEMAS (Extend API schemas with UI fields) -// ===================================================== - -export const loginFormSchema = loginRequestSchema; - -export const signupFormSchema = signupRequestSchema; - -export const passwordResetRequestFormSchema = passwordResetRequestSchema; - -export const passwordResetFormSchema = passwordResetSchema; - -export const setPasswordFormSchema = setPasswordRequestSchema; - -export const changePasswordFormSchema = changePasswordRequestSchema; - -export const linkWhmcsFormSchema = linkWhmcsRequestSchema; - -// ===================================================== -// FORM TO API TRANSFORMATIONS -// ===================================================== - -export const loginFormToRequest = (formData: LoginFormData): LoginRequestData => - loginRequestSchema.parse(formData); - -export const signupFormToRequest = (formData: SignupFormData): SignupRequestData => - signupRequestSchema.parse(formData); - -export const passwordResetFormToRequest = (formData: PasswordResetFormData): PasswordResetData => - passwordResetSchema.parse(formData); - -export const setPasswordFormToRequest = (formData: SetPasswordFormData): SetPasswordRequestData => - setPasswordRequestSchema.parse(formData); - -export const changePasswordFormToRequest = ( - formData: ChangePasswordFormData -): ChangePasswordRequestData => changePasswordRequestSchema.parse(formData); - -// ===================================================== -// TYPE EXPORTS -// ===================================================== - -// Import API types -import type { - LoginRequestInput, - SignupRequestInput, - PasswordResetRequestInput, - PasswordResetInput, - SetPasswordRequestInput, - ChangePasswordRequestInput, - LinkWhmcsRequestInput, -} from "../api/requests"; - -type LoginRequestData = LoginRequestInput; -type SignupRequestData = SignupRequestInput; -type PasswordResetRequestData = PasswordResetRequestInput; -type PasswordResetData = PasswordResetInput; -type SetPasswordRequestData = SetPasswordRequestInput; -type ChangePasswordRequestData = ChangePasswordRequestInput; -type LinkWhmcsRequestData = LinkWhmcsRequestInput; - -// Export form types (aliases of API request types) -export type LoginFormData = LoginRequestInput; -export type SignupFormData = SignupRequestInput; -export type PasswordResetRequestFormData = PasswordResetRequestInput; -export type PasswordResetFormData = PasswordResetInput; -export type SetPasswordFormData = SetPasswordRequestInput; -export type ChangePasswordFormData = ChangePasswordRequestInput; -export type LinkWhmcsFormData = LinkWhmcsRequestInput; - -// Re-export API types for convenience -export type { - LoginRequestData, - SignupRequestData, - PasswordResetRequestData, - PasswordResetData, - SetPasswordRequestData, - ChangePasswordRequestData, - LinkWhmcsRequestData, -}; diff --git a/packages/domain/src/validation/forms/profile.d.ts b/packages/domain/src/validation/forms/profile.d.ts index 5ff9ba22..2c9f80ae 100644 --- a/packages/domain/src/validation/forms/profile.d.ts +++ b/packages/domain/src/validation/forms/profile.d.ts @@ -1,17 +1,6 @@ import { z } from "zod"; -export declare const profileEditFormSchema: z.ZodObject<{ - phone: z.ZodOptional; - company: z.ZodOptional; - firstName: z.ZodString; - lastName: z.ZodString; -}, z.core.$strip>; -export declare const profileDisplaySchema: z.ZodObject<{ - phone: z.ZodOptional; - company: z.ZodOptional; - firstName: z.ZodString; - lastName: z.ZodString; - email: z.ZodString; -}, z.core.$strip>; +export declare const profileEditFormSchema: any; +export declare const profileDisplaySchema: any; export declare const addressFormSchema: z.ZodObject<{ street: z.ZodString; streetLine2: z.ZodOptional; @@ -20,22 +9,7 @@ export declare const addressFormSchema: z.ZodObject<{ postalCode: z.ZodString; country: z.ZodString; }, z.core.$strip>; -export declare const contactFormSchema: z.ZodObject<{ - subject: z.ZodString; - message: z.ZodString; - category: z.ZodEnum<{ - billing: "billing"; - technical: "technical"; - account: "account"; - general: "general"; - }>; - priority: z.ZodDefault>>; -}, z.core.$strip>; +export declare const contactFormSchema: any; export declare const profileFormToRequest: (formData: ProfileEditFormData) => UpdateProfileRequestData; export declare const addressFormToRequest: (formData: AddressFormData) => UpdateAddressRequestData; export declare const contactFormToRequest: (formData: ContactFormData) => ContactRequestData; diff --git a/packages/domain/src/validation/index.d.ts b/packages/domain/src/validation/index.d.ts deleted file mode 100644 index 3af7117f..00000000 --- a/packages/domain/src/validation/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export * from "./shared/primitives"; -export * from "./shared/identifiers"; -export * from "./shared/common"; -export * from "./shared/utilities"; -export * from "./shared/entities"; -export * from "./shared/order"; -export type { SubscriptionBillingCycleSchema as SubscriptionBillingCycle } from "./shared/primitives"; -export { loginRequestSchema, signupRequestSchema, passwordResetRequestSchema, passwordResetSchema, setPasswordRequestSchema, changePasswordRequestSchema, linkWhmcsRequestSchema, validateSignupRequestSchema, accountStatusRequestSchema, ssoLinkRequestSchema, checkPasswordNeededRequestSchema, refreshTokenRequestSchema, updateProfileRequestSchema, updateAddressRequestSchema, createOrderRequestSchema, orderConfigurationsSchema, simTopupRequestSchema, simCancelRequestSchema, simChangePlanRequestSchema, simFeaturesRequestSchema, contactRequestSchema, invoiceItemSchema, invoiceSchema, invoiceListSchema, invoiceListQuerySchema, paginationQuerySchema, subscriptionQuerySchema, invoicePaymentLinkSchema, sfOrderIdParamSchema, type LoginRequestInput, type SignupRequestInput, type PasswordResetRequestInput, type PasswordResetInput, type SetPasswordRequestInput, type ChangePasswordRequestInput, type LinkWhmcsRequestInput, type ValidateSignupRequestInput, type AccountStatusRequestInput, type SsoLinkRequestInput, type CheckPasswordNeededRequestInput, type RefreshTokenRequestInput, type UpdateProfileRequest, type UpdateAddressRequest, type CreateOrderRequest, type OrderConfigurations, type SimTopupRequest, type SimCancelRequest, type SimChangePlanRequest, type SimFeaturesRequest, type ContactRequest, type InvoiceListQuery, type PaginationQuery, type SubscriptionQuery, type InvoicePaymentLinkInput, type SfOrderIdParam, } from "./api/requests"; -export { authResponseSchema, type AuthResponse, type AuthTokensSchema } from "./api/responses"; -export { loginFormSchema, signupFormSchema, passwordResetRequestFormSchema, passwordResetFormSchema, setPasswordFormSchema, linkWhmcsFormSchema, type LoginFormData, type SignupFormData, type PasswordResetRequestFormData, type PasswordResetFormData, type SetPasswordFormData, type LinkWhmcsFormData, loginFormToRequest, signupFormToRequest, passwordResetFormToRequest, setPasswordFormToRequest, type LinkWhmcsRequestData, } from "./forms/auth"; -export { profileEditFormSchema, profileDisplaySchema, addressFormSchema, contactFormSchema, type ProfileEditFormData, type ProfileDisplayData, type AddressFormData, type ContactFormData, profileFormToRequest, addressFormToRequest, contactFormToRequest, } from "./forms/profile"; -export { simTypeEnum, activationTypeEnum, mnpGenderEnum, mnpDataSchema, simConfigureFormSchema, simConfigureFormToRequest, type SimType, type ActivationType, type MnpGender, type MnpData, type SimConfigureFormData, } from "./forms/sim-configure"; -export { orderBusinessValidationSchema, skuValidationSchema, userMappingValidationSchema, paymentMethodValidationSchema, type OrderBusinessValidation, type SkuValidation, type UserMappingValidation, type PaymentMethodValidation, } from "./business"; -export { z, parseOrThrow, safeParse } from "./shared/utilities"; diff --git a/packages/domain/src/validation/index.ts b/packages/domain/src/validation/index.ts deleted file mode 100644 index 317a041e..00000000 --- a/packages/domain/src/validation/index.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Validation Module - Unified Architecture - * - * Clean, organized validation system with clear separation of concerns: - * - * 1. shared/* - Modular validation primitives and patterns - * 2. api/requests.ts - Backend API request schemas - * 3. forms/* - Frontend form schemas (extend API schemas with UI fields) - * 4. business/* - Business logic validation rules - */ - -// ===================================================== -// CORE VALIDATION SYSTEM -// ===================================================== - -// Shared validation modules (modular architecture) - MUST BE FIRST -export * from "./shared/primitives"; -export * from "./shared/identifiers"; -export * from "./shared/common"; -export * from "./shared/utilities"; -export * from "./shared/entities"; -export * from "./shared/order"; - -// Export specific billing cycle types for convenience -export type { SubscriptionBillingCycleSchema as SubscriptionBillingCycle } from "./shared/primitives"; - -// API request schemas (backend) - explicit exports for better tree shaking -export { - // Auth API schemas - loginRequestSchema, - signupRequestSchema, - passwordResetRequestSchema, - passwordResetSchema, - setPasswordRequestSchema, - changePasswordRequestSchema, - linkWhmcsRequestSchema, - validateSignupRequestSchema, - accountStatusRequestSchema, - ssoLinkRequestSchema, - checkPasswordNeededRequestSchema, - refreshTokenRequestSchema, - - // User management API schemas - updateProfileRequestSchema, - updateAddressRequestSchema, - - // Order API schemas - createOrderRequestSchema, - orderConfigurationsSchema, - - // Subscription API schemas - simTopupRequestSchema, - simCancelRequestSchema, - simChangePlanRequestSchema, - simFeaturesRequestSchema, - - // Contact & billing schemas - contactRequestSchema, - invoiceItemSchema, - invoiceSchema, - invoiceListSchema, - invoiceListQuerySchema, - paginationQuerySchema, - subscriptionQuerySchema, - invoicePaymentLinkSchema, - sfOrderIdParamSchema, - - // API types - type LoginRequestInput, - type SignupRequestInput, - type PasswordResetRequestInput, - type PasswordResetInput, - type SetPasswordRequestInput, - type ChangePasswordRequestInput, - type LinkWhmcsRequestInput, - type ValidateSignupRequestInput, - type AccountStatusRequestInput, - type SsoLinkRequestInput, - type CheckPasswordNeededRequestInput, - type RefreshTokenRequestInput, - type UpdateProfileRequest, - type UpdateAddressRequest, - type CreateOrderRequest, - type OrderConfigurations, - type SimTopupRequest, - type SimCancelRequest, - type SimChangePlanRequest, - type SimFeaturesRequest, - type ContactRequest, - type InvoiceListQuery, - type PaginationQuery, - type SubscriptionQuery, - type InvoicePaymentLinkInput, - type SfOrderIdParam, -} from "./api/requests"; - -// Form schemas (frontend) - explicit exports for better tree shaking -export { authResponseSchema, type AuthResponse, type AuthTokensSchema } from "./api/responses"; - -export { - // Auth form schemas - loginFormSchema, - signupFormSchema, - passwordResetRequestFormSchema, - passwordResetFormSchema, - setPasswordFormSchema, - linkWhmcsFormSchema, - - // Auth form types - type LoginFormData, - type SignupFormData, - type PasswordResetRequestFormData, - type PasswordResetFormData, - type SetPasswordFormData, - type LinkWhmcsFormData, - - // Auth transformations - loginFormToRequest, - signupFormToRequest, - passwordResetFormToRequest, - setPasswordFormToRequest, - - // Auth API type aliases - type LinkWhmcsRequestData, -} from "./forms/auth"; - -export { - // Profile form schemas - profileEditFormSchema, - profileDisplaySchema, - addressFormSchema, - contactFormSchema, - - // Profile form types - type ProfileEditFormData, - type ProfileDisplayData, - type AddressFormData, - type ContactFormData, - - // Profile transformations - profileFormToRequest, - addressFormToRequest, - contactFormToRequest, -} from "./forms/profile"; - -// SIM configuration forms -export { - simTypeEnum, - activationTypeEnum, - mnpGenderEnum, - mnpDataSchema, - simConfigureFormSchema, - simConfigureFormToRequest, - type SimType, - type ActivationType, - type MnpGender, - type MnpData, - type SimConfigureFormData, -} from "./forms/sim-configure"; - -// Business validation schemas - use schema.safeParse() directly -export { - orderBusinessValidationSchema, - skuValidationSchema, - userMappingValidationSchema, - paymentMethodValidationSchema, - type OrderBusinessValidation, - type SkuValidation, - type UserMappingValidation, - type PaymentMethodValidation, -} from "./business"; - -// Order validation schemas and types are already exported above via "./shared/order" - -// Simple validation utilities (direct Zod usage) -export { z, parseOrThrow, safeParse } from "./shared/utilities"; diff --git a/packages/domain/src/validation/shared/entities.d.ts b/packages/domain/src/validation/shared/entities.d.ts index 2d49b663..cb0ff5c3 100644 --- a/packages/domain/src/validation/shared/entities.d.ts +++ b/packages/domain/src/validation/shared/entities.d.ts @@ -1,250 +1 @@ -import { z } from "zod"; -export declare const paymentMethodTypeSchema: any; -export declare const orderStatusSchema: z.ZodEnum<{ - Pending: "Pending"; - Cancelled: "Cancelled"; - Active: "Active"; - Fraud: "Fraud"; -}>; -export declare const invoiceStatusSchema: z.ZodEnum<{ - Draft: "Draft"; - Pending: "Pending"; - Paid: "Paid"; - Unpaid: "Unpaid"; - Overdue: "Overdue"; - Cancelled: "Cancelled"; - Refunded: "Refunded"; - Collections: "Collections"; -}>; -export declare const subscriptionStatusSchema: any; -export declare const caseStatusSchema: z.ZodEnum<{ - New: "New"; - Working: "Working"; - Escalated: "Escalated"; - Closed: "Closed"; -}>; -export declare const casePrioritySchema: z.ZodEnum<{ - Low: "Low"; - Medium: "Medium"; - High: "High"; - Critical: "Critical"; -}>; -export declare const paymentStatusSchema: z.ZodEnum<{ - pending: "pending"; - completed: "completed"; - failed: "failed"; - processing: "processing"; - cancelled: "cancelled"; - refunded: "refunded"; -}>; -export declare const caseTypeSchema: z.ZodEnum<{ - Question: "Question"; - Problem: "Problem"; - "Feature Request": "Feature Request"; -}>; -export declare const subscriptionCycleSchema: any; -export declare const userSchema: z.ZodObject<{ - id: z.ZodString; - createdAt: z.ZodString; - updatedAt: z.ZodString; - email: z.ZodString; - firstName: z.ZodOptional; - lastName: z.ZodOptional; - company: z.ZodOptional; - phone: z.ZodOptional; - address: z.ZodOptional; - streetLine2: z.ZodNullable; - city: z.ZodNullable; - state: z.ZodNullable; - postalCode: z.ZodNullable; - country: z.ZodNullable; - }, z.core.$strip>>; - mfaEnabled: z.ZodBoolean; - emailVerified: z.ZodBoolean; -}, z.core.$strip>; -export declare const userProfileSchema: z.ZodObject<{ - id: z.ZodString; - createdAt: z.ZodString; - updatedAt: z.ZodString; - email: z.ZodString; - firstName: z.ZodOptional; - lastName: z.ZodOptional; - company: z.ZodOptional; - phone: z.ZodOptional; - address: z.ZodOptional; - streetLine2: z.ZodNullable; - city: z.ZodNullable; - state: z.ZodNullable; - postalCode: z.ZodNullable; - country: z.ZodNullable; - }, z.core.$strip>>; - mfaEnabled: z.ZodBoolean; - emailVerified: z.ZodBoolean; - avatar: z.ZodOptional; - preferences: z.ZodOptional>; - lastLoginAt: z.ZodOptional; - role: z.ZodEnum<{ - USER: "USER"; - ADMIN: "ADMIN"; - }>; -}, z.core.$strip>; -export declare const prismaUserProfileSchema: z.ZodObject<{ - id: z.ZodString; - email: z.ZodString; - firstName: z.ZodNullable; - lastName: z.ZodNullable; - company: z.ZodNullable; - phone: z.ZodNullable; - mfaSecret: z.ZodNullable; - emailVerified: z.ZodBoolean; - createdAt: z.ZodDate; - updatedAt: z.ZodDate; - lastLoginAt: z.ZodNullable; -}, z.core.$strip>; -export declare const mnpDetailsSchema: z.ZodObject<{ - currentProvider: z.ZodString; - phoneNumber: z.ZodString; - accountNumber: z.ZodOptional; - pin: z.ZodOptional; -}, z.core.$strip>; -export declare const orderTotalsSchema: z.ZodObject<{ - monthlyTotal: z.ZodNumber; - oneTimeTotal: z.ZodNumber; -}, z.core.$strip>; -export declare const whmcsOrderItemSchema: z.ZodObject<{ - productId: z.ZodNumber; - productName: z.ZodString; - domain: z.ZodOptional; - cycle: z.ZodString; - quantity: z.ZodNumber; - price: z.ZodNumber; - setup: z.ZodOptional; - configOptions: z.ZodOptional>; -}, z.core.$strip>; -export declare const whmcsOrderSchema: z.ZodObject<{ - id: z.ZodNumber; - orderNumber: z.ZodString; - status: z.ZodEnum<{ - Pending: "Pending"; - Cancelled: "Cancelled"; - Active: "Active"; - Fraud: "Fraud"; - }>; - date: z.ZodString; - amount: z.ZodNumber; - currency: z.ZodString; - paymentMethod: z.ZodOptional; - items: z.ZodArray; - cycle: z.ZodString; - quantity: z.ZodNumber; - price: z.ZodNumber; - setup: z.ZodOptional; - configOptions: z.ZodOptional>; - }, z.core.$strip>>; - invoiceId: z.ZodOptional; -}, z.core.$strip>; -export declare const invoiceItemSchema: any; -export declare const invoiceSchema: any; -export declare const invoiceListSchema: any; -export declare const subscriptionSchema: any; -export declare const subscriptionListSchema: any; -export declare const paymentMethodSchema: any; -export declare const paymentGatewaySchema: any; -export declare const paymentSchema: z.ZodObject<{ - id: z.core.$ZodBranded; - userId: z.core.$ZodBranded; - invoiceId: z.ZodOptional>; - subscriptionId: z.ZodOptional>; - amount: z.ZodNumber; - currency: z.ZodOptional; - status: z.ZodEnum<{ - pending: "pending"; - completed: "completed"; - failed: "failed"; - processing: "processing"; - cancelled: "cancelled"; - refunded: "refunded"; - }>; - transactionId: z.ZodOptional; - failureReason: z.ZodOptional; - processedAt: z.ZodOptional; - createdAt: z.ZodString; - updatedAt: z.ZodString; -}, z.core.$strip>; -export declare const caseCommentSchema: z.ZodObject<{ - id: z.ZodString; - body: z.ZodString; - isPublic: z.ZodBoolean; - createdDate: z.ZodString; - createdBy: z.ZodObject<{ - id: z.ZodString; - name: z.ZodString; - type: z.ZodEnum<{ - user: "user"; - customer: "customer"; - }>; - }, z.core.$strip>; -}, z.core.$strip>; -export declare const supportCaseSchema: z.ZodObject<{ - id: z.ZodString; - createdDate: z.ZodString; - lastModifiedDate: z.ZodString; - number: z.ZodString; - subject: z.ZodString; - description: z.ZodOptional; - status: z.ZodEnum<{ - New: "New"; - Working: "Working"; - Escalated: "Escalated"; - Closed: "Closed"; - }>; - priority: z.ZodEnum<{ - Low: "Low"; - Medium: "Medium"; - High: "High"; - Critical: "Critical"; - }>; - type: z.ZodEnum<{ - Question: "Question"; - Problem: "Problem"; - "Feature Request": "Feature Request"; - }>; - closedDate: z.ZodOptional; - contactId: z.ZodOptional; - accountId: z.ZodOptional; - ownerId: z.ZodOptional; - ownerName: z.ZodOptional; - comments: z.ZodOptional; - }, z.core.$strip>; - }, z.core.$strip>>>; -}, z.core.$strip>; -export type UserSchema = z.infer; -export type UserProfileSchema = z.infer; -export type MnpDetailsSchema = z.infer; -export type OrderTotalsSchema = z.infer; -export type WhmcsOrderItemSchema = z.infer; -export type WhmcsOrderSchema = z.infer; -export type InvoiceItemSchema = z.infer; -export type InvoiceSchema = z.infer; -export type InvoiceListSchema = z.infer; -export type SubscriptionSchema = z.infer; -export type PaymentMethodSchema = z.infer; -export type PaymentSchema = z.infer; -export type CaseCommentSchema = z.infer; -export type SupportCaseSchema = z.infer; +export {}; diff --git a/packages/domain/src/validation/shared/entities.js b/packages/domain/src/validation/shared/entities.js index f393720b..e30a8d69 100644 --- a/packages/domain/src/validation/shared/entities.js +++ b/packages/domain/src/validation/shared/entities.js @@ -1,141 +1,3 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.supportCaseSchema = exports.caseCommentSchema = exports.paymentSchema = exports.paymentGatewaySchema = exports.paymentMethodSchema = exports.subscriptionListSchema = exports.subscriptionSchema = exports.invoiceListSchema = exports.invoiceSchema = exports.invoiceItemSchema = exports.whmcsOrderSchema = exports.whmcsOrderItemSchema = exports.orderTotalsSchema = exports.mnpDetailsSchema = exports.prismaUserProfileSchema = exports.userProfileSchema = exports.userSchema = exports.subscriptionCycleSchema = exports.caseTypeSchema = exports.paymentStatusSchema = exports.casePrioritySchema = exports.caseStatusSchema = exports.subscriptionStatusSchema = exports.invoiceStatusSchema = exports.orderStatusSchema = exports.paymentMethodTypeSchema = void 0; -const zod_1 = require("zod"); -const invoice_schema_1 = require("@customer-portal/schemas/billing/invoice.schema"); -const subscription_schema_1 = require("@customer-portal/schemas/subscriptions/subscription.schema"); -const payment_schema_1 = require("@customer-portal/schemas/payments/payment.schema"); -const primitives_1 = require("./primitives"); -const identifiers_1 = require("./identifiers"); -const common_1 = require("./common"); -const status_1 = require("../../enums/status"); -const tupleFromEnum = (enumObject) => { - const values = Object.values(enumObject); - if (values.length === 0) { - throw new Error("Enum must have at least one value"); - } - return [...new Set(values)]; -}; -const addressRecordSchema = zod_1.z.object({ - street: zod_1.z.string().nullable(), - streetLine2: zod_1.z.string().nullable(), - city: zod_1.z.string().nullable(), - state: zod_1.z.string().nullable(), - postalCode: zod_1.z.string().nullable(), - country: zod_1.z.string().nullable(), -}); -exports.paymentMethodTypeSchema = payment_schema_1.paymentMethodTypeSchema; -exports.orderStatusSchema = zod_1.z.enum(["Pending", "Active", "Cancelled", "Fraud"]); -exports.invoiceStatusSchema = zod_1.z.enum(tupleFromEnum(status_1.INVOICE_STATUS)); -exports.subscriptionStatusSchema = subscription_schema_1.subscriptionStatusSchema; -exports.caseStatusSchema = zod_1.z.enum(tupleFromEnum(status_1.CASE_STATUS)); -exports.casePrioritySchema = zod_1.z.enum(tupleFromEnum(status_1.CASE_PRIORITY)); -exports.paymentStatusSchema = zod_1.z.enum(tupleFromEnum(status_1.PAYMENT_STATUS)); -exports.caseTypeSchema = zod_1.z.enum(["Question", "Problem", "Feature Request"]); -exports.subscriptionCycleSchema = subscription_schema_1.subscriptionCycleSchema; -exports.userSchema = common_1.baseEntitySchema.extend({ - email: primitives_1.emailSchema, - firstName: primitives_1.nameSchema.optional(), - lastName: primitives_1.nameSchema.optional(), - company: zod_1.z.string().optional(), - phone: primitives_1.phoneSchema.optional(), - address: addressRecordSchema.optional(), - mfaEnabled: zod_1.z.boolean(), - emailVerified: zod_1.z.boolean(), -}); -exports.userProfileSchema = exports.userSchema.extend({ - avatar: zod_1.z.string().optional(), - preferences: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(), - lastLoginAt: primitives_1.timestampSchema.optional(), - role: zod_1.z.enum(["USER", "ADMIN"]), -}); -exports.prismaUserProfileSchema = zod_1.z.object({ - id: zod_1.z.string().uuid(), - email: primitives_1.emailSchema, - firstName: zod_1.z.string().nullable(), - lastName: zod_1.z.string().nullable(), - company: zod_1.z.string().nullable(), - phone: zod_1.z.string().nullable(), - mfaSecret: zod_1.z.string().nullable(), - emailVerified: zod_1.z.boolean(), - createdAt: zod_1.z.date(), - updatedAt: zod_1.z.date(), - lastLoginAt: zod_1.z.date().nullable(), -}); -exports.mnpDetailsSchema = zod_1.z.object({ - currentProvider: zod_1.z.string().min(1, "Current provider is required"), - phoneNumber: primitives_1.phoneSchema, - accountNumber: zod_1.z.string().min(1, "Account number is required").optional(), - pin: zod_1.z.string().min(1, "PIN is required").optional(), -}); -exports.orderTotalsSchema = zod_1.z.object({ - monthlyTotal: zod_1.z.number().nonnegative("Monthly total must be non-negative"), - oneTimeTotal: zod_1.z.number().nonnegative("One-time total must be non-negative"), -}); -exports.whmcsOrderItemSchema = zod_1.z.object({ - productId: zod_1.z.number().int().positive("Product id must be positive"), - productName: zod_1.z.string().min(1, "Product name is required"), - domain: zod_1.z.string().optional(), - cycle: zod_1.z.string().min(1, "Billing cycle is required"), - quantity: zod_1.z.number().int().positive("Quantity must be positive"), - price: zod_1.z.number(), - setup: zod_1.z.number().optional(), - configOptions: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(), -}); -exports.whmcsOrderSchema = common_1.whmcsEntitySchema.extend({ - orderNumber: zod_1.z.string().min(1, "Order number is required"), - status: exports.orderStatusSchema, - date: zod_1.z.string().min(1, "Order date is required"), - amount: zod_1.z.number(), - currency: zod_1.z.string().min(1, "Currency is required"), - paymentMethod: zod_1.z.string().optional(), - items: zod_1.z.array(exports.whmcsOrderItemSchema), - invoiceId: zod_1.z.number().int().positive().optional(), -}); -exports.invoiceItemSchema = invoice_schema_1.invoiceItemSchema; -exports.invoiceSchema = invoice_schema_1.invoiceSchema; -exports.invoiceListSchema = invoice_schema_1.invoiceListSchema; -exports.subscriptionSchema = subscription_schema_1.subscriptionSchema; -exports.subscriptionListSchema = subscription_schema_1.subscriptionListSchema; -exports.paymentMethodSchema = payment_schema_1.paymentMethodSchema; -exports.paymentGatewaySchema = payment_schema_1.paymentGatewaySchema; -exports.paymentSchema = zod_1.z.object({ - id: identifiers_1.paymentIdSchema, - userId: identifiers_1.userIdSchema, - invoiceId: identifiers_1.invoiceIdSchema.optional(), - subscriptionId: identifiers_1.subscriptionIdSchema.optional(), - amount: primitives_1.moneyAmountSchema, - currency: zod_1.z.string().length(3, "Currency must be a 3-letter ISO code").optional(), - status: exports.paymentStatusSchema, - transactionId: zod_1.z.string().optional(), - failureReason: zod_1.z.string().optional(), - processedAt: primitives_1.timestampSchema.optional(), - createdAt: primitives_1.timestampSchema, - updatedAt: primitives_1.timestampSchema, -}); -exports.caseCommentSchema = zod_1.z.object({ - id: zod_1.z.string().min(1, "Comment id is required"), - body: zod_1.z.string().min(1, "Comment body is required"), - isPublic: zod_1.z.boolean(), - createdDate: primitives_1.timestampSchema, - createdBy: zod_1.z.object({ - id: zod_1.z.string().min(1, "Created by id is required"), - name: zod_1.z.string().min(1, "Created by name is required"), - type: zod_1.z.enum(["user", "customer"]), - }), -}); -exports.supportCaseSchema = common_1.salesforceEntitySchema.extend({ - number: zod_1.z.string().min(1, "Case number is required"), - subject: zod_1.z.string().min(1, "Subject is required"), - description: zod_1.z.string().optional(), - status: exports.caseStatusSchema, - priority: exports.casePrioritySchema, - type: exports.caseTypeSchema, - closedDate: primitives_1.timestampSchema.optional(), - contactId: zod_1.z.string().optional(), - accountId: zod_1.z.string().optional(), - ownerId: zod_1.z.string().optional(), - ownerName: zod_1.z.string().optional(), - comments: zod_1.z.array(exports.caseCommentSchema).optional(), -}); //# sourceMappingURL=entities.js.map \ No newline at end of file diff --git a/packages/domain/src/validation/shared/entities.js.map b/packages/domain/src/validation/shared/entities.js.map index 11417909..fa764abe 100644 --- a/packages/domain/src/validation/shared/entities.js.map +++ b/packages/domain/src/validation/shared/entities.js.map @@ -1 +1 @@ -{"version":3,"file":"entities.js","sourceRoot":"","sources":["entities.ts"],"names":[],"mappings":";;;AAKA,6BAAwB;AAExB,oFAIyD;AACzD,oGAKoE;AACpE,qFAI0D;AAC1D,6CAMsB;AACtB,+CAKuB;AACvB,qCAAuF;AACvF,+CAM4B;AAM5B,MAAM,aAAa,GAAG,CAAmC,UAAa,EAAE,EAAE;IACxE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAuC,CAAC;AACpE,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEU,QAAA,uBAAuB,GAAG,wCAA6B,CAAC;AAExD,QAAA,iBAAiB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;AACxE,QAAA,mBAAmB,GAAG,OAAC,CAAC,IAAI,CAAC,aAAa,CAAC,uBAAc,CAAC,CAAC,CAAC;AAC5D,QAAA,wBAAwB,GAAG,8CAA8B,CAAC;AAC1D,QAAA,gBAAgB,GAAG,OAAC,CAAC,IAAI,CAAC,aAAa,CAAC,oBAAW,CAAC,CAAC,CAAC;AACtD,QAAA,kBAAkB,GAAG,OAAC,CAAC,IAAI,CAAC,aAAa,CAAC,sBAAa,CAAC,CAAC,CAAC;AAC1D,QAAA,mBAAmB,GAAG,OAAC,CAAC,IAAI,CAAC,aAAa,CAAC,uBAAc,CAAC,CAAC,CAAC;AAC5D,QAAA,cAAc,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAEpE,QAAA,uBAAuB,GAAG,6CAA6B,CAAC;AAMxD,QAAA,UAAU,GAAG,yBAAgB,CAAC,MAAM,CAAC;IAChD,KAAK,EAAE,wBAAW;IAClB,SAAS,EAAE,uBAAU,CAAC,QAAQ,EAAE;IAChC,QAAQ,EAAE,uBAAU,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,KAAK,EAAE,wBAAW,CAAC,QAAQ,EAAE;IAC7B,OAAO,EAAE,mBAAmB,CAAC,QAAQ,EAAE;IACvC,UAAU,EAAE,OAAC,CAAC,OAAO,EAAE;IACvB,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE;CAC3B,CAAC,CAAC;AAEU,QAAA,iBAAiB,GAAG,kBAAU,CAAC,MAAM,CAAC;IACjD,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,WAAW,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzD,WAAW,EAAE,4BAAe,CAAC,QAAQ,EAAE;IACvC,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC,CAAC;AAEU,QAAA,uBAAuB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC9C,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;IACrB,KAAK,EAAE,wBAAW;IAClB,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE;IAC1B,SAAS,EAAE,OAAC,CAAC,IAAI,EAAE;IACnB,SAAS,EAAE,OAAC,CAAC,IAAI,EAAE;IACnB,WAAW,EAAE,OAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAEU,QAAA,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IACvC,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,8BAA8B,CAAC;IAClE,WAAW,EAAE,wBAAW;IACxB,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC,QAAQ,EAAE;IACzE,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC;AAMU,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,oCAAoC,CAAC;IAC1E,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,qCAAqC,CAAC;CAC5E,CAAC,CAAC;AAEU,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IACnE,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,0BAA0B,CAAC;IAC1D,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,2BAA2B,CAAC;IACrD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;IAChE,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,aAAa,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC3D,CAAC,CAAC;AAEU,QAAA,gBAAgB,GAAG,0BAAiB,CAAC,MAAM,CAAC;IACvD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,0BAA0B,CAAC;IAC1D,MAAM,EAAE,yBAAiB;IACzB,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;IACjD,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;IAClB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC;IACnD,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,4BAAoB,CAAC;IACpC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAClD,CAAC,CAAC;AAMU,QAAA,iBAAiB,GAAG,kCAAwB,CAAC;AAE7C,QAAA,aAAa,GAAG,8BAAoB,CAAC;AAErC,QAAA,iBAAiB,GAAG,kCAAwB,CAAC;AAM7C,QAAA,kBAAkB,GAAG,wCAAwB,CAAC;AAC9C,QAAA,sBAAsB,GAAG,4CAA4B,CAAC;AAMtD,QAAA,mBAAmB,GAAG,oCAAyB,CAAC;AAChD,QAAA,oBAAoB,GAAG,qCAA0B,CAAC;AAElD,QAAA,aAAa,GAAG,OAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,6BAAe;IACnB,MAAM,EAAE,0BAAY;IACpB,SAAS,EAAE,6BAAe,CAAC,QAAQ,EAAE;IACrC,cAAc,EAAE,kCAAoB,CAAC,QAAQ,EAAE;IAC/C,MAAM,EAAE,8BAAiB;IACzB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,sCAAsC,CAAC,CAAC,QAAQ,EAAE;IACjF,MAAM,EAAE,2BAAmB;IAC3B,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,WAAW,EAAE,4BAAe,CAAC,QAAQ,EAAE;IACvC,SAAS,EAAE,4BAAe;IAC1B,SAAS,EAAE,4BAAe;CAC3B,CAAC,CAAC;AAMU,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;IAC/C,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,0BAA0B,CAAC;IACnD,QAAQ,EAAE,OAAC,CAAC,OAAO,EAAE;IACrB,WAAW,EAAE,4BAAe;IAC5B,SAAS,EAAE,OAAC,CAAC,MAAM,CAAC;QAClB,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,2BAA2B,CAAC;QAClD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,6BAA6B,CAAC;QACtD,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;KACnC,CAAC;CACH,CAAC,CAAC;AAEU,QAAA,iBAAiB,GAAG,+BAAsB,CAAC,MAAM,CAAC;IAC7D,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,yBAAyB,CAAC;IACpD,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,qBAAqB,CAAC;IACjD,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,MAAM,EAAE,wBAAgB;IACxB,QAAQ,EAAE,0BAAkB;IAC5B,IAAI,EAAE,sBAAc;IACpB,UAAU,EAAE,4BAAe,CAAC,QAAQ,EAAE;IACtC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,yBAAiB,CAAC,CAAC,QAAQ,EAAE;CAChD,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"entities.js","sourceRoot":"","sources":["entities.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/domain/src/validation/shared/entities.ts b/packages/domain/src/validation/shared/entities.ts deleted file mode 100644 index 4a4c1385..00000000 --- a/packages/domain/src/validation/shared/entities.ts +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Entity Schemas - * Business entity validation schemas aligned with documented domain entities. - */ - -import { z } from "zod"; - -import { - invoiceItemSchema as billingInvoiceItemSchema, - invoiceListSchema as billingInvoiceListSchema, - invoiceSchema as billingInvoiceSchema, -} from "@customer-portal/schemas/billing/invoice.schema"; -import { - subscriptionCycleSchema as sharedSubscriptionCycleSchema, - subscriptionListSchema as sharedSubscriptionListSchema, - subscriptionSchema as sharedSubscriptionSchema, - subscriptionStatusSchema as sharedSubscriptionStatusSchema, -} from "@customer-portal/schemas/subscriptions/subscription.schema"; -import { - paymentGatewaySchema as sharedPaymentGatewaySchema, - paymentMethodSchema as sharedPaymentMethodSchema, - paymentMethodTypeSchema as sharedPaymentMethodTypeSchema, -} from "@customer-portal/schemas/payments/payment.schema"; -import { - emailSchema, - nameSchema, - phoneSchema, - moneyAmountSchema, - timestampSchema, -} from "./primitives"; -import { - userIdSchema, - invoiceIdSchema, - subscriptionIdSchema, - paymentIdSchema, -} from "./identifiers"; -import { baseEntitySchema, whmcsEntitySchema, salesforceEntitySchema } from "./common"; -import { - INVOICE_STATUS, - SUBSCRIPTION_STATUS, - CASE_STATUS, - CASE_PRIORITY, - PAYMENT_STATUS, -} from "../../enums/status"; - -// ===================================================== -// HELPERS -// ===================================================== - -const tupleFromEnum = >(enumObject: T) => { - const values = Object.values(enumObject); - if (values.length === 0) { - throw new Error("Enum must have at least one value"); - } - return [...new Set(values)] as [T[keyof T], ...Array]; -}; - -const addressRecordSchema = z.object({ - street: z.string().nullable(), - streetLine2: z.string().nullable(), - city: z.string().nullable(), - state: z.string().nullable(), - postalCode: z.string().nullable(), - country: z.string().nullable(), -}); - -export const paymentMethodTypeSchema = sharedPaymentMethodTypeSchema; - -export const orderStatusSchema = z.enum(["Pending", "Active", "Cancelled", "Fraud"]); -export const invoiceStatusSchema = z.enum(tupleFromEnum(INVOICE_STATUS)); -export const subscriptionStatusSchema = sharedSubscriptionStatusSchema; -export const caseStatusSchema = z.enum(tupleFromEnum(CASE_STATUS)); -export const casePrioritySchema = z.enum(tupleFromEnum(CASE_PRIORITY)); -export const paymentStatusSchema = z.enum(tupleFromEnum(PAYMENT_STATUS)); -export const caseTypeSchema = z.enum(["Question", "Problem", "Feature Request"]); - -export const subscriptionCycleSchema = sharedSubscriptionCycleSchema; - -// ===================================================== -// USER ENTITIES -// ===================================================== - -export const userSchema = baseEntitySchema.extend({ - email: emailSchema, - firstName: nameSchema.optional(), - lastName: nameSchema.optional(), - company: z.string().optional(), - phone: phoneSchema.optional(), - address: addressRecordSchema.optional(), - mfaEnabled: z.boolean(), - emailVerified: z.boolean(), -}); - -export const userProfileSchema = userSchema.extend({ - avatar: z.string().optional(), - preferences: z.record(z.string(), z.unknown()).optional(), - lastLoginAt: timestampSchema.optional(), - role: z.enum(["USER", "ADMIN"]), -}); - -export const prismaUserProfileSchema = z.object({ - id: z.string().uuid(), - email: emailSchema, - firstName: z.string().nullable(), - lastName: z.string().nullable(), - company: z.string().nullable(), - phone: z.string().nullable(), - mfaSecret: z.string().nullable(), - emailVerified: z.boolean(), - createdAt: z.date(), - updatedAt: z.date(), - lastLoginAt: z.date().nullable(), -}); - -export const mnpDetailsSchema = z.object({ - currentProvider: z.string().min(1, "Current provider is required"), - phoneNumber: phoneSchema, - accountNumber: z.string().min(1, "Account number is required").optional(), - pin: z.string().min(1, "PIN is required").optional(), -}); - -// ===================================================== -// ORDER ENTITIES (WHMCS) -// ===================================================== - -export const orderTotalsSchema = z.object({ - monthlyTotal: z.number().nonnegative("Monthly total must be non-negative"), - oneTimeTotal: z.number().nonnegative("One-time total must be non-negative"), -}); - -export const whmcsOrderItemSchema = z.object({ - productId: z.number().int().positive("Product id must be positive"), - productName: z.string().min(1, "Product name is required"), - domain: z.string().optional(), - cycle: z.string().min(1, "Billing cycle is required"), - quantity: z.number().int().positive("Quantity must be positive"), - price: z.number(), - setup: z.number().optional(), - configOptions: z.record(z.string(), z.string()).optional(), -}); - -export const whmcsOrderSchema = whmcsEntitySchema.extend({ - orderNumber: z.string().min(1, "Order number is required"), - status: orderStatusSchema, - date: z.string().min(1, "Order date is required"), - amount: z.number(), - currency: z.string().min(1, "Currency is required"), - paymentMethod: z.string().optional(), - items: z.array(whmcsOrderItemSchema), - invoiceId: z.number().int().positive().optional(), -}); - -// ===================================================== -// INVOICE ENTITIES (WHMCS) -// ===================================================== - -export const invoiceItemSchema = billingInvoiceItemSchema; - -export const invoiceSchema = billingInvoiceSchema; - -export const invoiceListSchema = billingInvoiceListSchema; - -// ===================================================== -// SUBSCRIPTION ENTITIES (WHMCS) -// ===================================================== - -export const subscriptionSchema = sharedSubscriptionSchema; -export const subscriptionListSchema = sharedSubscriptionListSchema; - -// ===================================================== -// PAYMENT ENTITIES (WHMCS & PORTAL) -// ===================================================== - -export const paymentMethodSchema = sharedPaymentMethodSchema; -export const paymentGatewaySchema = sharedPaymentGatewaySchema; - -export const paymentSchema = z.object({ - id: paymentIdSchema, - userId: userIdSchema, - invoiceId: invoiceIdSchema.optional(), - subscriptionId: subscriptionIdSchema.optional(), - amount: moneyAmountSchema, - currency: z.string().length(3, "Currency must be a 3-letter ISO code").optional(), - status: paymentStatusSchema, - transactionId: z.string().optional(), - failureReason: z.string().optional(), - processedAt: timestampSchema.optional(), - createdAt: timestampSchema, - updatedAt: timestampSchema, -}); - -// ===================================================== -// SUPPORT CASE ENTITIES (SALESFORCE) -// ===================================================== - -export const caseCommentSchema = z.object({ - id: z.string().min(1, "Comment id is required"), - body: z.string().min(1, "Comment body is required"), - isPublic: z.boolean(), - createdDate: timestampSchema, - createdBy: z.object({ - id: z.string().min(1, "Created by id is required"), - name: z.string().min(1, "Created by name is required"), - type: z.enum(["user", "customer"]), - }), -}); - -export const supportCaseSchema = salesforceEntitySchema.extend({ - number: z.string().min(1, "Case number is required"), - subject: z.string().min(1, "Subject is required"), - description: z.string().optional(), - status: caseStatusSchema, - priority: casePrioritySchema, - type: caseTypeSchema, - closedDate: timestampSchema.optional(), - contactId: z.string().optional(), - accountId: z.string().optional(), - ownerId: z.string().optional(), - ownerName: z.string().optional(), - comments: z.array(caseCommentSchema).optional(), -}); - -// ===================================================== -// TYPE EXPORTS (Schema-backed) -// ===================================================== - -export type UserSchema = z.infer; -export type UserProfileSchema = z.infer; -export type MnpDetailsSchema = z.infer; -export type OrderTotalsSchema = z.infer; -export type WhmcsOrderItemSchema = z.infer; -export type WhmcsOrderSchema = z.infer; -export type InvoiceItemSchema = z.infer; -export type InvoiceSchema = z.infer; -export type InvoiceListSchema = z.infer; -export type SubscriptionSchema = z.infer; -export type PaymentMethodSchema = z.infer; -export type PaymentSchema = z.infer; -export type CaseCommentSchema = z.infer; -export type SupportCaseSchema = z.infer; diff --git a/packages/domain/src/validation/shared/primitives.d.ts b/packages/domain/src/validation/shared/primitives.d.ts index 4d0f382c..1e75df9f 100644 --- a/packages/domain/src/validation/shared/primitives.d.ts +++ b/packages/domain/src/validation/shared/primitives.d.ts @@ -44,26 +44,26 @@ export declare const priorityEnum: z.ZodEnum<{ }>; export declare const categoryEnum: z.ZodEnum<{ billing: "billing"; - technical: "technical"; account: "account"; + technical: "technical"; general: "general"; }>; export declare const billingCycleEnum: z.ZodEnum<{ Monthly: "Monthly"; Quarterly: "Quarterly"; Annually: "Annually"; - Onetime: "Onetime"; Free: "Free"; + Onetime: "Onetime"; }>; export declare const subscriptionBillingCycleEnum: z.ZodEnum<{ - "One-time": "One-time"; Monthly: "Monthly"; Quarterly: "Quarterly"; - Annually: "Annually"; - Free: "Free"; "Semi-Annually": "Semi-Annually"; + Annually: "Annually"; Biennially: "Biennially"; Triennially: "Triennially"; + "One-time": "One-time"; + Free: "Free"; }>; export type EmailSchema = z.infer; export type PasswordSchema = z.infer; diff --git a/packages/domain/tsconfig.json b/packages/domain/tsconfig.json index f2703120..b2889149 100644 --- a/packages/domain/tsconfig.json +++ b/packages/domain/tsconfig.json @@ -23,6 +23,7 @@ "orders/**/*", "catalog/**/*", "common/**/*", + "auth/**/*", "toolkit/**/*", "index.ts" ], diff --git a/packages/integrations/whmcs/src/mappers/order.mapper.ts b/packages/integrations/whmcs/src/mappers/order.mapper.ts index 32185814..28efc89b 100644 --- a/packages/integrations/whmcs/src/mappers/order.mapper.ts +++ b/packages/integrations/whmcs/src/mappers/order.mapper.ts @@ -1,10 +1,14 @@ -import type { FulfillmentOrderItem } from "@customer-portal/contracts/orders"; +import type { FulfillmentOrderItem } from "@customer-portal/domain/orders"; +import { + Providers, + type OrderItemSummary, +} from "@customer-portal/domain/orders"; import { type WhmcsOrderItem, type WhmcsAddOrderParams, type WhmcsAddOrderPayload, whmcsOrderItemSchema, -} from "@customer-portal/schemas/integrations/whmcs/order.schema"; +} from "@customer-portal/domain/orders/providers/whmcs"; import { z } from "zod"; const fulfillmentOrderItemSchema = z.object({ diff --git a/packages/integrations/whmcs/src/mappers/payment.mapper.ts b/packages/integrations/whmcs/src/mappers/payment.mapper.ts index 8630ab6f..f6f9267c 100644 --- a/packages/integrations/whmcs/src/mappers/payment.mapper.ts +++ b/packages/integrations/whmcs/src/mappers/payment.mapper.ts @@ -1,12 +1,13 @@ -import type { PaymentGateway, PaymentMethod } from "@customer-portal/contracts/payments"; +import type { PaymentGateway, PaymentMethod } from "@customer-portal/domain/payments"; import { paymentGatewaySchema, paymentMethodSchema, -} from "@customer-portal/schemas/payments/payment.schema"; + Providers, +} from "@customer-portal/domain/payments"; import { - whmcsPaymentGatewaySchema, - whmcsPaymentMethodSchema, -} from "@customer-portal/schemas/integrations/whmcs/payment.schema"; + whmcsPaymentGatewayRawSchema, + whmcsPaymentMethodRawSchema, +} from "@customer-portal/domain/payments/providers/whmcs"; const paymentMethodTypeMap: Record = { creditcard: "CreditCard", @@ -40,7 +41,7 @@ function mapPaymentGatewayType(type?: string | null): PaymentGateway["type"] { } export function transformWhmcsPaymentMethod(raw: unknown): PaymentMethod { - const method = whmcsPaymentMethodSchema.parse(raw); + const method = whmcsPaymentMethodRawSchema.parse(raw); const paymentMethod: PaymentMethod = { id: method.id, @@ -69,7 +70,7 @@ export function transformWhmcsPaymentMethod(raw: unknown): PaymentMethod { } export function transformWhmcsPaymentGateway(raw: unknown): PaymentGateway { - const gateway = whmcsPaymentGatewaySchema.parse(raw); + const gateway = whmcsPaymentGatewayRawSchema.parse(raw); const paymentGateway: PaymentGateway = { name: gateway.name, diff --git a/packages/schemas/src/integrations/whmcs/order.schema.ts b/packages/schemas/src/integrations/whmcs/order.schema.ts deleted file mode 100644 index bfee3e98..00000000 --- a/packages/schemas/src/integrations/whmcs/order.schema.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { z } from "zod"; - -/** - * WHMCS Order Item Schema - * Represents a single product line item in a WHMCS order - */ -export const whmcsOrderItemSchema = z.object({ - productId: z.string().min(1, "Product ID is required"), // WHMCS Product ID - billingCycle: z.enum(["monthly", "quarterly", "semiannually", "annually", "biennially", "triennially", "onetime", "free"]), - quantity: z.number().int().positive("Quantity must be positive").default(1), - configOptions: z.record(z.string(), z.string()).optional(), - customFields: z.record(z.string(), z.string()).optional(), -}); - -export type WhmcsOrderItem = z.infer; - -/** - * WHMCS AddOrder API Parameters Schema - * Based on official WHMCS API documentation - * https://developers.whmcs.com/api-reference/addorder/ - */ -export const whmcsAddOrderParamsSchema = z.object({ - clientId: z.number().int().positive("Client ID must be positive"), - items: z.array(whmcsOrderItemSchema).min(1, "At least one item is required"), - paymentMethod: z.string().min(1, "Payment method is required"), // Required by WHMCS API - promoCode: z.string().optional(), - notes: z.string().optional(), - sfOrderId: z.string().optional(), // For tracking back to Salesforce - noinvoice: z.boolean().optional(), // Don't create invoice during provisioning - noinvoiceemail: z.boolean().optional(), // Suppress invoice email (if invoice is created) - noemail: z.boolean().optional(), // Don't send any emails -}); - -export type WhmcsAddOrderParams = z.infer; - -/** - * WHMCS AddOrder API Payload Schema - * The actual payload sent to WHMCS API with array format for items - */ -export const whmcsAddOrderPayloadSchema = z.object({ - clientid: z.number().int().positive(), - paymentmethod: z.string().min(1), - promocode: z.string().optional(), - noinvoice: z.boolean().optional(), - noinvoiceemail: z.boolean().optional(), - noemail: z.boolean().optional(), - pid: z.array(z.string()).min(1), - billingcycle: z.array(z.string()).min(1), - qty: z.array(z.number().int().positive()).min(1), - configoptions: z.array(z.string()).optional(), // base64 encoded serialized arrays - customfields: z.array(z.string()).optional(), // base64 encoded serialized arrays -}); - -export type WhmcsAddOrderPayload = z.infer; - -/** - * WHMCS Order Result Schema - */ -export const whmcsOrderResultSchema = z.object({ - orderId: z.number().int().positive(), - invoiceId: z.number().int().positive().optional(), - serviceIds: z.array(z.number().int().positive()).default([]), -}); - -export type WhmcsOrderResult = z.infer; - -/** - * WHMCS AcceptOrder API Response Schema - */ -export const whmcsAcceptOrderResponseSchema = z.object({ - result: z.string(), - orderid: z.number().int().positive(), - invoiceid: z.number().int().positive().optional(), - productids: z.string().optional(), // Comma-separated service IDs -}); - -export type WhmcsAcceptOrderResponse = z.infer; - diff --git a/packages/schemas/src/integrations/whmcs/payment.schema.ts b/packages/schemas/src/integrations/whmcs/payment.schema.ts deleted file mode 100644 index cfa0ff28..00000000 --- a/packages/schemas/src/integrations/whmcs/payment.schema.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { z } from "zod"; - -export const whmcsPaymentMethodSchema = z.object({ - id: z.number(), - type: z.string(), - description: z.string().optional(), - gateway_name: z.string().optional(), - contact_type: z.string().optional(), - contact_id: z.number().optional(), - card_last_four: z.string().optional(), - expiry_date: z.string().optional(), - start_date: z.string().optional(), - issue_number: z.string().optional(), - card_type: z.string().optional(), - remote_token: z.string().optional(), - last_updated: z.string().optional(), - bank_name: z.string().optional(), - is_default: z.boolean().optional(), -}); - -export const whmcsPaymentGatewaySchema = z.object({ - name: z.string(), - display_name: z.string().optional(), - type: z.string().optional(), - active: z.boolean().optional(), -}); - -export type WhmcsPaymentMethodSchema = typeof whmcsPaymentMethodSchema; -export type WhmcsPaymentGatewaySchema = typeof whmcsPaymentGatewaySchema;