diff --git a/apps/bff/src/auth/services/token-blacklist.service.ts b/apps/bff/src/auth/services/token-blacklist.service.ts index 993aee50..4a8d35a7 100644 --- a/apps/bff/src/auth/services/token-blacklist.service.ts +++ b/apps/bff/src/auth/services/token-blacklist.service.ts @@ -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")); diff --git a/apps/bff/src/subscriptions/sim-usage-store.service.ts b/apps/bff/src/subscriptions/sim-usage-store.service.ts index 3af5bc67..63d2b352 100644 --- a/apps/bff/src/subscriptions/sim-usage-store.service.ts +++ b/apps/bff/src/subscriptions/sim-usage-store.service.ts @@ -19,8 +19,8 @@ export class SimUsageStoreService { async upsertToday(account: string, usageMb: number, date?: Date): Promise { 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 { 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; } } diff --git a/apps/bff/src/vendors/freebit/freebit.service.ts b/apps/bff/src/vendors/freebit/freebit.service.ts index c33910da..4b803b77 100644 --- a/apps/bff/src/vendors/freebit/freebit.service.ts +++ b/apps/bff/src/vendors/freebit/freebit.service.ts @@ -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( - endpoint: string, - data: unknown - ): Promise { + private async makeAuthenticatedRequest< + T extends { + resultCode: string | number; + status?: { message?: string; statusCode?: string | number }; + } + >(endpoint: string, data: unknown): Promise { const authKey = await this.getAuthKey(); const requestData = { ...(data as Record), 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; } } diff --git a/apps/bff/src/vendors/whmcs/services/whmcs-invoice.service.ts b/apps/bff/src/vendors/whmcs/services/whmcs-invoice.service.ts index 7f1bc5a4..93936f3d 100644 --- a/apps/bff/src/vendors/whmcs/services/whmcs-invoice.service.ts +++ b/apps/bff/src/vendors/whmcs/services/whmcs-invoice.service.ts @@ -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"; diff --git a/apps/portal/scripts/dev-prep.mjs b/apps/portal/scripts/dev-prep.mjs index 8a2a5ff1..283787ae 100644 --- a/apps/portal/scripts/dev-prep.mjs +++ b/apps/portal/scripts/dev-prep.mjs @@ -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");