103 lines
3.5 KiB
JavaScript
103 lines
3.5 KiB
JavaScript
/**
|
||
* 插件存储 worker(免打包纯 ESM,只依赖 node 内置模块;构建期拷到 out/main/ 旁)。
|
||
* 持有全部插件的 SQLite 连接(node:sqlite DatabaseSync),按消息逐个执行——
|
||
* 同步 API 全部留在本线程,主进程只做异步 RPC,插件查询永不阻塞主进程事件循环。
|
||
*
|
||
* 协议:主进程 → { reqId, op, ...payload };worker → { reqId, ok, result? | error? }。
|
||
* 首条消息必须是 init(下发 dataRoot,避免 worker 依赖 electron app 路径)。
|
||
*/
|
||
import { DatabaseSync } from 'node:sqlite'
|
||
import { mkdirSync } from 'node:fs'
|
||
import { join } from 'node:path'
|
||
import { parentPort } from 'node:worker_threads'
|
||
|
||
/** dataRoot(userData/plugin-data);由 init 消息下发 */
|
||
let dataRoot = null
|
||
|
||
/** pluginId → 已打开连接(跨消息复用;close/clearData 时移除) */
|
||
const dbs = new Map()
|
||
|
||
function openDb(pluginId) {
|
||
let db = dbs.get(pluginId)
|
||
if (db) return db
|
||
// pluginId 已由宿主按 manifest 规则校验(小写字母开头,字母/数字/连字符),可安全作路径段
|
||
mkdirSync(join(dataRoot, pluginId), { recursive: true })
|
||
db = new DatabaseSync(join(dataRoot, pluginId, 'data.db'))
|
||
db.exec('PRAGMA journal_mode = WAL;')
|
||
// 宿主管理的 KV 表:api.storage.kv 的底层(value JSON 编码)
|
||
db.exec('CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)')
|
||
dbs.set(pluginId, db)
|
||
return db
|
||
}
|
||
|
||
function handle(op, msg) {
|
||
switch (op) {
|
||
case 'init':
|
||
dataRoot = msg.dataRoot
|
||
return null
|
||
case 'close': {
|
||
const db = dbs.get(msg.pluginId)
|
||
if (db) {
|
||
dbs.delete(msg.pluginId)
|
||
db.close()
|
||
}
|
||
return null
|
||
}
|
||
case 'kvGet': {
|
||
const row = openDb(msg.pluginId).prepare('SELECT value FROM kv WHERE key = ?').get(msg.key)
|
||
return row ? JSON.parse(row.value) : null
|
||
}
|
||
case 'kvSet':
|
||
openDb(msg.pluginId)
|
||
.prepare('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value')
|
||
.run(msg.key, JSON.stringify(msg.value ?? null))
|
||
return null
|
||
case 'kvDelete':
|
||
openDb(msg.pluginId).prepare('DELETE FROM kv WHERE key = ?').run(msg.key)
|
||
return null
|
||
case 'kvKeys':
|
||
return openDb(msg.pluginId)
|
||
.prepare('SELECT key FROM kv ORDER BY key')
|
||
.all()
|
||
.map((r) => r.key)
|
||
case 'exec':
|
||
openDb(msg.pluginId).exec(msg.sql)
|
||
return null
|
||
case 'run':
|
||
openDb(msg.pluginId).prepare(msg.sql).run(...(msg.params ?? []))
|
||
return null
|
||
case 'query':
|
||
return openDb(msg.pluginId).prepare(msg.sql).all(...(msg.params ?? []))
|
||
case 'batch': {
|
||
const db = openDb(msg.pluginId)
|
||
db.exec('BEGIN')
|
||
try {
|
||
for (const st of msg.statements ?? []) db.prepare(st.sql).run(...(st.params ?? []))
|
||
db.exec('COMMIT')
|
||
} catch (err) {
|
||
try {
|
||
db.exec('ROLLBACK')
|
||
} catch {
|
||
// 连接已坏时 ROLLBACK 自身会失败——原始错误优先
|
||
}
|
||
throw err
|
||
}
|
||
return null
|
||
}
|
||
default:
|
||
throw new Error(`未知存储操作: ${op}`)
|
||
}
|
||
}
|
||
|
||
parentPort.on('message', (msg) => {
|
||
if (!dataRoot && msg.op !== 'init') {
|
||
parentPort.postMessage({ reqId: msg.reqId, ok: false, error: '存储 worker 未初始化' })
|
||
return
|
||
}
|
||
try {
|
||
parentPort.postMessage({ reqId: msg.reqId, ok: true, result: handle(msg.op, msg) ?? null })
|
||
} catch (err) {
|
||
parentPort.postMessage({ reqId: msg.reqId, ok: false, error: err instanceof Error ? err.message : String(err) })
|
||
}
|
||
})
|