Files
LagZero/src/utils/latency-session.ts
T
ZenEcho c2bb7e6ed8 feat: 重构前端代码结构并增强类型定义
- 新增 API 模块统一封装所有 IPC 调用,提高代码可维护性
- 创建 `src/types/index.ts` 集中定义核心业务类型(Game, NodeConfig 等)
- 新增组合式函数 `useFilePicker`、`useGameScanner` 和 `useAppUpdater` 以复用逻辑
- 改进设置页面,新增侧边栏导航和多个设置模块(通用、网络、日志、关于)
- 优化本地代理逻辑,增加节点列表变化监听和自动重启机制
- 扩展协议解析测试用例,覆盖更多 VMess、VLESS Reality 等场景
- 更新项目结构文档 `docs/src-structure.md` 说明模块职责
- 修复 Electron 主进程预加载路径兼容性问题
2026-02-19 20:16:55 +08:00

127 lines
4.0 KiB
TypeScript

import type { LatencyRecord } from '@/types'
const DB_NAME = 'lagzero-latency-session'
const DB_VERSION = 2
const STORE_NAME = 'latency_records'
let dbPromise: Promise<IDBDatabase> | null = null
let initialized = false
function openDb(): Promise<IDBDatabase> {
if (dbPromise) return dbPromise
dbPromise = new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION)
request.onupgradeneeded = () => {
const db = request.result
let store: IDBObjectStore
if (!db.objectStoreNames.contains(STORE_NAME)) {
store = db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true })
} else {
const tx = request.transaction
if (!tx) return
store = tx.objectStore(STORE_NAME)
}
if (!store.indexNames.contains('nodeKey')) {
store.createIndex('nodeKey', 'nodeKey', { unique: false })
}
if (!store.indexNames.contains('timestamp')) {
store.createIndex('timestamp', 'timestamp', { unique: false })
}
if (!store.indexNames.contains('nodeKey_timestamp')) {
store.createIndex('nodeKey_timestamp', ['nodeKey', 'timestamp'], { unique: false })
}
if (!store.indexNames.contains('gameId_timestamp')) {
store.createIndex('gameId_timestamp', ['gameId', 'timestamp'], { unique: false })
}
}
request.onsuccess = () => resolve(request.result)
request.onerror = () => reject(request.error ?? new Error('Failed to open latency session DB'))
})
return dbPromise
}
function txDone(tx: IDBTransaction): Promise<void> {
return new Promise((resolve, reject) => {
tx.oncomplete = () => resolve()
tx.onerror = () => reject(tx.error ?? new Error('IndexedDB transaction failed'))
tx.onabort = () => reject(tx.error ?? new Error('IndexedDB transaction aborted'))
})
}
export async function initLatencySessionStore() {
const db = await openDb()
if (initialized) return
const tx = db.transaction(STORE_NAME, 'readwrite')
tx.objectStore(STORE_NAME).clear()
await txDone(tx)
initialized = true
}
export async function appendLatencyRecord(record: LatencyRecord) {
const db = await openDb()
const tx = db.transaction(STORE_NAME, 'readwrite')
tx.objectStore(STORE_NAME).add(record)
await txDone(tx)
}
export async function getRecentLatencyRecords(nodeKey: string, limit = 240): Promise<LatencyRecord[]> {
if (!nodeKey || limit <= 0) return []
const db = await openDb()
const tx = db.transaction(STORE_NAME, 'readonly')
const index = tx.objectStore(STORE_NAME).index('nodeKey_timestamp')
const range = IDBKeyRange.bound([nodeKey, 0], [nodeKey, Number.MAX_SAFE_INTEGER])
return new Promise((resolve, reject) => {
const records: LatencyRecord[] = []
const req = index.openCursor(range, 'prev')
req.onsuccess = () => {
const cursor = req.result
if (!cursor || records.length >= limit) {
resolve(records.reverse())
return
}
records.push(cursor.value as LatencyRecord)
cursor.continue()
}
req.onerror = () => reject(req.error ?? new Error('Failed to read latency records'))
})
}
export async function getGameLatencyStats(gameId: string): Promise<{ total: number; lost: number }> {
if (!gameId) return { total: 0, lost: 0 }
const db = await openDb()
const tx = db.transaction(STORE_NAME, 'readonly')
const index = tx.objectStore(STORE_NAME).index('gameId_timestamp')
const range = IDBKeyRange.bound([gameId, 0], [gameId, Number.MAX_SAFE_INTEGER])
return new Promise((resolve, reject) => {
let total = 0
let lost = 0
const req = index.openCursor(range, 'next')
req.onsuccess = () => {
const cursor = req.result
if (!cursor) {
resolve({ total, lost })
return
}
const row = cursor.value as LatencyRecord
total += 1
if (row.loss > 0 || row.latency <= 0) {
lost += 1
}
cursor.continue()
}
req.onerror = () => reject(req.error ?? new Error('Failed to read game latency stats'))
})
}