124 lines
3.9 KiB
TypeScript
Raw Normal View History

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 {
AuthRequest as FreebitAuthRequest,
AuthResponse as FreebitAuthResponse,
} from "@customer-portal/domain/sim/providers/freebit";
import { Providers } from "@customer-portal/domain/sim";
import { FreebitError } from "./freebit-error.service";
interface FreebitConfig {
baseUrl: string;
oemId: string;
oemKey: string;
timeout: number;
retryAttempts: number;
detailsEndpoint?: string;
}
@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 = Providers.Freebit.schemas.auth.parse({
oemId: this.config.oemId,
oemKey: this.config.oemKey,
});
const response = await fetch(`${this.config.baseUrl}/authOem/`, {
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 json = (await response.json()) as unknown;
const data = Providers.Freebit.mapper.transformFreebitAuthResponse(json);
if (data.resultCode !== "100" || !data.authKey) {
throw new FreebitError(
`Authentication failed: ${data.status?.message ?? "Unknown error"}`,
data.resultCode,
data.status?.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());
}
}