214 lines
7.5 KiB
TypeScript
214 lines
7.5 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import type { Customer, CustomerAddress, CustomerStats } from "../../contract";
|
|
import {
|
|
customerAddressSchema,
|
|
customerEmailPreferencesSchema,
|
|
customerSchema,
|
|
customerStatsSchema,
|
|
customerUserSchema,
|
|
} from "../../schema";
|
|
import {
|
|
whmcsClientSchema,
|
|
whmcsClientResponseSchema,
|
|
whmcsClientStatsSchema,
|
|
type WhmcsClient,
|
|
type WhmcsClientResponse,
|
|
type WhmcsClientStats,
|
|
whmcsCustomFieldSchema,
|
|
whmcsUserSchema,
|
|
} from "./raw.types";
|
|
|
|
const toBoolean = (value: unknown): boolean | undefined => {
|
|
if (value === undefined || value === null) {
|
|
return undefined;
|
|
}
|
|
if (typeof value === "boolean") {
|
|
return value;
|
|
}
|
|
if (typeof value === "number") {
|
|
return value === 1;
|
|
}
|
|
if (typeof value === "string") {
|
|
const normalized = value.trim().toLowerCase();
|
|
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const toNumber = (value: unknown): number | undefined => {
|
|
if (value === undefined || value === null) {
|
|
return undefined;
|
|
}
|
|
if (typeof value === "number") {
|
|
return value;
|
|
}
|
|
const parsed = Number.parseInt(String(value).replace(/[^0-9-]/g, ""), 10);
|
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
};
|
|
|
|
const toString = (value: unknown): string | undefined => {
|
|
if (value === undefined || value === null) {
|
|
return undefined;
|
|
}
|
|
return String(value);
|
|
};
|
|
|
|
const normalizeCustomFields = (input: unknown): Record<string, string> | undefined => {
|
|
if (!input) {
|
|
return undefined;
|
|
}
|
|
|
|
const parsed = whmcsCustomFieldSchema.array().safeParse(input).success
|
|
? (input as Array<{ id: number | string; value?: string | null; name?: string }>)
|
|
: (() => {
|
|
const wrapped = z.object({ customfield: z.union([whmcsCustomFieldSchema, whmcsCustomFieldSchema.array()]) }).safeParse(input);
|
|
if (wrapped.success) {
|
|
const value = wrapped.data.customfield;
|
|
return Array.isArray(value) ? value : [value];
|
|
}
|
|
return undefined;
|
|
})();
|
|
|
|
if (!parsed) {
|
|
return undefined;
|
|
}
|
|
|
|
const result: Record<string, string> = {};
|
|
for (const field of parsed) {
|
|
const key = field.name || String(field.id);
|
|
if (key) {
|
|
result[key] = field.value ?? "";
|
|
}
|
|
}
|
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
};
|
|
|
|
const normalizeUsers = (input: unknown): Customer["users"] => {
|
|
if (!input) {
|
|
return undefined;
|
|
}
|
|
|
|
const parsed = z
|
|
.union([whmcsUserSchema, whmcsUserSchema.array()])
|
|
.safeParse(input);
|
|
|
|
if (!parsed.success) {
|
|
return undefined;
|
|
}
|
|
|
|
const usersArray = Array.isArray(parsed.data) ? parsed.data : [parsed.data];
|
|
|
|
const normalize = (value: z.infer<typeof whmcsUserSchema>) => customerUserSchema.parse(value);
|
|
|
|
const users = usersArray.map(normalize);
|
|
return users.length > 0 ? users : undefined;
|
|
};
|
|
|
|
const normalizeAddress = (client: WhmcsClient): CustomerAddress | undefined => {
|
|
const address = customerAddressSchema.parse({
|
|
address1: client.address1 ?? null,
|
|
address2: client.address2 ?? null,
|
|
city: client.city ?? null,
|
|
state: client.fullstate ?? client.state ?? null,
|
|
postcode: client.postcode ?? null,
|
|
country: client.country ?? null,
|
|
countryCode: client.countrycode ?? null,
|
|
phoneNumber: client.phonenumberformatted ?? client.telephoneNumber ?? client.phonenumber ?? null,
|
|
phoneCountryCode: client.phonecc ?? null,
|
|
});
|
|
|
|
const hasValues = Object.values(address).some(value => value !== undefined && value !== null && value !== "");
|
|
return hasValues ? address : undefined;
|
|
};
|
|
|
|
const normalizeEmailPreferences = (input: unknown) => customerEmailPreferencesSchema.parse(input ?? {});
|
|
|
|
const normalizeStats = (input: unknown): CustomerStats | undefined => {
|
|
const parsed = whmcsClientStatsSchema.safeParse(input);
|
|
if (!parsed.success || !parsed.data) {
|
|
return undefined;
|
|
}
|
|
const stats = customerStatsSchema.parse(parsed.data);
|
|
return stats;
|
|
};
|
|
|
|
export const parseWhmcsClient = (raw: unknown): WhmcsClient => whmcsClientSchema.parse(raw);
|
|
|
|
export const parseWhmcsClientResponse = (raw: unknown): WhmcsClientResponse =>
|
|
whmcsClientResponseSchema.parse(raw);
|
|
|
|
export const transformWhmcsClient = (raw: unknown): Customer => {
|
|
const client = parseWhmcsClient(raw);
|
|
const customer: Customer = {
|
|
id: Number(client.id),
|
|
clientId: client.client_id ? Number(client.client_id) : client.userid ? Number(client.userid) : undefined,
|
|
ownerUserId: client.owner_user_id ? Number(client.owner_user_id) : undefined,
|
|
userId: client.userid ? Number(client.userid) : undefined,
|
|
uuid: client.uuid ?? null,
|
|
firstname: client.firstname ?? null,
|
|
lastname: client.lastname ?? null,
|
|
fullname: client.fullname ?? null,
|
|
companyName: client.companyname ?? null,
|
|
email: client.email,
|
|
status: client.status ?? null,
|
|
language: client.language ?? null,
|
|
defaultGateway: client.defaultgateway ?? null,
|
|
defaultPaymentMethodId: client.defaultpaymethodid ? Number(client.defaultpaymethodid) : undefined,
|
|
currencyId: client.currency !== undefined ? toNumber(client.currency) : undefined,
|
|
currencyCode: client.currency_code ?? null,
|
|
taxId: client.tax_id ?? null,
|
|
phoneNumber: client.phonenumberformatted ?? client.telephoneNumber ?? client.phonenumber ?? null,
|
|
phoneCountryCode: client.phonecc ?? null,
|
|
telephoneNumber: client.telephoneNumber ?? null,
|
|
allowSingleSignOn: toBoolean(client.allowSingleSignOn) ?? null,
|
|
emailVerified: toBoolean(client.email_verified) ?? null,
|
|
marketingEmailsOptIn:
|
|
toBoolean(client.isOptedInToMarketingEmails) ?? toBoolean(client.marketing_emails_opt_in) ?? null,
|
|
notes: client.notes ?? null,
|
|
createdAt: client.datecreated ?? null,
|
|
lastLogin: client.lastlogin ?? null,
|
|
address: normalizeAddress(client),
|
|
emailPreferences: normalizeEmailPreferences(client.email_preferences),
|
|
customFields: normalizeCustomFields(client.customfields),
|
|
users: normalizeUsers(client.users?.user ?? client.users),
|
|
raw: client,
|
|
};
|
|
|
|
return customerSchema.parse(customer);
|
|
};
|
|
|
|
export const transformWhmcsClientResponse = (raw: unknown): Customer => {
|
|
const parsed = parseWhmcsClientResponse(raw);
|
|
const customer = transformWhmcsClient(parsed.client);
|
|
const stats = normalizeStats(parsed.stats);
|
|
if (stats) {
|
|
customer.stats = stats;
|
|
}
|
|
customer.raw = parsed;
|
|
return customerSchema.parse(customer);
|
|
};
|
|
|
|
export const transformWhmcsClientAddress = (raw: unknown): CustomerAddress | undefined => {
|
|
const client = parseWhmcsClient(raw);
|
|
return normalizeAddress(client);
|
|
};
|
|
|
|
export const transformWhmcsClientStats = (raw: unknown): CustomerStats | undefined => normalizeStats(raw);
|
|
|
|
export const prepareWhmcsClientAddressUpdate = (
|
|
address: Partial<CustomerAddress>
|
|
): Record<string, unknown> => {
|
|
const update: Record<string, unknown> = {};
|
|
if (address.address1 !== undefined) update.address1 = address.address1 ?? "";
|
|
if (address.address2 !== undefined) update.address2 = address.address2 ?? "";
|
|
if (address.city !== undefined) update.city = address.city ?? "";
|
|
if (address.state !== undefined) update.state = address.state ?? "";
|
|
if (address.postcode !== undefined) update.postcode = address.postcode ?? "";
|
|
if (address.country !== undefined) update.country = address.country ?? "";
|
|
if (address.countryCode !== undefined) update.countrycode = address.countryCode ?? "";
|
|
if (address.phoneNumber !== undefined) update.phonenumber = address.phoneNumber ?? "";
|
|
if (address.phoneCountryCode !== undefined) update.phonecc = address.phoneCountryCode ?? "";
|
|
return update;
|
|
};
|