diff --git a/apps/csms/src/lib/db.ts b/apps/csms/src/lib/db.ts index 9ae54a4..e0b5e7e 100644 --- a/apps/csms/src/lib/db.ts +++ b/apps/csms/src/lib/db.ts @@ -9,8 +9,6 @@ export const useDrizzle = () => { if (!pgPoolInstance || !drizzleInstance) { pgPoolInstance = new Pool({ connectionString: process.env.DATABASE_CONNECTION_STRING, - connectionTimeoutMillis: 3000, - idleTimeoutMillis: 10000, }) drizzleInstance = drizzle({ client: pgPoolInstance, schema }) } diff --git a/apps/csms/src/ocpp/actions/boot-notification.ts b/apps/csms/src/ocpp/actions/boot-notification.ts index d5a2150..c3a9985 100644 --- a/apps/csms/src/ocpp/actions/boot-notification.ts +++ b/apps/csms/src/ocpp/actions/boot-notification.ts @@ -8,86 +8,58 @@ import type { } from '../types.ts' const DEFAULT_HEARTBEAT_INTERVAL = 60 -const BOOT_NOTIFICATION_DB_TIMEOUT_MS = 3000 - -function withTimeout(promise: Promise, timeoutMs: number, label: string) { - return Promise.race([ - promise, - new Promise((_, reject) => { - setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs) - }), - ]) -} export async function handleBootNotification( payload: BootNotificationRequest, ctx: OcppConnectionContext, ): Promise { const db = useDrizzle() - const currentTime = dayjs().toISOString() - console.log(`[OCPP] BootNotification ${ctx.chargePointIdentifier} received`) + const [cp] = await db + .insert(chargePoint) + .values({ + id: crypto.randomUUID(), + chargePointIdentifier: ctx.chargePointIdentifier, + chargePointVendor: payload.chargePointVendor, + chargePointModel: payload.chargePointModel, + chargePointSerialNumber: payload.chargePointSerialNumber ?? null, + firmwareVersion: payload.firmwareVersion ?? null, + iccid: payload.iccid ?? null, + imsi: payload.imsi ?? null, + meterType: payload.meterType ?? null, + meterSerialNumber: payload.meterSerialNumber ?? null, + // New, unknown devices start as Pending — admin must manually accept them + registrationStatus: 'Pending', + heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL, + lastBootNotificationAt: dayjs().toDate(), + }) + .onConflictDoUpdate({ + target: chargePoint.chargePointIdentifier, + set: { + chargePointVendor: payload.chargePointVendor, + chargePointModel: payload.chargePointModel, + chargePointSerialNumber: payload.chargePointSerialNumber ?? null, + firmwareVersion: payload.firmwareVersion ?? null, + iccid: payload.iccid ?? null, + imsi: payload.imsi ?? null, + meterType: payload.meterType ?? null, + meterSerialNumber: payload.meterSerialNumber ?? null, + // Do NOT override registrationStatus — preserve whatever the admin set + heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL, + lastBootNotificationAt: dayjs().toDate(), + updatedAt: dayjs().toDate(), + }, + }) + .returning() - try { - const [cp] = await withTimeout( - db - .insert(chargePoint) - .values({ - id: crypto.randomUUID(), - chargePointIdentifier: ctx.chargePointIdentifier, - chargePointVendor: payload.chargePointVendor, - chargePointModel: payload.chargePointModel, - chargePointSerialNumber: payload.chargePointSerialNumber ?? null, - firmwareVersion: payload.firmwareVersion ?? null, - iccid: payload.iccid ?? null, - imsi: payload.imsi ?? null, - meterType: payload.meterType ?? null, - meterSerialNumber: payload.meterSerialNumber ?? null, - // New, unknown devices start as Pending — admin must manually accept them - registrationStatus: 'Pending', - heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL, - lastBootNotificationAt: dayjs().toDate(), - }) - .onConflictDoUpdate({ - target: chargePoint.chargePointIdentifier, - set: { - chargePointVendor: payload.chargePointVendor, - chargePointModel: payload.chargePointModel, - chargePointSerialNumber: payload.chargePointSerialNumber ?? null, - firmwareVersion: payload.firmwareVersion ?? null, - iccid: payload.iccid ?? null, - imsi: payload.imsi ?? null, - meterType: payload.meterType ?? null, - meterSerialNumber: payload.meterSerialNumber ?? null, - // Do NOT override registrationStatus — preserve whatever the admin set - heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL, - lastBootNotificationAt: dayjs().toDate(), - updatedAt: dayjs().toDate(), - }, - }) - .returning(), - BOOT_NOTIFICATION_DB_TIMEOUT_MS, - `BootNotification persistence for ${ctx.chargePointIdentifier}`, - ) + const status = cp.registrationStatus + ctx.isRegistered = status === 'Accepted' - const status = cp.registrationStatus - ctx.isRegistered = status === 'Accepted' + console.log(`[OCPP] BootNotification ${ctx.chargePointIdentifier} status=${status}`) - console.log(`[OCPP] BootNotification ${ctx.chargePointIdentifier} status=${status}`) - - return { - currentTime, - interval: DEFAULT_HEARTBEAT_INTERVAL, - status, - } - } catch (error) { - ctx.isRegistered = false - console.error(`[OCPP] BootNotification ${ctx.chargePointIdentifier} persistence failed:`, error) - - return { - currentTime, - interval: DEFAULT_HEARTBEAT_INTERVAL, - status: 'Pending', - } + return { + currentTime: dayjs().toISOString(), + interval: DEFAULT_HEARTBEAT_INTERVAL, + status, } }