Files
clawpanel/src/lib/message-db.js
晴天 3705ab8081 feat: 聊天页面 + Agent 管理 + 模型排序固化 + 多项增强
- 新增聊天页面(WebSocket 实时通信、会话管理、Markdown 渲染)
- 新增 Agent 管理页面(CRUD、备份、工作区配置、模型选择)
- 新增设备认证模块(Ed25519 签名)
- 模型排序固化到配置文件(拖拽排序 + 下拉排序均写入底层数据)
- 记忆文件页面支持 Agent 切换
- Gateway 配置页重构为选项卡片布局
- 页面路由加载动画
- 批量测试支持终止、实时刷新卡片
- 服务管理备份说明文案
- Modal 组件支持 readonly 字段
- 脱敏 mock 数据中的内部地址
2026-03-01 12:19:21 +08:00

113 lines
3.5 KiB
JavaScript

/**
* 本地消息存储 - IndexedDB
* 从 clawapp 移植,适配 ClawPanel
*/
const DB_NAME = 'clawpanel-messages'
const DB_VERSION = 1
const STORE_NAME = 'messages'
const STORE_SESSIONS = 'sessions'
let _db = null
function openDB() {
return new Promise((resolve, reject) => {
if (_db) return resolve(_db)
const request = indexedDB.open(DB_NAME, DB_VERSION)
request.onerror = () => reject(request.error)
request.onsuccess = () => { _db = request.result; resolve(_db) }
request.onupgradeneeded = (event) => {
const db = event.target.result
if (!db.objectStoreNames.contains(STORE_NAME)) {
const msgStore = db.createObjectStore(STORE_NAME, { keyPath: 'id' })
msgStore.createIndex('sessionKey', 'sessionKey', { unique: false })
msgStore.createIndex('timestamp', 'timestamp', { unique: false })
msgStore.createIndex('sessionKey_timestamp', ['sessionKey', 'timestamp'], { unique: false })
}
if (!db.objectStoreNames.contains(STORE_SESSIONS)) {
db.createObjectStore(STORE_SESSIONS, { keyPath: 'sessionKey' })
}
}
})
}
export async function saveMessage(message) {
if (!message || !message.id) return
try {
const db = await openDB()
const tx = db.transaction(STORE_NAME, 'readwrite')
tx.objectStore(STORE_NAME).put({
id: message.id,
sessionKey: message.sessionKey || '',
role: message.role || 'assistant',
content: message.content || message.text || '',
timestamp: message.timestamp || Date.now(),
sync: true
})
} catch (e) {
console.error('[db] saveMessage error:', e)
}
}
export async function saveMessages(messages) {
if (!messages?.length) return
try {
const db = await openDB()
const tx = db.transaction(STORE_NAME, 'readwrite')
const store = tx.objectStore(STORE_NAME)
messages.forEach(msg => {
if (!msg.id) return
store.put({
id: msg.id,
sessionKey: msg.sessionKey || '',
role: msg.role || 'assistant',
content: msg.content || msg.text || '',
timestamp: msg.timestamp || Date.now(),
sync: true
})
})
} catch (e) {
console.error('[db] saveMessages error:', e)
}
}
export async function getLocalMessages(sessionKey, limit = 200) {
try {
const db = await openDB()
return new Promise((resolve) => {
const tx = db.transaction(STORE_NAME, 'readonly')
const index = tx.objectStore(STORE_NAME).index('sessionKey_timestamp')
const range = IDBKeyRange.bound([sessionKey, 0], [sessionKey, Date.now() + 1])
const messages = []
const request = index.openCursor(range, 'prev')
request.onsuccess = (event) => {
const cursor = event.target.result
if (cursor && messages.length < limit) { messages.push(cursor.value); cursor.continue() }
}
tx.oncomplete = () => resolve(messages.reverse())
tx.onerror = () => resolve([])
})
} catch (e) {
console.error('[db] getLocalMessages error:', e)
return []
}
}
export async function clearSessionMessages(sessionKey) {
try {
const db = await openDB()
const tx = db.transaction(STORE_NAME, 'readwrite')
const request = tx.objectStore(STORE_NAME).index('sessionKey').openCursor(sessionKey)
request.onsuccess = (event) => {
const cursor = event.target.result
if (cursor) { cursor.delete(); cursor.continue() }
}
} catch (e) {
console.error('[db] clearSessionMessages error:', e)
}
}
export function isStorageAvailable() {
try { return 'indexedDB' in window && !!indexedDB } catch { return false }
}