Update package.json for version bump to 2.0.3, add postinstall script, and include new ssh2 dependency. Enhance main.js with SSH tunnel management for database connections, including creation and closure of tunnels, and update connection handling to support SSH-enabled configurations.
This commit is contained in:
parent
d5546b524c
commit
5591081812
377
electron/main.js
377
electron/main.js
@ -3,6 +3,7 @@ import path from 'path'
|
|||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import net from 'net'
|
||||||
import mysql from 'mysql2/promise'
|
import mysql from 'mysql2/promise'
|
||||||
import pg from 'pg'
|
import pg from 'pg'
|
||||||
import initSqlJs from 'sql.js'
|
import initSqlJs from 'sql.js'
|
||||||
@ -10,16 +11,130 @@ import { MongoClient } from 'mongodb'
|
|||||||
import Redis from 'ioredis'
|
import Redis from 'ioredis'
|
||||||
import mssql from 'mssql'
|
import mssql from 'mssql'
|
||||||
import Blowfish from 'blowfish-node'
|
import Blowfish from 'blowfish-node'
|
||||||
|
import { Client as SSHClient } from 'ssh2'
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
const __dirname = path.dirname(__filename)
|
const __dirname = path.dirname(__filename)
|
||||||
|
|
||||||
// 存储活动的数据库连接
|
// 存储活动的数据库连接
|
||||||
const connections = new Map()
|
const connections = new Map()
|
||||||
|
// 存储活动的 SSH 隧道
|
||||||
|
const sshTunnels = new Map()
|
||||||
// 配置文件路径
|
// 配置文件路径
|
||||||
const configPath = path.join(app.getPath('userData'), 'connections.json')
|
const configPath = path.join(app.getPath('userData'), 'connections.json')
|
||||||
// SQL.js 初始化
|
// SQL.js 初始化
|
||||||
let SQL = null
|
let SQL = null
|
||||||
|
// 用于分配本地端口
|
||||||
|
let nextLocalPort = 33060
|
||||||
|
|
||||||
|
// ============ SSH 隧道管理 ============
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 SSH 隧道
|
||||||
|
* @param {Object} config - 连接配置
|
||||||
|
* @returns {Promise<{ssh, server, localPort, localHost}>}
|
||||||
|
*/
|
||||||
|
async function createSSHTunnel(config) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const ssh = new SSHClient()
|
||||||
|
const localPort = nextLocalPort++
|
||||||
|
|
||||||
|
// 端口范围重置
|
||||||
|
if (nextLocalPort > 65000) nextLocalPort = 33060
|
||||||
|
|
||||||
|
let server = null
|
||||||
|
let connected = false
|
||||||
|
|
||||||
|
ssh.on('ready', () => {
|
||||||
|
console.log(`[SSH] 连接成功: ${config.sshUser}@${config.sshHost}:${config.sshPort}`)
|
||||||
|
connected = true
|
||||||
|
|
||||||
|
// 创建本地 TCP 服务器进行端口转发
|
||||||
|
server = net.createServer((socket) => {
|
||||||
|
ssh.forwardOut(
|
||||||
|
'127.0.0.1', localPort,
|
||||||
|
config.host, config.port,
|
||||||
|
(err, stream) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('[SSH] 转发失败:', err.message)
|
||||||
|
socket.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socket.pipe(stream).pipe(socket)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(localPort, '127.0.0.1', () => {
|
||||||
|
console.log(`[SSH] 隧道就绪: localhost:${localPort} → ${config.host}:${config.port}`)
|
||||||
|
resolve({ ssh, server, localPort, localHost: '127.0.0.1' })
|
||||||
|
})
|
||||||
|
|
||||||
|
server.on('error', (err) => {
|
||||||
|
console.error('[SSH] 本地服务器错误:', err.message)
|
||||||
|
ssh.end()
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ssh.on('error', (err) => {
|
||||||
|
console.error('[SSH] 连接错误:', err.message)
|
||||||
|
if (!connected) reject(new Error(`SSH 连接失败: ${err.message}`))
|
||||||
|
})
|
||||||
|
|
||||||
|
ssh.on('close', () => {
|
||||||
|
console.log('[SSH] 连接已关闭')
|
||||||
|
if (server) server.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 构建 SSH 配置
|
||||||
|
const sshConfig = {
|
||||||
|
host: config.sshHost,
|
||||||
|
port: config.sshPort || 22,
|
||||||
|
username: config.sshUser,
|
||||||
|
readyTimeout: 10000,
|
||||||
|
keepaliveInterval: 10000,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码认证
|
||||||
|
if (config.sshPassword) {
|
||||||
|
sshConfig.password = config.sshPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
// 私钥认证
|
||||||
|
if (config.sshKey) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(config.sshKey)) {
|
||||||
|
sshConfig.privateKey = fs.readFileSync(config.sshKey)
|
||||||
|
} else if (config.sshKey.includes('-----BEGIN')) {
|
||||||
|
sshConfig.privateKey = config.sshKey
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[SSH] 读取私钥失败:', e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[SSH] 正在连接: ${config.sshUser}@${config.sshHost}:${config.sshPort}`)
|
||||||
|
ssh.connect(sshConfig)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭 SSH 隧道
|
||||||
|
*/
|
||||||
|
function closeSSHTunnel(tunnelId) {
|
||||||
|
const tunnel = sshTunnels.get(tunnelId)
|
||||||
|
if (tunnel) {
|
||||||
|
try {
|
||||||
|
if (tunnel.server) tunnel.server.close()
|
||||||
|
if (tunnel.ssh) tunnel.ssh.end()
|
||||||
|
console.log(`[SSH] 隧道已关闭: ${tunnelId}`)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[SSH] 关闭隧道失败:', e.message)
|
||||||
|
}
|
||||||
|
sshTunnels.delete(tunnelId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mainWindow
|
let mainWindow
|
||||||
|
|
||||||
@ -91,16 +206,25 @@ app.whenReady().then(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
// 关闭所有数据库连接
|
// 关闭所有数据库连接和 SSH 隧道
|
||||||
for (const [id, connInfo] of connections) {
|
for (const [id, connInfo] of connections) {
|
||||||
try {
|
try {
|
||||||
closeConnection(connInfo.connection, connInfo.type)
|
closeConnection(connInfo.connection, connInfo.type, id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('关闭连接失败:', e)
|
console.error('关闭连接失败:', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connections.clear()
|
connections.clear()
|
||||||
|
|
||||||
|
// 清理残留的 SSH 隧道
|
||||||
|
for (const [id, tunnel] of sshTunnels) {
|
||||||
|
try {
|
||||||
|
if (tunnel.server) tunnel.server.close()
|
||||||
|
if (tunnel.ssh) tunnel.ssh.end()
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
sshTunnels.clear()
|
||||||
|
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
@ -150,9 +274,10 @@ ipcMain.handle('config:save', async (event, connectionsList) => {
|
|||||||
// ============ 数据库操作 ============
|
// ============ 数据库操作 ============
|
||||||
ipcMain.handle('db:test', async (event, config) => {
|
ipcMain.handle('db:test', async (event, config) => {
|
||||||
try {
|
try {
|
||||||
const conn = await createConnection(config)
|
const conn = await createConnection(config, null)
|
||||||
await closeConnection(conn, config.type)
|
await closeConnection(conn, config.type, null)
|
||||||
return { success: true, message: '连接成功' }
|
const msg = config.sshEnabled ? '通过 SSH 隧道连接成功' : '连接成功'
|
||||||
|
return { success: true, message: msg }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { success: false, message: e.message }
|
return { success: false, message: e.message }
|
||||||
}
|
}
|
||||||
@ -160,9 +285,10 @@ ipcMain.handle('db:test', async (event, config) => {
|
|||||||
|
|
||||||
ipcMain.handle('db:connect', async (event, config) => {
|
ipcMain.handle('db:connect', async (event, config) => {
|
||||||
try {
|
try {
|
||||||
const conn = await createConnection(config)
|
const conn = await createConnection(config, config.id)
|
||||||
connections.set(config.id, { connection: conn, type: config.type, config })
|
connections.set(config.id, { connection: conn, type: config.type, config })
|
||||||
return { success: true, message: '连接成功' }
|
const msg = config.sshEnabled ? '通过 SSH 隧道连接成功' : '连接成功'
|
||||||
|
return { success: true, message: msg }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { success: false, message: e.message }
|
return { success: false, message: e.message }
|
||||||
}
|
}
|
||||||
@ -171,7 +297,7 @@ ipcMain.handle('db:connect', async (event, config) => {
|
|||||||
ipcMain.handle('db:disconnect', async (event, id) => {
|
ipcMain.handle('db:disconnect', async (event, id) => {
|
||||||
const connInfo = connections.get(id)
|
const connInfo = connections.get(id)
|
||||||
if (connInfo) {
|
if (connInfo) {
|
||||||
await closeConnection(connInfo.connection, connInfo.type)
|
await closeConnection(connInfo.connection, connInfo.type, id)
|
||||||
connections.delete(id)
|
connections.delete(id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -222,15 +348,16 @@ async function ensureConnection(id) {
|
|||||||
if (!alive && connInfo.config) {
|
if (!alive && connInfo.config) {
|
||||||
console.log(`连接 ${id} 已断开,尝试重新连接...`)
|
console.log(`连接 ${id} 已断开,尝试重新连接...`)
|
||||||
try {
|
try {
|
||||||
// 尝试关闭旧连接(忽略错误)
|
// 尝试关闭旧连接和 SSH 隧道
|
||||||
try {
|
try {
|
||||||
await closeConnection(connInfo.connection, connInfo.type)
|
await closeConnection(connInfo.connection, connInfo.type, id)
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// 重新建立连接
|
// 重新建立连接(包括 SSH 隧道)
|
||||||
const newConn = await createConnection(connInfo.config)
|
const newConn = await createConnection(connInfo.config, id)
|
||||||
connections.set(id, { connection: newConn, type: connInfo.type, config: connInfo.config })
|
connections.set(id, { connection: newConn, type: connInfo.type, config: connInfo.config })
|
||||||
console.log(`连接 ${id} 重新连接成功`)
|
const sshNote = connInfo.config.sshEnabled ? '(通过 SSH 隧道)' : ''
|
||||||
|
console.log(`连接 ${id} 重新连接成功${sshNote}`)
|
||||||
return connections.get(id)
|
return connections.get(id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`连接 ${id} 重新连接失败:`, e.message)
|
console.error(`连接 ${id} 重新连接失败:`, e.message)
|
||||||
@ -1139,101 +1266,148 @@ function navicatXorDecrypt(encryptedBuffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============ 数据库连接辅助函数 ============
|
// ============ 数据库连接辅助函数 ============
|
||||||
async function createConnection(config) {
|
async function createConnection(config, connectionId = null) {
|
||||||
const { type, host, port, username, password, database } = config
|
let { type, host, port, username, password, database } = config
|
||||||
|
const originalHost = host // 保存原始 host(SQLite 需要用)
|
||||||
switch (type) {
|
|
||||||
case 'mysql':
|
// 如果启用了 SSH 隧道,先建立隧道
|
||||||
case 'mariadb':
|
let tunnel = null
|
||||||
return await mysql.createConnection({
|
if (config.sshEnabled && config.sshHost) {
|
||||||
host,
|
console.log(`[DB] 为连接创建 SSH 隧道...`)
|
||||||
port,
|
try {
|
||||||
user: username,
|
tunnel = await createSSHTunnel(config)
|
||||||
password,
|
host = tunnel.localHost
|
||||||
database: database || undefined,
|
port = tunnel.localPort
|
||||||
connectTimeout: 10000,
|
|
||||||
dateStrings: true
|
|
||||||
})
|
|
||||||
|
|
||||||
case 'postgresql':
|
|
||||||
case 'postgres': {
|
|
||||||
const client = new pg.Client({
|
|
||||||
host,
|
|
||||||
port,
|
|
||||||
user: username,
|
|
||||||
password,
|
|
||||||
database: database || 'postgres',
|
|
||||||
connectionTimeoutMillis: 10000
|
|
||||||
})
|
|
||||||
await client.connect()
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'sqlite': {
|
|
||||||
await initSqlite()
|
|
||||||
const dbPath = host || database
|
|
||||||
let dbData = null
|
|
||||||
|
|
||||||
if (dbPath && fs.existsSync(dbPath)) {
|
// 保存隧道(正式连接时)
|
||||||
dbData = fs.readFileSync(dbPath)
|
if (connectionId) {
|
||||||
|
sshTunnels.set(connectionId, tunnel)
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
const db = new SQL.Database(dbData)
|
throw new Error(`SSH 隧道失败: ${e.message}`)
|
||||||
db._path = dbPath
|
|
||||||
return db
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 'mongodb': {
|
try {
|
||||||
const uri = username && password
|
let conn
|
||||||
? `mongodb://${username}:${password}@${host}:${port}/${database || 'admin'}?authSource=admin`
|
|
||||||
: `mongodb://${host}:${port}/${database || 'admin'}`
|
switch (type) {
|
||||||
const client = new MongoClient(uri, {
|
case 'mysql':
|
||||||
serverSelectionTimeoutMS: 10000,
|
case 'mariadb':
|
||||||
connectTimeoutMS: 10000
|
conn = await mysql.createConnection({
|
||||||
})
|
host,
|
||||||
await client.connect()
|
port,
|
||||||
client._database = database || 'admin'
|
user: username,
|
||||||
return client
|
password,
|
||||||
}
|
database: database || undefined,
|
||||||
|
connectTimeout: 10000,
|
||||||
|
dateStrings: true
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
case 'redis': {
|
case 'postgresql':
|
||||||
const redis = new Redis({
|
case 'postgres': {
|
||||||
host,
|
const client = new pg.Client({
|
||||||
port,
|
host,
|
||||||
password: password || undefined,
|
port,
|
||||||
db: parseInt(database) || 0,
|
user: username,
|
||||||
connectTimeout: 10000,
|
password,
|
||||||
lazyConnect: true
|
database: database || 'postgres',
|
||||||
})
|
connectionTimeoutMillis: 10000
|
||||||
await redis.connect()
|
})
|
||||||
return redis
|
await client.connect()
|
||||||
}
|
conn = client
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case 'sqlserver': {
|
case 'sqlite': {
|
||||||
const sqlConfig = {
|
await initSqlite()
|
||||||
user: username,
|
const dbPath = originalHost || database // SQLite 用原始路径
|
||||||
password,
|
let dbData = null
|
||||||
database: database || 'master',
|
|
||||||
server: host,
|
if (dbPath && fs.existsSync(dbPath)) {
|
||||||
port: port || 1433,
|
dbData = fs.readFileSync(dbPath)
|
||||||
options: {
|
|
||||||
encrypt: false,
|
|
||||||
trustServerCertificate: true,
|
|
||||||
connectTimeout: 10000
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const db = new SQL.Database(dbData)
|
||||||
|
db._path = dbPath
|
||||||
|
conn = db
|
||||||
|
break
|
||||||
}
|
}
|
||||||
const pool = await mssql.connect(sqlConfig)
|
|
||||||
pool._database = database || 'master'
|
|
||||||
return pool
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
case 'mongodb': {
|
||||||
throw new Error(`不支持的数据库类型: ${type}`)
|
const uri = username && password
|
||||||
|
? `mongodb://${username}:${password}@${host}:${port}/${database || 'admin'}?authSource=admin`
|
||||||
|
: `mongodb://${host}:${port}/${database || 'admin'}`
|
||||||
|
const client = new MongoClient(uri, {
|
||||||
|
serverSelectionTimeoutMS: 10000,
|
||||||
|
connectTimeoutMS: 10000
|
||||||
|
})
|
||||||
|
await client.connect()
|
||||||
|
client._database = database || 'admin'
|
||||||
|
conn = client
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'redis': {
|
||||||
|
const redis = new Redis({
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
password: password || undefined,
|
||||||
|
db: parseInt(database) || 0,
|
||||||
|
connectTimeout: 10000,
|
||||||
|
lazyConnect: true
|
||||||
|
})
|
||||||
|
await redis.connect()
|
||||||
|
conn = redis
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'sqlserver': {
|
||||||
|
const sqlConfig = {
|
||||||
|
user: username,
|
||||||
|
password,
|
||||||
|
database: database || 'master',
|
||||||
|
server: host,
|
||||||
|
port: port || 1433,
|
||||||
|
options: {
|
||||||
|
encrypt: false,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
connectTimeout: 10000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pool = await mssql.connect(sqlConfig)
|
||||||
|
pool._database = database || 'master'
|
||||||
|
conn = pool
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`不支持的数据库类型: ${type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试连接时,将隧道附加到连接对象
|
||||||
|
if (tunnel && !connectionId) {
|
||||||
|
conn._sshTunnel = tunnel
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn
|
||||||
|
} catch (e) {
|
||||||
|
// 连接失败时清理隧道
|
||||||
|
if (tunnel) {
|
||||||
|
try {
|
||||||
|
if (tunnel.server) tunnel.server.close()
|
||||||
|
if (tunnel.ssh) tunnel.ssh.end()
|
||||||
|
} catch (err) {}
|
||||||
|
if (connectionId) sshTunnels.delete(connectionId)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function closeConnection(conn, type) {
|
async function closeConnection(conn, type, connectionId = null) {
|
||||||
try {
|
try {
|
||||||
|
// 关闭数据库连接
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
case 'mariadb':
|
case 'mariadb':
|
||||||
@ -1260,6 +1434,19 @@ async function closeConnection(conn, type) {
|
|||||||
await conn.close()
|
await conn.close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭测试连接的 SSH 隧道
|
||||||
|
if (conn._sshTunnel) {
|
||||||
|
try {
|
||||||
|
if (conn._sshTunnel.server) conn._sshTunnel.server.close()
|
||||||
|
if (conn._sshTunnel.ssh) conn._sshTunnel.ssh.end()
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭正式连接的 SSH 隧道
|
||||||
|
if (connectionId) {
|
||||||
|
closeSSHTunnel(connectionId)
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('关闭连接时出错:', e)
|
console.error('关闭连接时出错:', e)
|
||||||
}
|
}
|
||||||
|
|||||||
17
package.json
17
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "easysql",
|
"name": "easysql",
|
||||||
"version": "2.0.1",
|
"version": "2.0.3",
|
||||||
"description": "Modern Database Management Tool",
|
"description": "Modern Database Management Tool",
|
||||||
"main": "electron/main.js",
|
"main": "electron/main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@ -15,7 +15,8 @@
|
|||||||
"version:patch": "node scripts/bump-version.js patch",
|
"version:patch": "node scripts/bump-version.js patch",
|
||||||
"version:minor": "node scripts/bump-version.js minor",
|
"version:minor": "node scripts/bump-version.js minor",
|
||||||
"version:major": "node scripts/bump-version.js major",
|
"version:major": "node scripts/bump-version.js major",
|
||||||
"icons": "node scripts/generate-icons.js"
|
"icons": "node scripts/generate-icons.js",
|
||||||
|
"postinstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
@ -28,7 +29,8 @@
|
|||||||
"mysql2": "^3.11.0",
|
"mysql2": "^3.11.0",
|
||||||
"pg": "^8.13.0",
|
"pg": "^8.13.0",
|
||||||
"sql-formatter": "^15.6.12",
|
"sql-formatter": "^15.6.12",
|
||||||
"sql.js": "^1.11.0"
|
"sql.js": "^1.11.0",
|
||||||
|
"ssh2": "^1.16.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
@ -57,7 +59,12 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist/**/*",
|
"dist/**/*",
|
||||||
"electron/**/*"
|
"electron/**/*",
|
||||||
|
"node_modules/**/*"
|
||||||
|
],
|
||||||
|
"asarUnpack": [
|
||||||
|
"node_modules/ssh2/**/*",
|
||||||
|
"node_modules/cpu-features/**/*"
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
@ -83,4 +90,4 @@
|
|||||||
"icon": "public/icon.png"
|
"icon": "public/icon.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user