- Introduced `validateSignup` endpoint in AuthController for customer number validation during signup. - Added `healthCheck` method in AuthService to verify service integrations and database connectivity. - Implemented `getPaymentMethods`, `getPaymentGateways`, and `refreshPaymentMethods` endpoints in InvoicesController for managing user payment options. - Enhanced InvoicesService with methods to invalidate payment methods cache and improved error handling. - Updated currency handling across various services and components to reflect JPY as the default currency. - Added new dependencies in package.json for ESLint configuration.
145 lines
3.4 KiB
TypeScript
145 lines
3.4 KiB
TypeScript
// Modern validation utilities with ES2024 features
|
|
|
|
/**
|
|
* Modern validation result type
|
|
*/
|
|
export type ValidationResult<T> =
|
|
| {
|
|
success: true;
|
|
data: T;
|
|
}
|
|
| {
|
|
success: false;
|
|
error: string;
|
|
errors?: Record<string, string[]>;
|
|
};
|
|
|
|
/**
|
|
* Create a successful validation result
|
|
*/
|
|
export function success<T>(data: T): ValidationResult<T> {
|
|
return { success: true, data };
|
|
}
|
|
|
|
/**
|
|
* Create a failed validation result
|
|
*/
|
|
export function failure<T>(error: string, errors?: Record<string, string[]>): ValidationResult<T> {
|
|
return { success: false, error, errors };
|
|
}
|
|
|
|
/**
|
|
* Enhanced email validation with better error reporting
|
|
*/
|
|
export function validateEmail(email: string): ValidationResult<string> {
|
|
if (!email || typeof email !== "string") {
|
|
return failure("Email is required");
|
|
}
|
|
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
|
if (!emailRegex.test(email)) {
|
|
return failure("Invalid email format");
|
|
}
|
|
|
|
const trimmed = email.trim().toLowerCase();
|
|
|
|
if (trimmed.length > 254) {
|
|
return failure("Email is too long");
|
|
}
|
|
|
|
return success(trimmed);
|
|
}
|
|
|
|
/**
|
|
* Legacy boolean validation (kept for backward compatibility)
|
|
*/
|
|
export function isValidEmail(email: string): boolean {
|
|
const result = validateEmail(email);
|
|
return result.success;
|
|
}
|
|
|
|
/**
|
|
* Enhanced phone validation with better error reporting
|
|
*/
|
|
export function validatePhoneNumber(phone: string): ValidationResult<string> {
|
|
if (!phone || typeof phone !== "string") {
|
|
return failure("Phone number is required");
|
|
}
|
|
|
|
const phoneRegex = /^\+?[\d\s\-()]{10,}$/;
|
|
|
|
if (!phoneRegex.test(phone)) {
|
|
return failure("Invalid phone number format");
|
|
}
|
|
|
|
const cleaned = phone.replace(/[\s\-()]/g, "");
|
|
|
|
if (cleaned.length < 10) {
|
|
return failure("Phone number is too short");
|
|
}
|
|
|
|
return success(cleaned);
|
|
}
|
|
|
|
/**
|
|
* Legacy boolean validation (kept for backward compatibility)
|
|
*/
|
|
export function isValidPhoneNumber(phone: string): boolean {
|
|
const result = validatePhoneNumber(phone);
|
|
return result.success;
|
|
}
|
|
|
|
/**
|
|
* Validates password strength
|
|
*/
|
|
export function isStrongPassword(password: string): boolean {
|
|
// At least 8 characters, one uppercase, one lowercase, one number
|
|
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
|
|
return passwordRegex.test(password);
|
|
}
|
|
|
|
/**
|
|
* Validates UUID format
|
|
*/
|
|
export function isValidUUID(uuid: string): boolean {
|
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
return uuidRegex.test(uuid);
|
|
}
|
|
|
|
/**
|
|
* Sanitizes string by removing HTML tags and scripts
|
|
*/
|
|
export function sanitizeString(input: string): string {
|
|
return input
|
|
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")
|
|
.replace(/<[^>]*>/g, "")
|
|
.trim();
|
|
}
|
|
|
|
/**
|
|
* Formats currency amount
|
|
*/
|
|
export function formatCurrency(amount: number, currency = "JPY"): string {
|
|
return new Intl.NumberFormat("ja-JP", {
|
|
style: "currency",
|
|
currency,
|
|
}).format(amount);
|
|
}
|
|
|
|
/**
|
|
* Formats date to locale string
|
|
*/
|
|
export function formatDate(date: Date | string, locale = "en-US"): string {
|
|
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
return dateObj.toLocaleDateString(locale);
|
|
}
|
|
|
|
/**
|
|
* Truncates string to specified length with ellipsis
|
|
*/
|
|
export function truncateString(str: string, maxLength: number): string {
|
|
if (str.length <= maxLength) return str;
|
|
return str.slice(0, maxLength - 3) + "...";
|
|
}
|