From 6e27d8a21ec73fccbe4444cd66bab31a50a38f2d Mon Sep 17 00:00:00 2001 From: tema Date: Tue, 9 Sep 2025 16:30:36 +0900 Subject: [PATCH] Refactor SIM management and usage services for improved functionality and type safety - Updated SimManagementService to schedule plan changes for immediate execution during testing. - Enhanced type safety in SimUsageStoreService by removing unsafe type assertions and improving method calls. - Improved error handling in FreebititService by ensuring proper type handling for plan codes and sizes. - Added functionality in manage.sh to automatically find and assign free ports for development services. --- .../subscriptions/sim-management.service.ts | 15 +++---- .../subscriptions/sim-usage-store.service.ts | 14 +++---- .../src/vendors/freebit/freebit.service.ts | 10 ++++- scripts/dev/manage.sh | 39 +++++++++++++++++++ 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/apps/bff/src/subscriptions/sim-management.service.ts b/apps/bff/src/subscriptions/sim-management.service.ts index 8a69ab6c..0a961b1b 100644 --- a/apps/bff/src/subscriptions/sim-management.service.ts +++ b/apps/bff/src/subscriptions/sim-management.service.ts @@ -580,18 +580,15 @@ export class SimManagementService { throw new BadRequestException("Invalid plan code"); } - // Automatically set to 1st of next month - const nextMonth = new Date(); - nextMonth.setMonth(nextMonth.getMonth() + 1); - nextMonth.setDate(1); // Set to 1st of the month - + // TESTING: schedule for immediate execution (today) + const now = new Date(); // Format as YYYYMMDD for Freebit API - const year = nextMonth.getFullYear(); - const month = String(nextMonth.getMonth() + 1).padStart(2, "0"); - const day = String(nextMonth.getDate()).padStart(2, "0"); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); const scheduledAt = `${year}${month}${day}`; - this.logger.log(`Auto-scheduled plan change to 1st of next month: ${scheduledAt}`, { + this.logger.log(`Scheduled plan change for testing (immediate): ${scheduledAt}`, { userId, subscriptionId, account, diff --git a/apps/bff/src/subscriptions/sim-usage-store.service.ts b/apps/bff/src/subscriptions/sim-usage-store.service.ts index 63d2b352..1487dc0d 100644 --- a/apps/bff/src/subscriptions/sim-usage-store.service.ts +++ b/apps/bff/src/subscriptions/sim-usage-store.service.ts @@ -19,10 +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({ - // @ts-expect-error composite unique input type depends on Prisma schema - where: { account_date: { account, date: day } as unknown }, + await (this.prisma as any).simUsageDaily.upsert({ + where: { account_date: { account, date: day } }, update: { usageMb }, create: { account, date: day, usageMb }, }); @@ -39,8 +37,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({ + const rows = (await (this.prisma as any).simUsageDaily.findMany({ where: { account, date: { gte: start, lte: end } }, orderBy: { date: "desc" }, })) as Array<{ date: Date; usageMb: number }>; @@ -50,10 +47,9 @@ export class SimUsageStoreService { async cleanupPreviousMonths(): Promise { const now = new Date(); const firstOfMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - const result = (await this.prisma.simUsageDaily.deleteMany({ + const result = (await (this.prisma as any).simUsageDaily.deleteMany({ where: { date: { lt: firstOfMonth } }, - })) as unknown as { count: number }; + })) 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 1c6df0c3..323208a7 100644 --- a/apps/bff/src/vendors/freebit/freebit.service.ts +++ b/apps/bff/src/vendors/freebit/freebit.service.ts @@ -344,10 +344,16 @@ export class FreebititService { iccid: simData.iccid ? String(simData.iccid) : undefined, imsi: simData.imsi ? String(simData.imsi) : undefined, eid: simData.eid, - planCode: simData.planCode, + planCode: String(simData.planCode ?? ""), status: this.mapSimStatus(String(simData.state || "pending")), simType: simData.eid ? "esim" : "physical", - size: simData.size, + size: ((): "standard" | "nano" | "micro" | "esim" => { + const sizeVal = String(simData.size ?? "").toLowerCase(); + if (sizeVal === "standard" || sizeVal === "nano" || sizeVal === "micro" || sizeVal === "esim") { + return sizeVal as "standard" | "nano" | "micro" | "esim"; + } + return simData.eid ? "esim" : "nano"; + })(), hasVoice: simData.talk === 10, hasSms: simData.sms === 10, remainingQuotaKb: typeof simData.quota === "number" ? simData.quota : 0, diff --git a/scripts/dev/manage.sh b/scripts/dev/manage.sh index 958283e7..4002d97b 100755 --- a/scripts/dev/manage.sh +++ b/scripts/dev/manage.sh @@ -175,6 +175,30 @@ kill_by_port() { fi } +# Check if a port is free using Node (portable) +is_port_free() { + local port="$1" + if ! command -v node >/dev/null 2>&1; then + return 0 # assume free if node unavailable + fi + node -e "const net=require('net');const p=parseInt(process.argv[1],10);const s=net.createServer();s.once('error',()=>process.exit(1));s.once('listening',()=>s.close(()=>process.exit(0)));s.listen({port:p,host:'127.0.0.1'});" "$port" +} + +# Find a free port starting from base, up to +50 +find_free_port() { + local base="$1" + local limit=$((base+50)) + local p="$base" + while [ "$p" -le "$limit" ]; do + if is_port_free "$p"; then + echo "$p" + return 0 + fi + p=$((p+1)) + done + echo "$base" +} + ######################################## # Commands ######################################## @@ -191,6 +215,21 @@ start_services() { local next="${NEXT_PORT:-$NEXT_PORT_DEFAULT}" local bff="${BFF_PORT:-$BFF_PORT_DEFAULT}" + # Auto-pick free ports if occupied + local next_free + next_free="$(find_free_port "$next")" + if [ "$next_free" != "$next" ]; then + warn "Port $next in use; assigning NEXT_PORT=$next_free" + export NEXT_PORT="$next_free" + next="$next_free" + fi + local bff_free + bff_free="$(find_free_port "$bff")" + if [ "$bff_free" != "$bff" ]; then + warn "Port $bff in use; assigning BFF_PORT=$bff_free" + export BFF_PORT="$bff_free" + bff="$bff_free" + fi log "✅ Development services are running!" log "🔗 Database: postgresql://${POSTGRES_USER:-$DB_USER_DEFAULT}:${POSTGRES_PASSWORD:-${POSTGRES_PASSWORD:-dev}}@localhost:5432/${POSTGRES_DB:-$DB_NAME_DEFAULT}" log "🔗 Redis: redis://localhost:6379"