1717 autoUpdaterLoadError = err ;
1818}
1919const { spawn, spawnSync } = require ( "child_process" ) ;
20+ const crypto = require ( "crypto" ) ;
2021const fs = require ( "fs" ) ;
2122const http = require ( "http" ) ;
2223const net = require ( "net" ) ;
@@ -33,6 +34,10 @@ const DEFAULT_BACKEND_HOST = String(process.env.WECHAT_TOOL_HOST || "127.0.0.1")
3334const DEFAULT_BACKEND_PORT = parsePort ( process . env . WECHAT_TOOL_PORT ) ?? 10392 ;
3435
3536let backendProc = null ;
37+ let wcdbSidecarProc = null ;
38+ let wcdbSidecarPort = null ;
39+ let wcdbSidecarUrl = "" ;
40+ let wcdbSidecarToken = "" ;
3641let resolvedDataDir = null ;
3742let mainWindow = null ;
3843let tray = null ;
@@ -1452,8 +1457,159 @@ function getPackagedWcdbDllPath() {
14521457 return path . join ( process . resourcesPath , "backend" , "native" , "wcdb_api.dll" ) ;
14531458}
14541459
1460+ function getDevWcdbDllPath ( ) {
1461+ return path . join ( repoRoot ( ) , "src" , "wechat_decrypt_tool" , "native" , "wcdb_api.dll" ) ;
1462+ }
1463+
1464+ function getWcdbDllPath ( ) {
1465+ return app . isPackaged ? getPackagedWcdbDllPath ( ) : getDevWcdbDllPath ( ) ;
1466+ }
1467+
1468+ function getWcdbDllDir ( ) {
1469+ return path . dirname ( getWcdbDllPath ( ) ) ;
1470+ }
1471+
1472+ function getWcdbSidecarScriptPath ( ) {
1473+ return app . isPackaged
1474+ ? path . join ( process . resourcesPath , "wcdb-sidecar.cjs" )
1475+ : path . join ( __dirname , "wcdb-sidecar.cjs" ) ;
1476+ }
1477+
1478+ function getKoffiDir ( ) {
1479+ return app . isPackaged ? path . join ( process . resourcesPath , "koffi" ) : path . join ( repoRoot ( ) , "desktop" , "vendor" , "koffi" ) ;
1480+ }
1481+
1482+ function getWcdbSidecarStdioLogPath ( dataDir ) {
1483+ return path . join ( dataDir , "wcdb-sidecar-stdio.log" ) ;
1484+ }
1485+
1486+ function getWcdbSidecarPort ( ) {
1487+ if ( parsePort ( wcdbSidecarPort ) != null ) return wcdbSidecarPort ;
1488+ const envPort = parsePort ( process . env . WECHAT_TOOL_WCDB_SIDECAR_PORT ) ;
1489+ wcdbSidecarPort = envPort ?? Math . min ( 65535 , getBackendPort ( ) + 101 ) ;
1490+ return wcdbSidecarPort ;
1491+ }
1492+
1493+ async function prepareWcdbSidecarPort ( ) {
1494+ if ( parsePort ( wcdbSidecarPort ) != null ) return wcdbSidecarPort ;
1495+ const envPort = parsePort ( process . env . WECHAT_TOOL_WCDB_SIDECAR_PORT ) ;
1496+ if ( envPort != null ) {
1497+ wcdbSidecarPort = envPort ;
1498+ return wcdbSidecarPort ;
1499+ }
1500+ const preferred = Math . min ( 65535 , getBackendPort ( ) + 101 ) ;
1501+ wcdbSidecarPort = await chooseAvailablePort ( preferred , "127.0.0.1" ) ;
1502+ if ( wcdbSidecarPort == null ) wcdbSidecarPort = preferred ;
1503+ return wcdbSidecarPort ;
1504+ }
1505+
1506+ function getWcdbResourcePaths ( ) {
1507+ const out = [ ] ;
1508+ const seen = new Set ( ) ;
1509+
1510+ const add = ( value ) => {
1511+ const raw = String ( value || "" ) . trim ( ) ;
1512+ if ( ! raw ) return ;
1513+ const resolved = path . resolve ( raw ) ;
1514+ const key = resolved . toLowerCase ( ) ;
1515+ if ( seen . has ( key ) ) return ;
1516+ seen . add ( key ) ;
1517+ out . push ( resolved ) ;
1518+ } ;
1519+
1520+ const dllDir = getWcdbDllDir ( ) ;
1521+ add ( dllDir ) ;
1522+ add ( path . dirname ( dllDir ) ) ;
1523+ add ( repoRoot ( ) ) ;
1524+ add ( path . join ( repoRoot ( ) , "resources" ) ) ;
1525+ const dataDir = resolveDataDir ( ) ;
1526+ if ( dataDir ) {
1527+ add ( dataDir ) ;
1528+ add ( path . join ( dataDir , "resources" ) ) ;
1529+ }
1530+ return out ;
1531+ }
1532+
1533+ function ensureWcdbSidecarEnv ( env ) {
1534+ if ( ! wcdbSidecarUrl || ! wcdbSidecarToken ) return env ;
1535+ env . WECHAT_TOOL_WCDB_SIDECAR_URL = wcdbSidecarUrl ;
1536+ env . WECHAT_TOOL_WCDB_SIDECAR_TOKEN = wcdbSidecarToken ;
1537+ return env ;
1538+ }
1539+
1540+ function startWcdbSidecar ( ) {
1541+ if ( process . env . WECHAT_TOOL_WCDB_SIDECAR === "0" ) return null ;
1542+ if ( wcdbSidecarProc && wcdbSidecarProc . exitCode == null ) return wcdbSidecarProc ;
1543+ if ( process . platform !== "win32" ) return null ;
1544+
1545+ const dllPath = getWcdbDllPath ( ) ;
1546+ const sidecarScript = getWcdbSidecarScriptPath ( ) ;
1547+ const koffiDir = getKoffiDir ( ) ;
1548+ if ( ! fs . existsSync ( dllPath ) ) {
1549+ logMain ( `[wcdb-sidecar] skip: missing wcdb_api.dll ${ dllPath } ` ) ;
1550+ return null ;
1551+ }
1552+ if ( ! fs . existsSync ( sidecarScript ) ) {
1553+ logMain ( `[wcdb-sidecar] skip: missing sidecar script ${ sidecarScript } ` ) ;
1554+ return null ;
1555+ }
1556+ if ( ! fs . existsSync ( koffiDir ) ) {
1557+ logMain ( `[wcdb-sidecar] skip: missing koffi runtime ${ koffiDir } ` ) ;
1558+ return null ;
1559+ }
1560+
1561+ const port = getWcdbSidecarPort ( ) ;
1562+ const host = "127.0.0.1" ;
1563+ wcdbSidecarUrl = `http://${ host } :${ port } ` ;
1564+ wcdbSidecarToken = wcdbSidecarToken || crypto . randomBytes ( 24 ) . toString ( "hex" ) ;
1565+
1566+ const env = {
1567+ ...process . env ,
1568+ ELECTRON_RUN_AS_NODE : "1" ,
1569+ WECHAT_TOOL_WCDB_SIDECAR_HOST : host ,
1570+ WECHAT_TOOL_WCDB_SIDECAR_PORT : String ( port ) ,
1571+ WECHAT_TOOL_WCDB_SIDECAR_TOKEN : wcdbSidecarToken ,
1572+ WECHAT_TOOL_WCDB_API_DLL_PATH : dllPath ,
1573+ WECHAT_TOOL_WCDB_DLL_DIR : getWcdbDllDir ( ) ,
1574+ WECHAT_TOOL_WCDB_RESOURCE_PATHS : JSON . stringify ( getWcdbResourcePaths ( ) ) ,
1575+ WECHAT_TOOL_KOFFI_DIR : koffiDir ,
1576+ } ;
1577+
1578+ logMain ( `[wcdb-sidecar] starting url=${ wcdbSidecarUrl } dll=${ dllPath } ` ) ;
1579+ wcdbSidecarProc = spawn ( process . execPath , [ sidecarScript ] , {
1580+ cwd : path . dirname ( sidecarScript ) ,
1581+ env,
1582+ stdio : [ "ignore" , "pipe" , "pipe" ] ,
1583+ windowsHide : true ,
1584+ } ) ;
1585+
1586+ const dataDir = resolveDataDir ( ) || getUserDataDir ( ) || repoRoot ( ) ;
1587+ attachBackendStdio ( wcdbSidecarProc , getWcdbSidecarStdioLogPath ( dataDir ) ) ;
1588+
1589+ const proc = wcdbSidecarProc ;
1590+ proc . on ( "exit" , ( code , signal ) => {
1591+ if ( wcdbSidecarProc === proc ) wcdbSidecarProc = null ;
1592+ logMain ( `[wcdb-sidecar] exited code=${ code } signal=${ signal } ` ) ;
1593+ } ) ;
1594+
1595+ process . env . WECHAT_TOOL_WCDB_SIDECAR_URL = wcdbSidecarUrl ;
1596+ process . env . WECHAT_TOOL_WCDB_SIDECAR_TOKEN = wcdbSidecarToken ;
1597+ return wcdbSidecarProc ;
1598+ }
1599+
1600+ function stopWcdbSidecar ( ) {
1601+ if ( ! wcdbSidecarProc ) return ;
1602+ const pid = wcdbSidecarProc . pid ;
1603+ logMain ( `[wcdb-sidecar] stop pid=${ pid || "?" } ` ) ;
1604+ try {
1605+ wcdbSidecarProc . kill ( ) ;
1606+ } catch { }
1607+ wcdbSidecarProc = null ;
1608+ }
1609+
14551610function startBackend ( ) {
14561611 if ( backendProc ) return backendProc ;
1612+ startWcdbSidecar ( ) ;
14571613
14581614 const env = {
14591615 ...process . env ,
@@ -1462,6 +1618,7 @@ function startBackend() {
14621618 // Make sure Python prints UTF-8 to stdout/stderr.
14631619 PYTHONIOENCODING : process . env . PYTHONIOENCODING || "utf-8" ,
14641620 } ;
1621+ ensureWcdbSidecarEnv ( env ) ;
14651622
14661623 // In packaged mode we expect to provide the generated Nuxt output dir via env.
14671624 if ( app . isPackaged && ! env . WECHAT_TOOL_UI_DIR ) {
@@ -2135,6 +2292,7 @@ async function main() {
21352292 await applyPendingOutputDirOnStartup ( ) ;
21362293 ensureOutputLink ( ) ;
21372294 await ensureBackendPortAvailableOnStartup ( ) ;
2295+ await prepareWcdbSidecarPort ( ) ;
21382296
21392297 logMain ( `[main] app.isPackaged=${ app . isPackaged } argv=${ JSON . stringify ( process . argv ) } ` ) ;
21402298
@@ -2190,6 +2348,7 @@ async function main() {
21902348
21912349app . on ( "window-all-closed" , ( ) => {
21922350 stopBackend ( ) ;
2351+ stopWcdbSidecar ( ) ;
21932352 if ( process . platform !== "darwin" ) app . quit ( ) ;
21942353} ) ;
21952354
@@ -2203,6 +2362,7 @@ app.on("before-quit", () => {
22032362 isQuitting = true ;
22042363 destroyTray ( ) ;
22052364 stopBackend ( ) ;
2365+ stopWcdbSidecar ( ) ;
22062366} ) ;
22072367
22082368if ( gotSingleInstanceLock ) {
@@ -2211,6 +2371,7 @@ if (gotSingleInstanceLock) {
22112371 console . error ( err ) ;
22122372 logMain ( `[main] fatal: ${ err ?. stack || String ( err ) } ` ) ;
22132373 stopBackend ( ) ;
2374+ stopWcdbSidecar ( ) ;
22142375 try {
22152376 const dir = getUserDataDir ( ) ;
22162377 const outputDir = resolveOutputDir ( ) ;
0 commit comments