- 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.
121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
import { Injectable, Inject, InternalServerErrorException } from "@nestjs/common";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { Logger } from "nestjs-pino";
|
|
import { getErrorMessage } from "@bff/core/utils/error.util";
|
|
import type {
|
|
FreebitConfig,
|
|
FreebitAuthRequest,
|
|
FreebitAuthResponse,
|
|
} from "../interfaces/freebit.types";
|
|
import { FreebitError } from "./freebit-error.service";
|
|
|
|
@Injectable()
|
|
export class FreebitAuthService {
|
|
private readonly config: FreebitConfig;
|
|
private authKeyCache: { token: string; expiresAt: number } | null = null;
|
|
|
|
constructor(
|
|
private readonly configService: ConfigService,
|
|
@Inject(Logger) private readonly logger: Logger
|
|
) {
|
|
this.config = {
|
|
baseUrl:
|
|
this.configService.get<string>("FREEBIT_BASE_URL") || "https://i1.mvno.net/emptool/api",
|
|
oemId: this.configService.get<string>("FREEBIT_OEM_ID") || "PASI",
|
|
oemKey: this.configService.get<string>("FREEBIT_OEM_KEY") || "",
|
|
timeout: this.configService.get<number>("FREEBIT_TIMEOUT") || 30000,
|
|
retryAttempts: this.configService.get<number>("FREEBIT_RETRY_ATTEMPTS") || 3,
|
|
detailsEndpoint:
|
|
this.configService.get<string>("FREEBIT_DETAILS_ENDPOINT") || "/master/getAcnt/",
|
|
};
|
|
|
|
if (!this.config.oemKey) {
|
|
this.logger.warn("FREEBIT_OEM_KEY is not configured. SIM management features will not work.");
|
|
}
|
|
|
|
this.logger.debug("Freebit auth service initialized", {
|
|
baseUrl: this.config.baseUrl,
|
|
oemId: this.config.oemId,
|
|
hasOemKey: !!this.config.oemKey,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the current configuration
|
|
*/
|
|
getConfig(): FreebitConfig {
|
|
return this.config;
|
|
}
|
|
|
|
/**
|
|
* Get authentication key (cached or fetch new one)
|
|
*/
|
|
async getAuthKey(): Promise<string> {
|
|
if (this.authKeyCache && this.authKeyCache.expiresAt > Date.now()) {
|
|
return this.authKeyCache.token;
|
|
}
|
|
|
|
try {
|
|
if (!this.config.oemKey) {
|
|
throw new Error("Freebit API not configured: FREEBIT_OEM_KEY is missing");
|
|
}
|
|
|
|
const request: FreebitAuthRequest = {
|
|
oemId: this.config.oemId,
|
|
oemKey: this.config.oemKey,
|
|
};
|
|
|
|
// Ensure proper URL construction - remove double slashes
|
|
const baseUrl = this.config.baseUrl.replace(/\/$/, '');
|
|
const authUrl = `${baseUrl}/authOem/`;
|
|
|
|
const response = await fetch(authUrl, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: `json=${JSON.stringify(request)}`,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const data = (await response.json()) as FreebitAuthResponse;
|
|
const resultCode = data?.resultCode != null ? String(data.resultCode).trim() : undefined;
|
|
const statusCode =
|
|
data?.status?.statusCode != null ? String(data.status.statusCode).trim() : undefined;
|
|
|
|
if (resultCode !== "100") {
|
|
throw new FreebitError(
|
|
`Authentication failed: ${data.status.message}`,
|
|
resultCode,
|
|
statusCode,
|
|
data.status.message
|
|
);
|
|
}
|
|
|
|
this.authKeyCache = { token: data.authKey, expiresAt: Date.now() + 50 * 60 * 1000 };
|
|
this.logger.log("Successfully authenticated with Freebit API");
|
|
return data.authKey;
|
|
} catch (error: unknown) {
|
|
const message = getErrorMessage(error);
|
|
this.logger.error("Failed to authenticate with Freebit API", { error: message });
|
|
throw new InternalServerErrorException("Failed to authenticate with Freebit API");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear cached authentication key
|
|
*/
|
|
clearAuthCache(): void {
|
|
this.authKeyCache = null;
|
|
this.logger.debug("Cleared Freebit auth cache");
|
|
}
|
|
|
|
/**
|
|
* Check if we have a valid cached auth key
|
|
*/
|
|
hasValidAuthCache(): boolean {
|
|
return !!(this.authKeyCache && this.authKeyCache.expiresAt > Date.now());
|
|
}
|
|
}
|