Refactor error handling and type safety across services
- Simplified error handling in TokenBlacklistService by removing unnecessary error variable. - Enhanced type safety in SimUsageStoreService with improved type assertions and error handling. - Updated FreebititService to provide clearer error messages and consistent error handling across multiple methods. - Standardized import statements in WhmcsInvoiceService for better organization and clarity. - Added missing imports in dev-prep script to ensure proper functionality.
This commit is contained in:
parent
de35397cf9
commit
5f7fb483d7
@ -24,7 +24,7 @@ export class TokenBlacklistService {
|
|||||||
if (ttl > 0) {
|
if (ttl > 0) {
|
||||||
await this.redis.setex(`blacklist:${token}`, ttl, "1");
|
await this.redis.setex(`blacklist:${token}`, ttl, "1");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
// If we can't parse the token, blacklist it for the default JWT expiry time
|
// If we can't parse the token, blacklist it for the default JWT expiry time
|
||||||
try {
|
try {
|
||||||
const defaultTtl = this.parseJwtExpiry(this.configService.get("JWT_EXPIRES_IN", "7d"));
|
const defaultTtl = this.parseJwtExpiry(this.configService.get("JWT_EXPIRES_IN", "7d"));
|
||||||
|
|||||||
@ -19,8 +19,8 @@ export class SimUsageStoreService {
|
|||||||
async upsertToday(account: string, usageMb: number, date?: Date): Promise<void> {
|
async upsertToday(account: string, usageMb: number, date?: Date): Promise<void> {
|
||||||
const day = this.normalizeDate(date);
|
const day = this.normalizeDate(date);
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
await this.prisma.simUsageDaily.upsert({
|
await this.prisma.simUsageDaily.upsert({
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-expect-error composite unique input type depends on Prisma schema
|
// @ts-expect-error composite unique input type depends on Prisma schema
|
||||||
where: { account_date: { account, date: day } as unknown },
|
where: { account_date: { account, date: day } as unknown },
|
||||||
update: { usageMb },
|
update: { usageMb },
|
||||||
@ -39,6 +39,7 @@ export class SimUsageStoreService {
|
|||||||
const end = this.normalizeDate();
|
const end = this.normalizeDate();
|
||||||
const start = new Date(end);
|
const start = new Date(end);
|
||||||
start.setUTCDate(end.getUTCDate() - (days - 1));
|
start.setUTCDate(end.getUTCDate() - (days - 1));
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
const rows = (await this.prisma.simUsageDaily.findMany({
|
const rows = (await this.prisma.simUsageDaily.findMany({
|
||||||
where: { account, date: { gte: start, lte: end } },
|
where: { account, date: { gte: start, lte: end } },
|
||||||
orderBy: { date: "desc" },
|
orderBy: { date: "desc" },
|
||||||
@ -49,9 +50,10 @@ export class SimUsageStoreService {
|
|||||||
async cleanupPreviousMonths(): Promise<number> {
|
async cleanupPreviousMonths(): Promise<number> {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const firstOfMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
const firstOfMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
||||||
const result = await this.prisma.simUsageDaily.deleteMany({
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
|
const result = (await this.prisma.simUsageDaily.deleteMany({
|
||||||
where: { date: { lt: firstOfMonth } },
|
where: { date: { lt: firstOfMonth } },
|
||||||
});
|
})) as unknown as { count: number };
|
||||||
return result.count;
|
return result.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
apps/bff/src/vendors/freebit/freebit.service.ts
vendored
105
apps/bff/src/vendors/freebit/freebit.service.ts
vendored
@ -138,8 +138,9 @@ export class FreebititService {
|
|||||||
|
|
||||||
this.logger.log("Successfully authenticated with Freebit API");
|
this.logger.log("Successfully authenticated with Freebit API");
|
||||||
return data.authKey;
|
return data.authKey;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.logger.error("Failed to authenticate with Freebit API", { error: error.message });
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
this.logger.error("Failed to authenticate with Freebit API", { error: message });
|
||||||
throw new InternalServerErrorException("Failed to authenticate with Freebit API");
|
throw new InternalServerErrorException("Failed to authenticate with Freebit API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,10 +148,12 @@ export class FreebititService {
|
|||||||
/**
|
/**
|
||||||
* Make authenticated API request with error handling
|
* Make authenticated API request with error handling
|
||||||
*/
|
*/
|
||||||
private async makeAuthenticatedRequest<T extends { resultCode: string | number; status?: { message?: string; statusCode?: string | number } }>(
|
private async makeAuthenticatedRequest<
|
||||||
endpoint: string,
|
T extends {
|
||||||
data: unknown
|
resultCode: string | number;
|
||||||
): Promise<T> {
|
status?: { message?: string; statusCode?: string | number };
|
||||||
|
}
|
||||||
|
>(endpoint: string, data: unknown): Promise<T> {
|
||||||
const authKey = await this.getAuthKey();
|
const authKey = await this.getAuthKey();
|
||||||
const requestData = { ...(data as Record<string, unknown>), authKey };
|
const requestData = { ...(data as Record<string, unknown>), authKey };
|
||||||
|
|
||||||
@ -165,14 +168,8 @@ export class FreebititService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let bodySnippet: string | undefined;
|
const text: string | null = await response.text().catch(() => null);
|
||||||
let text: string | null = null;
|
const bodySnippet: string | undefined = text ? text.slice(0, 500) : undefined;
|
||||||
try {
|
|
||||||
text = await response.text();
|
|
||||||
} catch (_e) {
|
|
||||||
text = null;
|
|
||||||
}
|
|
||||||
bodySnippet = text ? text.slice(0, 500) : undefined;
|
|
||||||
this.logger.error("Freebit API non-OK response", {
|
this.logger.error("Freebit API non-OK response", {
|
||||||
endpoint,
|
endpoint,
|
||||||
url,
|
url,
|
||||||
@ -289,10 +286,10 @@ export class FreebititService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
throw (
|
if (lastError instanceof Error) {
|
||||||
lastError ||
|
throw lastError;
|
||||||
new InternalServerErrorException("Failed to fetch SIM details: all endpoints failed")
|
}
|
||||||
);
|
throw new InternalServerErrorException("Failed to fetch SIM details: all endpoints failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
type AcctDetailItem = {
|
type AcctDetailItem = {
|
||||||
@ -307,7 +304,7 @@ export class FreebititService {
|
|||||||
imsi?: string | number;
|
imsi?: string | number;
|
||||||
eid?: string;
|
eid?: string;
|
||||||
contractLine?: string;
|
contractLine?: string;
|
||||||
size?: "standard" | "nano" | "micro" | "esim" | string;
|
size?: string;
|
||||||
sms?: number;
|
sms?: number;
|
||||||
talk?: number;
|
talk?: number;
|
||||||
ipv4?: string;
|
ipv4?: string;
|
||||||
@ -417,9 +414,10 @@ export class FreebititService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return simUsage;
|
return simUsage;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.logger.error(`Failed to get SIM usage for account ${account}`, { error: error.message });
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
throw error;
|
this.logger.error(`Failed to get SIM usage for account ${account}`, { error: message });
|
||||||
|
throw error as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,13 +476,10 @@ export class FreebititService {
|
|||||||
campaignCode: options.campaignCode,
|
campaignCode: options.campaignCode,
|
||||||
scheduled: isScheduled,
|
scheduled: isScheduled,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.logger.error(`Failed to top up SIM ${account}`, {
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
error: error.message,
|
this.logger.error(`Failed to top up SIM ${account}`, { error: message, account, quotaMb });
|
||||||
account,
|
throw error as Error;
|
||||||
quotaMb,
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,11 +523,10 @@ export class FreebititService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return history;
|
return history;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.logger.error(`Failed to get SIM top-up history for account ${account}`, {
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
error: error.message,
|
this.logger.error(`Failed to get SIM top-up history for account ${account}`, { error: message });
|
||||||
});
|
throw error as Error;
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,13 +565,14 @@ export class FreebititService {
|
|||||||
ipv4: response.ipv4,
|
ipv4: response.ipv4,
|
||||||
ipv6: response.ipv6,
|
ipv6: response.ipv6,
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
this.logger.error(`Failed to change SIM plan for account ${account}`, {
|
this.logger.error(`Failed to change SIM plan for account ${account}`, {
|
||||||
error: error.message,
|
error: message,
|
||||||
account,
|
account,
|
||||||
newPlanCode,
|
newPlanCode,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,12 +622,13 @@ export class FreebititService {
|
|||||||
internationalRoamingEnabled: features.internationalRoamingEnabled,
|
internationalRoamingEnabled: features.internationalRoamingEnabled,
|
||||||
networkType: features.networkType,
|
networkType: features.networkType,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
this.logger.error(`Failed to update SIM features for account ${account}`, {
|
this.logger.error(`Failed to update SIM features for account ${account}`, {
|
||||||
error: error.message,
|
error: message,
|
||||||
account,
|
account,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,12 +651,10 @@ export class FreebititService {
|
|||||||
account,
|
account,
|
||||||
scheduled: !!scheduledAt,
|
scheduled: !!scheduledAt,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.logger.error(`Failed to cancel SIM for account ${account}`, {
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
error: error.message,
|
this.logger.error(`Failed to cancel SIM for account ${account}`, { error: message, account });
|
||||||
account,
|
throw error as Error;
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,13 +729,14 @@ export class FreebititService {
|
|||||||
this.logger.log(`Successfully reissued eSIM profile via PA05-41 for account ${account}`, {
|
this.logger.log(`Successfully reissued eSIM profile via PA05-41 for account ${account}`, {
|
||||||
account,
|
account,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof BadRequestException) throw error;
|
if (error instanceof BadRequestException) throw error;
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
this.logger.error(`Failed to reissue eSIM profile via PA05-41 for account ${account}`, {
|
this.logger.error(`Failed to reissue eSIM profile via PA05-41 for account ${account}`, {
|
||||||
error: error.message,
|
error: message,
|
||||||
account,
|
account,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,13 +780,14 @@ export class FreebititService {
|
|||||||
oldProductNumber: options.oldProductNumber,
|
oldProductNumber: options.oldProductNumber,
|
||||||
oldEid: options.oldEid,
|
oldEid: options.oldEid,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
this.logger.error(`Failed to reissue eSIM profile via addAcnt for account ${account}`, {
|
this.logger.error(`Failed to reissue eSIM profile via addAcnt for account ${account}`, {
|
||||||
error: error.message,
|
error: message,
|
||||||
account,
|
account,
|
||||||
newEid,
|
newEid,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,8 +798,9 @@ export class FreebititService {
|
|||||||
try {
|
try {
|
||||||
await this.getAuthKey();
|
await this.getAuthKey();
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
this.logger.error("Freebit API health check failed", { error: error.message });
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
this.logger.error("Freebit API health check failed", { error: message });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,12 @@ import { Invoice, InvoiceList } from "@customer-portal/shared";
|
|||||||
import { WhmcsConnectionService } from "./whmcs-connection.service";
|
import { WhmcsConnectionService } from "./whmcs-connection.service";
|
||||||
import { WhmcsDataTransformer } from "../transformers/whmcs-data.transformer";
|
import { WhmcsDataTransformer } from "../transformers/whmcs-data.transformer";
|
||||||
import { WhmcsCacheService } from "../cache/whmcs-cache.service";
|
import { WhmcsCacheService } from "../cache/whmcs-cache.service";
|
||||||
import { WhmcsGetInvoicesParams } from "../types/whmcs-api.types";
|
import {
|
||||||
|
WhmcsGetInvoicesParams,
|
||||||
|
WhmcsCreateInvoiceParams,
|
||||||
|
WhmcsUpdateInvoiceParams,
|
||||||
|
WhmcsCapturePaymentParams,
|
||||||
|
} from "../types/whmcs-api.types";
|
||||||
|
|
||||||
export interface InvoiceFilters {
|
export interface InvoiceFilters {
|
||||||
status?: "Paid" | "Unpaid" | "Cancelled" | "Overdue" | "Collections";
|
status?: "Paid" | "Unpaid" | "Cancelled" | "Overdue" | "Collections";
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
// Ensure dev-time Next.js manifests exist to avoid noisy ENOENT errors
|
// Ensure dev-time Next.js manifests exist to avoid noisy ENOENT errors
|
||||||
import { mkdirSync, existsSync, writeFileSync } from "fs";
|
import { mkdirSync, existsSync, writeFileSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import { URL } from "node:url";
|
||||||
|
/* global console */
|
||||||
|
|
||||||
const root = new URL("..", import.meta.url).pathname; // apps/portal
|
const root = new URL("..", import.meta.url).pathname; // apps/portal
|
||||||
const nextDir = join(root, ".next");
|
const nextDir = join(root, ".next");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user