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:
tema 2025-09-09 16:07:48 +09:00
parent de35397cf9
commit 5f7fb483d7
5 changed files with 65 additions and 59 deletions

View File

@ -24,7 +24,7 @@ export class TokenBlacklistService {
if (ttl > 0) {
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
try {
const defaultTtl = this.parseJwtExpiry(this.configService.get("JWT_EXPIRES_IN", "7d"));

View File

@ -19,8 +19,8 @@ export class SimUsageStoreService {
async upsertToday(account: string, usageMb: number, date?: Date): Promise<void> {
const day = this.normalizeDate(date);
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
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
where: { account_date: { account, date: day } as unknown },
update: { usageMb },
@ -39,6 +39,7 @@ export class SimUsageStoreService {
const end = this.normalizeDate();
const start = new Date(end);
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({
where: { account, date: { gte: start, lte: end } },
orderBy: { date: "desc" },
@ -49,9 +50,10 @@ export class SimUsageStoreService {
async cleanupPreviousMonths(): Promise<number> {
const now = new Date();
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 } },
});
})) as unknown as { count: number };
return result.count;
}
}

View File

@ -138,8 +138,9 @@ export class FreebititService {
this.logger.log("Successfully authenticated with Freebit API");
return data.authKey;
} catch (error: any) {
this.logger.error("Failed to authenticate with Freebit API", { error: error.message });
} catch (error: unknown) {
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");
}
}
@ -147,10 +148,12 @@ export class FreebititService {
/**
* Make authenticated API request with error handling
*/
private async makeAuthenticatedRequest<T extends { resultCode: string | number; status?: { message?: string; statusCode?: string | number } }>(
endpoint: string,
data: unknown
): Promise<T> {
private async makeAuthenticatedRequest<
T extends {
resultCode: string | number;
status?: { message?: string; statusCode?: string | number };
}
>(endpoint: string, data: unknown): Promise<T> {
const authKey = await this.getAuthKey();
const requestData = { ...(data as Record<string, unknown>), authKey };
@ -165,14 +168,8 @@ export class FreebititService {
});
if (!response.ok) {
let bodySnippet: string | undefined;
let text: string | null = null;
try {
text = await response.text();
} catch (_e) {
text = null;
}
bodySnippet = text ? text.slice(0, 500) : undefined;
const text: string | null = await response.text().catch(() => null);
const bodySnippet: string | undefined = text ? text.slice(0, 500) : undefined;
this.logger.error("Freebit API non-OK response", {
endpoint,
url,
@ -289,10 +286,10 @@ export class FreebititService {
}
if (!response) {
throw (
lastError ||
new InternalServerErrorException("Failed to fetch SIM details: all endpoints failed")
);
if (lastError instanceof Error) {
throw lastError;
}
throw new InternalServerErrorException("Failed to fetch SIM details: all endpoints failed");
}
type AcctDetailItem = {
@ -307,7 +304,7 @@ export class FreebititService {
imsi?: string | number;
eid?: string;
contractLine?: string;
size?: "standard" | "nano" | "micro" | "esim" | string;
size?: string;
sms?: number;
talk?: number;
ipv4?: string;
@ -417,9 +414,10 @@ export class FreebititService {
});
return simUsage;
} catch (error: any) {
this.logger.error(`Failed to get SIM usage for account ${account}`, { error: error.message });
throw error;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(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,
scheduled: isScheduled,
});
} catch (error: any) {
this.logger.error(`Failed to top up SIM ${account}`, {
error: error.message,
account,
quotaMb,
});
throw error;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
this.logger.error(`Failed to top up SIM ${account}`, { error: message, account, quotaMb });
throw error as Error;
}
}
@ -528,11 +523,10 @@ export class FreebititService {
});
return history;
} catch (error: any) {
this.logger.error(`Failed to get SIM top-up history for account ${account}`, {
error: error.message,
});
throw error;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
this.logger.error(`Failed to get SIM top-up history for account ${account}`, { error: message });
throw error as Error;
}
}
@ -571,13 +565,14 @@ export class FreebititService {
ipv4: response.ipv4,
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}`, {
error: error.message,
error: message,
account,
newPlanCode,
});
throw error;
throw error as Error;
}
}
@ -627,12 +622,13 @@ export class FreebititService {
internationalRoamingEnabled: features.internationalRoamingEnabled,
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}`, {
error: error.message,
error: message,
account,
});
throw error;
throw error as Error;
}
}
@ -655,12 +651,10 @@ export class FreebititService {
account,
scheduled: !!scheduledAt,
});
} catch (error: any) {
this.logger.error(`Failed to cancel SIM for account ${account}`, {
error: error.message,
account,
});
throw error;
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
this.logger.error(`Failed to cancel SIM for account ${account}`, { error: message, account });
throw error as Error;
}
}
@ -735,13 +729,14 @@ export class FreebititService {
this.logger.log(`Successfully reissued eSIM profile via PA05-41 for account ${account}`, {
account,
});
} catch (error: any) {
} catch (error: unknown) {
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}`, {
error: error.message,
error: message,
account,
});
throw error;
throw error as Error;
}
}
@ -785,13 +780,14 @@ export class FreebititService {
oldProductNumber: options.oldProductNumber,
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}`, {
error: error.message,
error: message,
account,
newEid,
});
throw error;
throw error as Error;
}
}
@ -802,8 +798,9 @@ export class FreebititService {
try {
await this.getAuthKey();
return true;
} catch (error: any) {
this.logger.error("Freebit API health check failed", { error: error.message });
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
this.logger.error("Freebit API health check failed", { error: message });
return false;
}
}

View File

@ -5,7 +5,12 @@ import { Invoice, InvoiceList } from "@customer-portal/shared";
import { WhmcsConnectionService } from "./whmcs-connection.service";
import { WhmcsDataTransformer } from "../transformers/whmcs-data.transformer";
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 {
status?: "Paid" | "Unpaid" | "Cancelled" | "Overdue" | "Collections";

View File

@ -4,6 +4,8 @@
// Ensure dev-time Next.js manifests exist to avoid noisy ENOENT errors
import { mkdirSync, existsSync, writeFileSync } from "fs";
import { join } from "path";
import { URL } from "node:url";
/* global console */
const root = new URL("..", import.meta.url).pathname; // apps/portal
const nextDir = join(root, ".next");