diff --git a/src/env.ts b/src/env.ts index ee87105..ab7a276 100644 --- a/src/env.ts +++ b/src/env.ts @@ -3,7 +3,7 @@ import { z } from 'zod' export const env = createEnv({ server: { - DATABASE_URL: z.url({ protocol: /^postgres(ql)?$/ }), + DATABASE_URL: z.url({ protocol: /^mysql$/ }), LOG_DB: z.stringbool().default(false), LOG_FORMAT: z.enum(['pretty', 'json']).optional(), LOG_LEVEL: z.enum(['trace', 'debug', 'info', 'warning', 'error', 'fatal']).default('info'), diff --git a/src/server/battery/mysql.ts b/src/server/battery/mysql.ts new file mode 100644 index 0000000..c33a2e4 --- /dev/null +++ b/src/server/battery/mysql.ts @@ -0,0 +1,75 @@ +import mysql, { type Pool, type RowDataPacket } from 'mysql2/promise' + +import { type BatteryInfo, type BatteryInfoSourceRow, toBatteryInfo } from '@/domain/battery' +import { env } from '@/env' + +const historyLimit = 500 + +type BatteryInfoMysqlRow = RowDataPacket & BatteryInfoSourceRow + +let pool: Pool | undefined + +function getBatteryPool() { + pool ??= mysql.createPool({ + uri: env.DATABASE_URL, + waitForConnections: true, + connectionLimit: 5, + namedPlaceholders: true, + }) + + return pool +} + +export async function closeBatteryPool() { + if (!pool) return + + await pool.end() + pool = undefined +} + +const sourceColumns = ` + id, + user_id AS userId, + mac, + dev_model AS devModel, + dev_name AS devName, + is_low_power AS isLowPower, + power_status AS powerStatus, + power, + create_time AS createTime, + remark +` + +export async function getBatteryHistory(mac: string): Promise { + const [rows] = await getBatteryPool().query( + ` + SELECT ${sourceColumns} + FROM ls_battery_info + WHERE mac = :mac + ORDER BY create_time DESC, id DESC + LIMIT :limit + `, + { mac, limit: historyLimit }, + ) + + return rows.map(toBatteryInfo) +} + +export async function getLatestBatteryPerDevice(): Promise { + const [rows] = await getBatteryPool().query(` + SELECT ${sourceColumns} + FROM ls_battery_info AS current_record + WHERE NOT EXISTS ( + SELECT 1 + FROM ls_battery_info AS newer_record + WHERE newer_record.mac = current_record.mac + AND ( + newer_record.create_time > current_record.create_time + OR (newer_record.create_time = current_record.create_time AND newer_record.id > current_record.id) + ) + ) + ORDER BY current_record.create_time DESC, current_record.id DESC + `) + + return rows.map(toBatteryInfo) +} diff --git a/src/server/plugins/shutdown.ts b/src/server/plugins/shutdown.ts index 8742f75..c78c48e 100644 --- a/src/server/plugins/shutdown.ts +++ b/src/server/plugins/shutdown.ts @@ -1,4 +1,4 @@ -import { db } from '@/server/db' +import { closeBatteryPool } from '@/server/battery/mysql' import { getLogger } from '@/server/logger' export default () => { @@ -17,8 +17,8 @@ export default () => { await Bun.sleep(500) try { - await db.$client.end() - logger.info('DB pool closed, exiting') + await closeBatteryPool() + logger.info('Battery MySQL pool closed, exiting') } catch (error) { logger.error('DB pool close failed during shutdown', { error }) }