- Deleted migration file that removed cached profile fields from the users table, centralizing profile data retrieval from WHMCS. - Updated CsrfMiddleware to include new public authentication endpoints for password reset, setting password, and WHMCS account linking. - Enhanced error handling in password and WHMCS linking workflows to provide clearer feedback on missing mappings and improve user experience. - Adjusted user creation and update methods in UsersFacade to handle cases where WHMCS mappings are not yet available, ensuring smoother account setup.
120 lines
3.7 KiB
TypeScript
120 lines
3.7 KiB
TypeScript
import { Injectable, Inject } from "@nestjs/common";
|
|
import { Logger } from "nestjs-pino";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { EmailService } from "@bff/infra/email/email.service";
|
|
import { getErrorMessage } from "@bff/core/utils/error.util";
|
|
import type { SimNotificationContext } from "../interfaces/sim-base.interface";
|
|
|
|
@Injectable()
|
|
export class SimNotificationService {
|
|
constructor(
|
|
@Inject(Logger) private readonly logger: Logger,
|
|
private readonly email: EmailService,
|
|
private readonly configService: ConfigService
|
|
) {}
|
|
|
|
/**
|
|
* Send notification for SIM actions
|
|
*/
|
|
async notifySimAction(
|
|
action: string,
|
|
status: "SUCCESS" | "ERROR",
|
|
context: SimNotificationContext
|
|
): Promise<void> {
|
|
const subject = `[SIM ACTION] ${action} - ${status}`;
|
|
const toAddress = this.configService.get<string>("SIM_ALERT_EMAIL_TO");
|
|
const fromAddress = this.configService.get<string>("SIM_ALERT_EMAIL_FROM");
|
|
|
|
if (!toAddress || !fromAddress) {
|
|
this.logger.debug("SIM action notification skipped: email config missing", {
|
|
action,
|
|
status,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const publicContext = this.redactSensitiveFields(context);
|
|
|
|
try {
|
|
const lines: string[] = [
|
|
`Action: ${action}`,
|
|
`Result: ${status}`,
|
|
`Timestamp: ${new Date().toISOString()}`,
|
|
"",
|
|
"Context:",
|
|
JSON.stringify(publicContext, null, 2),
|
|
];
|
|
await this.email.sendEmail({
|
|
to: toAddress,
|
|
from: fromAddress,
|
|
subject,
|
|
text: lines.join("\n"),
|
|
});
|
|
} catch (err) {
|
|
this.logger.warn("Failed to send SIM action notification email", {
|
|
action,
|
|
status,
|
|
error: getErrorMessage(err),
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redact sensitive information from notification context
|
|
*/
|
|
private redactSensitiveFields(context: Record<string, unknown>): Record<string, unknown> {
|
|
const sanitized: Record<string, unknown> = {};
|
|
for (const [key, value] of Object.entries(context)) {
|
|
if (typeof key === "string" && key.toLowerCase().includes("password")) {
|
|
sanitized[key] = "[REDACTED]";
|
|
continue;
|
|
}
|
|
|
|
if (typeof value === "string" && value.length > 200) {
|
|
sanitized[key] = `${value.substring(0, 200)}…`;
|
|
continue;
|
|
}
|
|
|
|
sanitized[key] = value;
|
|
}
|
|
return sanitized;
|
|
}
|
|
|
|
/**
|
|
* Convert technical errors to user-friendly messages for SIM operations
|
|
*/
|
|
getUserFriendlySimError(technicalError: string): string {
|
|
if (!technicalError) {
|
|
return "SIM operation failed. Please try again or contact support.";
|
|
}
|
|
|
|
const errorLower = technicalError.toLowerCase();
|
|
|
|
// Freebit API errors
|
|
if (errorLower.includes("api error: ng") || errorLower.includes("account not found")) {
|
|
return "SIM account not found. Please contact support to verify your SIM configuration.";
|
|
}
|
|
|
|
if (errorLower.includes("authentication failed") || errorLower.includes("auth")) {
|
|
return "SIM service is temporarily unavailable. Please try again later.";
|
|
}
|
|
|
|
if (errorLower.includes("timeout") || errorLower.includes("network")) {
|
|
return "SIM service request timed out. Please try again.";
|
|
}
|
|
|
|
// WHMCS errors
|
|
if (errorLower.includes("invalid permissions") || errorLower.includes("not allowed")) {
|
|
return "SIM service is temporarily unavailable. Please contact support for assistance.";
|
|
}
|
|
|
|
// Generic errors
|
|
if (errorLower.includes("failed") || errorLower.includes("error")) {
|
|
return "SIM operation failed. Please try again or contact support.";
|
|
}
|
|
|
|
// Default fallback
|
|
return "SIM operation failed. Please try again or contact support.";
|
|
}
|
|
}
|