mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 03:27:00 +08:00
154 lines
4.4 KiB
JavaScript
154 lines
4.4 KiB
JavaScript
const { fork } = require('node:child_process')
|
|
const { existsSync, mkdtempSync } = require('node:fs')
|
|
const { rm } = require('node:fs/promises')
|
|
const { tmpdir } = require('node:os')
|
|
const { join } = require('node:path')
|
|
const { randomUUID, createHash } = require('node:crypto')
|
|
|
|
const workerPath = join(__dirname, '..', 'out', 'main', 'knowledgeWorker.js')
|
|
if (!existsSync(workerPath)) throw new Error(`Knowledge worker build is missing: ${workerPath}`)
|
|
|
|
const root = mkdtempSync(join(tmpdir(), 'wxe-knowledge-worker-'))
|
|
const child = fork(workerPath, [], {
|
|
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
serialization: 'advanced',
|
|
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }
|
|
})
|
|
const pending = new Map()
|
|
|
|
function request(type, payload) {
|
|
const requestId = randomUUID()
|
|
return new Promise((resolve, reject) => {
|
|
pending.set(requestId, { resolve, reject })
|
|
child.send({ version: 1, type, requestId, payload }, (error) => {
|
|
if (error) reject(error)
|
|
})
|
|
})
|
|
}
|
|
|
|
child.on('message', (message) => {
|
|
if (!message || message.type === 'progress') return
|
|
const current = pending.get(message.requestId)
|
|
if (!current) return
|
|
pending.delete(message.requestId)
|
|
if (message.type === 'error') current.reject(new Error(message.error))
|
|
else current.resolve(message.payload)
|
|
})
|
|
|
|
function fts(profileId) {
|
|
return {
|
|
profileId,
|
|
tokenizer: 'trigram',
|
|
contentMode: 'external',
|
|
detail: 'full',
|
|
columnsize: 1
|
|
}
|
|
}
|
|
|
|
function conversation(accountId, id) {
|
|
return {
|
|
conversationId: `conversation-${id}`,
|
|
completeSnapshot: true,
|
|
messages: [
|
|
{
|
|
accountId,
|
|
conversationId: `conversation-${id}`,
|
|
messageId: `message-${id}`,
|
|
createTime: 1,
|
|
senderId: 'fixture-member',
|
|
senderName: '脱敏成员',
|
|
kind: 'text',
|
|
text: `脱敏索引内容 ${id}`
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
function accountPath(accountId) {
|
|
const key = createHash('sha256')
|
|
.update(`knowledge-account-v1:${accountId}`)
|
|
.digest('hex')
|
|
.slice(0, 32)
|
|
return join(root, key, 'knowledge.sqlite')
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
const chunker = {
|
|
version: 'conversation-v1',
|
|
maxGapMs: 600000,
|
|
maxMessages: 12,
|
|
maxCharacters: 1200,
|
|
overlapMessages: 3
|
|
}
|
|
const accountA = 'worker-fixture-a'
|
|
const accountB = 'worker-fixture-b'
|
|
const first = await request('index', {
|
|
accountId: accountA,
|
|
databaseRoot: root,
|
|
conversations: [conversation(accountA, 'a')],
|
|
chunker,
|
|
fts: fts('worker-a')
|
|
})
|
|
await request('index', {
|
|
accountId: accountB,
|
|
databaseRoot: root,
|
|
conversations: [conversation(accountB, 'b')],
|
|
chunker,
|
|
fts: fts('worker-b')
|
|
})
|
|
if (
|
|
!first ||
|
|
first.cancelled ||
|
|
!existsSync(accountPath(accountA)) ||
|
|
!existsSync(accountPath(accountB))
|
|
) {
|
|
throw new Error('Knowledge worker did not create isolated derived databases')
|
|
}
|
|
const search = await request('search', {
|
|
accountId: accountA,
|
|
databaseRoot: root,
|
|
fts: fts('worker-a'),
|
|
text: '查询脱敏索引内容 a',
|
|
terms: ['脱敏索引内容', 'a'],
|
|
limit: 10
|
|
})
|
|
const evidence = search?.evidence?.[0]
|
|
if (
|
|
search?.state !== 'ready' ||
|
|
!evidence ||
|
|
evidence.messageId !== 'message-a' ||
|
|
evidence.conversationId !== 'conversation-a' ||
|
|
evidence.sender !== '脱敏成员' ||
|
|
typeof evidence.timestamp !== 'number'
|
|
) {
|
|
throw new Error('Knowledge worker search did not return message-level evidence')
|
|
}
|
|
await request('remove', { accountId: accountA, databaseRoot: root })
|
|
if (existsSync(accountPath(accountA)) || !existsSync(accountPath(accountB))) {
|
|
throw new Error('Knowledge worker removal crossed an account boundary')
|
|
}
|
|
const unavailable = await request('search', {
|
|
accountId: accountA,
|
|
databaseRoot: root,
|
|
fts: fts('worker-a'),
|
|
text: '查询脱敏索引内容 a',
|
|
terms: ['脱敏索引内容'],
|
|
limit: 10
|
|
})
|
|
if (unavailable?.state !== 'unavailable' || unavailable.evidence?.length) {
|
|
throw new Error('Knowledge worker did not report unavailable index after removal')
|
|
}
|
|
await request('close', {})
|
|
console.log('Knowledge worker integration check passed')
|
|
} finally {
|
|
child.kill()
|
|
await rm(root, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error)
|
|
process.exitCode = 1
|
|
})
|