tema 1640fae457 Add new payment methods and health check endpoints in Auth and Invoices services
- 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.
2025-08-30 15:10:24 +09:00

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) + "...";
}