fdb2 1.0.23 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -607
- package/bootstrap.js +26 -0
- package/dist/public/bootstrap.js +5 -5
- package/dist/public/explorer.js +203 -197
- package/dist/public/favicon.ico +0 -0
- package/dist/public/index.js +189 -168
- package/dist/public/layout.js +4 -4
- package/dist/public/vue.js +120 -90
- package/dist/scripts/electron-build.js +145 -0
- package/dist/scripts/electron-dev.js +116 -0
- package/dist/server/service/connection.service.d.ts.map +1 -1
- package/dist/server/service/connection.service.js +3 -7
- package/dist/server/service/connection.service.js.map +1 -1
- package/dist/server/service/connection.service.ts +3 -7
- package/dist/server/utils/data-dir.d.ts +19 -0
- package/dist/server/utils/data-dir.d.ts.map +1 -0
- package/dist/server/utils/data-dir.js +84 -0
- package/dist/server/utils/data-dir.js.map +1 -0
- package/dist/server/utils/data-dir.ts +47 -0
- package/electron/main.js +269 -0
- package/electron/preload.js +59 -0
- package/electron/updater.js +181 -0
- package/package.json +28 -35
- package/public/favicon.icns +0 -0
- package/public/favicon.ico +0 -0
- package/scripts/electron-build.js +145 -0
- package/scripts/electron-dev.js +116 -0
- package/server.js +6 -3
- package/dist/package.json +0 -139
- package/dist/pnpm-lock.yaml +0 -7332
- package/dist/scripts/nw-build.js +0 -136
- package/dist/scripts/nw-dev.js +0 -67
- package/dist/server/service/session.service.ts +0 -158
- package/scripts/nw-build.js +0 -136
- package/scripts/nw-dev.js +0 -67
package/electron/main.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Electron 主进程入口
|
|
3
|
+
* @description 创建桌面窗口,通过 IPC 直接处理数据库 API 请求(无需 HTTP 服务)
|
|
4
|
+
*
|
|
5
|
+
* 生产模式:加载本地 HTML,API 通过主进程 IPC 通信
|
|
6
|
+
* 开发模式:连接 Vite 开发服务器(API 走 HTTP)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { app, BrowserWindow, shell, ipcMain, protocol, net, Menu } = require('electron');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const { initAutoUpdater } = require('./updater');
|
|
13
|
+
|
|
14
|
+
// ========== 日志输出到文件(生产模式调试用) ==========
|
|
15
|
+
// 打包后 __dirname 指向 app.asar 内部(只读),日志须写到用户数据目录
|
|
16
|
+
const logDir = app.isPackaged
|
|
17
|
+
? path.join(app.getPath('userData'), 'logs')
|
|
18
|
+
: path.join(__dirname, '..', 'logs');
|
|
19
|
+
try { if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true }); } catch (e) { /* ignore */ }
|
|
20
|
+
const logFile = path.join(logDir, 'electron.log');
|
|
21
|
+
const logStream = (() => {
|
|
22
|
+
try {
|
|
23
|
+
return fs.createWriteStream(logFile, { flags: 'w' });
|
|
24
|
+
} catch (e) { return null; }
|
|
25
|
+
})();
|
|
26
|
+
const origLog = console.log;
|
|
27
|
+
const origError = console.error;
|
|
28
|
+
const origWarn = console.warn;
|
|
29
|
+
function ts() { return new Date().toISOString(); }
|
|
30
|
+
console.log = function(...args) {
|
|
31
|
+
origLog.apply(console, args);
|
|
32
|
+
if (logStream) logStream.write(`[${ts()}] LOG ${args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')}\n`);
|
|
33
|
+
};
|
|
34
|
+
console.error = function(...args) {
|
|
35
|
+
origError.apply(console, args);
|
|
36
|
+
if (logStream) logStream.write(`[${ts()}] ERR ${args.map(a => typeof a === 'string' ? a : (a instanceof Error ? a.message + '\n' + a.stack : JSON.stringify(a))).join(' ')}\n`);
|
|
37
|
+
};
|
|
38
|
+
console.warn = function(...args) {
|
|
39
|
+
origWarn.apply(console, args);
|
|
40
|
+
if (logStream) logStream.write(`[${ts()}] WRN ${args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')}\n`);
|
|
41
|
+
};
|
|
42
|
+
console.log(`[Electron] Log file: ${logFile}`);
|
|
43
|
+
console.log(`[Electron] App starting, packaged: ${app.isPackaged}, platform: ${process.platform}, arch: ${process.arch}`);
|
|
44
|
+
console.log(`[Electron] __dirname: ${__dirname}, cwd: ${process.cwd()}`);
|
|
45
|
+
|
|
46
|
+
// ========== 注册自定义协议(必须在 app.whenReady 之前) ==========
|
|
47
|
+
// 解决 file:// 协议下绝对路径(/public/xxx.js)无法加载的问题
|
|
48
|
+
protocol.registerSchemesAsPrivileged([
|
|
49
|
+
{ scheme: 'app', privileges: { standard: true, secure: true, supportFetchAPI: true } },
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
// 解析命令行参数
|
|
53
|
+
const args = process.argv.slice(app.isPackaged ? 1 : 2);
|
|
54
|
+
const devUrl = args.find(a => a.startsWith('--dev-url='))?.split('=')[1];
|
|
55
|
+
const isDev = !!devUrl;
|
|
56
|
+
|
|
57
|
+
let mainWindow = null;
|
|
58
|
+
|
|
59
|
+
// ========== 服务实例(延迟加载) ==========
|
|
60
|
+
let _serverModule = null;
|
|
61
|
+
|
|
62
|
+
function getServerModule() {
|
|
63
|
+
if (_serverModule) return _serverModule;
|
|
64
|
+
if (isDev) {
|
|
65
|
+
console.warn('[Electron] Dev mode: services are provided by Vite dev server, IPC disabled');
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
// 生产模式下,__dirname 指向 app.asar/electron/
|
|
70
|
+
// 需要加载 app.asar/dist/server/index.js
|
|
71
|
+
const serverPath = path.join(__dirname, '..', 'dist', 'server', 'index.js');
|
|
72
|
+
console.log(`[Electron] Loading server module from: ${serverPath}`);
|
|
73
|
+
console.log(`[Electron] __dirname: ${__dirname}`);
|
|
74
|
+
console.log(`[Electron] File exists: ${require('fs').existsSync(serverPath)}`);
|
|
75
|
+
|
|
76
|
+
// 在 asar 包内,需要使用 require 直接加载,而不是先检查文件是否存在
|
|
77
|
+
_serverModule = require(serverPath);
|
|
78
|
+
console.log(`[Electron] Server module loaded, keys: ${Object.keys(_serverModule).join(', ')}`);
|
|
79
|
+
return _serverModule;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.error(`[Electron] Failed to load server module: ${e.message}`);
|
|
82
|
+
console.error(e.stack);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ========== API IPC 处理(替代渲染进程 HTTP 请求) ==========
|
|
88
|
+
|
|
89
|
+
function setupApiIPC() {
|
|
90
|
+
ipcMain.handle('api:request', async (_event, payload) => {
|
|
91
|
+
const serverModule = getServerModule();
|
|
92
|
+
if (!serverModule) {
|
|
93
|
+
return { ret: 500, msg: 'Services not available (dev mode - use Vite HTTP server instead)' };
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const pathname = String(payload?.pathname || '');
|
|
97
|
+
if (!pathname.startsWith('/api/')) {
|
|
98
|
+
return { ret: 400, msg: 'Invalid API path' };
|
|
99
|
+
}
|
|
100
|
+
const data = await serverModule.handleDatabaseRoutes(pathname, payload?.body || {});
|
|
101
|
+
// 确保返回数据可被 structured clone 序列化(去除 TypeORM 实体等复杂对象)
|
|
102
|
+
let safeData;
|
|
103
|
+
try {
|
|
104
|
+
safeData = JSON.parse(JSON.stringify(data));
|
|
105
|
+
} catch (e) {
|
|
106
|
+
safeData = data;
|
|
107
|
+
}
|
|
108
|
+
return { ret: 0, msg: 'success', data: safeData };
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error('[IPC] API request failed:', err);
|
|
111
|
+
return { ret: 500, msg: err.message || 'Internal error' };
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ========== 创建窗口 ==========
|
|
117
|
+
|
|
118
|
+
function createWindow(loadTarget) {
|
|
119
|
+
mainWindow = new BrowserWindow({
|
|
120
|
+
width: 1200,
|
|
121
|
+
height: 800,
|
|
122
|
+
minWidth: 800,
|
|
123
|
+
minHeight: 600,
|
|
124
|
+
show: false,
|
|
125
|
+
title: 'FDB2 - 数据库管理工具',
|
|
126
|
+
icon: path.join(__dirname, '..', 'public', process.platform === 'darwin' || process.platform === 'win32' ? 'favicon.ico' : 'favicon.png'),
|
|
127
|
+
webPreferences: {
|
|
128
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
129
|
+
nodeIntegration: false,
|
|
130
|
+
contextIsolation: true,
|
|
131
|
+
sandbox: true,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
mainWindow.once('ready-to-show', () => {
|
|
136
|
+
mainWindow.show();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
140
|
+
if (url.startsWith('http')) shell.openExternal(url);
|
|
141
|
+
return { action: 'deny' };
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (loadTarget.startsWith('http')) {
|
|
145
|
+
mainWindow.loadURL(loadTarget);
|
|
146
|
+
} else if (loadTarget.startsWith('app://')) {
|
|
147
|
+
mainWindow.loadURL(loadTarget);
|
|
148
|
+
} else {
|
|
149
|
+
mainWindow.loadFile(loadTarget);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
mainWindow.on('closed', () => {
|
|
153
|
+
mainWindow = null;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (isDev) {
|
|
157
|
+
mainWindow.webContents.openDevTools({ mode: 'detach' });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 生产模式下可通过快捷键打开 DevTools 调试
|
|
161
|
+
mainWindow.webContents.on('before-input-event', (event, input) => {
|
|
162
|
+
if ((input.meta || input.control) && input.shift && input.key.toLowerCase() === 'i') {
|
|
163
|
+
mainWindow.webContents.toggleDevTools();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ========== 应用菜单 ==========
|
|
169
|
+
|
|
170
|
+
function setupMenu() {
|
|
171
|
+
const template = [
|
|
172
|
+
{
|
|
173
|
+
label: 'Edit',
|
|
174
|
+
submenu: [
|
|
175
|
+
{ role: 'undo' },
|
|
176
|
+
{ role: 'redo' },
|
|
177
|
+
{ type: 'separator' },
|
|
178
|
+
{ role: 'cut' },
|
|
179
|
+
{ role: 'copy' },
|
|
180
|
+
{ role: 'paste' },
|
|
181
|
+
{ role: 'selectAll' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
label: 'Help',
|
|
186
|
+
submenu: [
|
|
187
|
+
{ label: 'About FDB2', click: () => shell.openExternal('https://github.com/fefeding/fdb2') },
|
|
188
|
+
{ type: 'separator' },
|
|
189
|
+
{ label: 'Check for Updates...', click: () => {
|
|
190
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
191
|
+
mainWindow.webContents.send('menu:action', 'check-update');
|
|
192
|
+
}
|
|
193
|
+
}},
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
// macOS 需要添加 app 菜单
|
|
199
|
+
if (process.platform === 'darwin') {
|
|
200
|
+
template.unshift({
|
|
201
|
+
label: app.name,
|
|
202
|
+
submenu: [
|
|
203
|
+
{ label: 'About FDB2', click: () => shell.openExternal('https://github.com/fefeding/fdb2') },
|
|
204
|
+
{ type: 'separator' },
|
|
205
|
+
{ role: 'hide' },
|
|
206
|
+
{ role: 'hideOthers' },
|
|
207
|
+
{ role: 'unhide' },
|
|
208
|
+
{ type: 'separator' },
|
|
209
|
+
{ role: 'quit' },
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const menu = Menu.buildFromTemplate(template);
|
|
215
|
+
Menu.setApplicationMenu(menu);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ========== 应用生命周期 ==========
|
|
219
|
+
|
|
220
|
+
app.whenReady().then(() => {
|
|
221
|
+
// 注册 app:// 协议处理器(生产模式加载本地文件)
|
|
222
|
+
protocol.handle('app', (request) => {
|
|
223
|
+
const { pathname } = new URL(request.url);
|
|
224
|
+
// app://local/xxx → 项目根目录/dist/xxx
|
|
225
|
+
const filePath = path.join(__dirname, '..', 'dist', pathname);
|
|
226
|
+
return net.fetch('file://' + filePath);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
setupApiIPC();
|
|
230
|
+
setupMenu();
|
|
231
|
+
|
|
232
|
+
// 初始化服务(仅生产模式加载模块,开发模式由 Vite 提供)
|
|
233
|
+
try {
|
|
234
|
+
const serverModule = getServerModule();
|
|
235
|
+
if (serverModule) {
|
|
236
|
+
console.log('[Electron] Server module initialized (production mode)');
|
|
237
|
+
} else {
|
|
238
|
+
console.log('[Electron] Server module skipped (dev mode - using Vite dev server)');
|
|
239
|
+
}
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.error('[Electron] Failed to initialize server module:', e.message);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let target;
|
|
245
|
+
if (isDev) {
|
|
246
|
+
target = devUrl;
|
|
247
|
+
console.log(`[Electron] Dev mode, connecting to ${target}`);
|
|
248
|
+
} else {
|
|
249
|
+
target = 'app://local/view/index.html';
|
|
250
|
+
console.log(`[Electron] Production mode, loading ${target}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
createWindow(target);
|
|
254
|
+
|
|
255
|
+
// 初始化自动更新(生产模式启用,开发模式自动禁用)
|
|
256
|
+
initAutoUpdater({ isDev });
|
|
257
|
+
|
|
258
|
+
app.on('activate', () => {
|
|
259
|
+
if (BrowserWindow.getAllWindows().length === 0) {
|
|
260
|
+
createWindow(target);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
app.on('window-all-closed', () => {
|
|
266
|
+
if (process.platform !== 'darwin') {
|
|
267
|
+
app.quit();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Electron 预加载脚本
|
|
3
|
+
* @description 通过 contextBridge 向渲染进程暴露最小 IPC 通信接口
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { contextBridge, ipcRenderer, clipboard } = require('electron');
|
|
7
|
+
|
|
8
|
+
// 自动更新 API
|
|
9
|
+
const updater = {
|
|
10
|
+
/** 手动检查更新 */
|
|
11
|
+
checkForUpdates() {
|
|
12
|
+
return ipcRenderer.invoke('update:check');
|
|
13
|
+
},
|
|
14
|
+
/** 获取当前更新状态 */
|
|
15
|
+
getStatus() {
|
|
16
|
+
return ipcRenderer.invoke('update:status');
|
|
17
|
+
},
|
|
18
|
+
/** 安装更新并重启 */
|
|
19
|
+
install() {
|
|
20
|
+
return ipcRenderer.invoke('update:install');
|
|
21
|
+
},
|
|
22
|
+
/** 监听更新事件(available, progress, downloaded, error) */
|
|
23
|
+
onEvent(callback) {
|
|
24
|
+
const handler = (_event, msg) => callback(msg);
|
|
25
|
+
ipcRenderer.on('update:event', handler);
|
|
26
|
+
return () => ipcRenderer.removeListener('update:event', handler);
|
|
27
|
+
},
|
|
28
|
+
/** 监听菜单操作(如 check-update) */
|
|
29
|
+
onMenuAction(callback) {
|
|
30
|
+
const handler = (_event, action) => callback(action);
|
|
31
|
+
ipcRenderer.on('menu:action', handler);
|
|
32
|
+
return () => ipcRenderer.removeListener('menu:action', handler);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const api = {
|
|
37
|
+
request(pathname, body) {
|
|
38
|
+
return ipcRenderer.invoke('api:request', { pathname, body });
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// 剪贴板操作(contextIsolation 下渲染进程无法直接访问 navigator.clipboard)
|
|
43
|
+
const clip = {
|
|
44
|
+
writeText(text) {
|
|
45
|
+
clipboard.writeText(text);
|
|
46
|
+
},
|
|
47
|
+
readText() {
|
|
48
|
+
return clipboard.readText();
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
contextBridge.exposeInMainWorld('electronAPI', {
|
|
53
|
+
isElectron: true,
|
|
54
|
+
isPackaged: !process.env.ELECTRON_DEV,
|
|
55
|
+
platform: process.platform,
|
|
56
|
+
api,
|
|
57
|
+
clipboard: clip,
|
|
58
|
+
updater,
|
|
59
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Electron 自动更新模块
|
|
3
|
+
* @description 使用 electron-updater 检测、下载和安装应用更新
|
|
4
|
+
*
|
|
5
|
+
* 更新流程:
|
|
6
|
+
* 1. 应用启动时自动检查更新
|
|
7
|
+
* 2. 发现新版本 → 通知渲染进程显示提示
|
|
8
|
+
* 3. 后台下载更新(利用 blockmap 增量下载)
|
|
9
|
+
* 4. 下载完成 → 通知渲染进程显示"重启安装"按钮
|
|
10
|
+
* 5. 用户确认后重启并安装
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { autoUpdater } = require('electron-updater');
|
|
14
|
+
const { BrowserWindow, ipcMain, app } = require('electron');
|
|
15
|
+
|
|
16
|
+
// 更新状态
|
|
17
|
+
let updateAvailable = null;
|
|
18
|
+
let updateDownloaded = false;
|
|
19
|
+
let downloadProgress = null;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 向所有窗口广播更新事件
|
|
23
|
+
*/
|
|
24
|
+
function broadcastUpdate(event, data) {
|
|
25
|
+
BrowserWindow.getAllWindows().forEach((win) => {
|
|
26
|
+
if (!win.isDestroyed()) {
|
|
27
|
+
win.webContents.send('update:event', { event, data });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 初始化自动更新
|
|
34
|
+
* @param {object} options - 配置选项
|
|
35
|
+
* @param {boolean} options.isDev - 是否开发模式
|
|
36
|
+
*/
|
|
37
|
+
function initAutoUpdater(options = {}) {
|
|
38
|
+
// 开发模式下禁用自动更新
|
|
39
|
+
if (options.isDev) {
|
|
40
|
+
console.log('[Updater] Disabled in dev mode');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 未打包时禁用(如通过 npm start 运行的 Web 模式)
|
|
45
|
+
if (!app.isPackaged) {
|
|
46
|
+
console.log('[Updater] Disabled: app is not packaged');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 配置 autoUpdater
|
|
51
|
+
autoUpdater.autoDownload = true;
|
|
52
|
+
autoUpdater.autoInstallOnAppQuit = true;
|
|
53
|
+
|
|
54
|
+
// 事件监听
|
|
55
|
+
autoUpdater.on('checking-for-update', () => {
|
|
56
|
+
console.log('[Updater] Checking for updates...');
|
|
57
|
+
broadcastUpdate('checking', null);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
autoUpdater.on('update-available', (info) => {
|
|
61
|
+
console.log(`[Updater] Update available: v${info.version}`);
|
|
62
|
+
updateAvailable = info;
|
|
63
|
+
updateDownloaded = false;
|
|
64
|
+
broadcastUpdate('available', {
|
|
65
|
+
version: info.version,
|
|
66
|
+
releaseDate: info.releaseDate,
|
|
67
|
+
releaseNotes: info.releaseNotes,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
autoUpdater.on('update-not-available', (info) => {
|
|
72
|
+
console.log(`[Updater] Already up to date: v${info.version}`);
|
|
73
|
+
updateAvailable = null;
|
|
74
|
+
updateDownloaded = false;
|
|
75
|
+
broadcastUpdate('not-available', { version: info.version });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
autoUpdater.on('download-progress', (progress) => {
|
|
79
|
+
downloadProgress = {
|
|
80
|
+
percent: Math.round(progress.percent),
|
|
81
|
+
bytesPerSecond: progress.bytesPerSecond,
|
|
82
|
+
transferred: progress.transferred,
|
|
83
|
+
total: progress.total,
|
|
84
|
+
};
|
|
85
|
+
// 每 10% 广播一次,避免过于频繁
|
|
86
|
+
if (downloadProgress.percent % 10 === 0) {
|
|
87
|
+
broadcastUpdate('progress', downloadProgress);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
autoUpdater.on('update-downloaded', (info) => {
|
|
92
|
+
console.log(`[Updater] Update downloaded: v${info.version}`);
|
|
93
|
+
updateDownloaded = true;
|
|
94
|
+
broadcastUpdate('downloaded', {
|
|
95
|
+
version: info.version,
|
|
96
|
+
releaseDate: info.releaseDate,
|
|
97
|
+
releaseNotes: info.releaseNotes,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
autoUpdater.on('error', (error) => {
|
|
102
|
+
console.error('[Updater] Error:', error.message);
|
|
103
|
+
updateAvailable = null;
|
|
104
|
+
updateDownloaded = false;
|
|
105
|
+
downloadProgress = null;
|
|
106
|
+
// 静默处理网络错误,不打扰用户
|
|
107
|
+
if (!error.message.includes('net::') && !error.message.includes('ENOTFOUND')) {
|
|
108
|
+
broadcastUpdate('error', { message: error.message });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// 注册 IPC 处理器
|
|
113
|
+
setupUpdateIPC();
|
|
114
|
+
|
|
115
|
+
// 延迟 3 秒后检查更新(避免启动时太卡)
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
checkForUpdates();
|
|
118
|
+
}, 3000);
|
|
119
|
+
|
|
120
|
+
// 每小时检查一次更新
|
|
121
|
+
setInterval(() => {
|
|
122
|
+
checkForUpdates();
|
|
123
|
+
}, 60 * 60 * 1000);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 检查更新
|
|
128
|
+
*/
|
|
129
|
+
async function checkForUpdates() {
|
|
130
|
+
try {
|
|
131
|
+
await autoUpdater.checkForUpdates();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error('[Updater] Check failed:', err.message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 安装更新并重启
|
|
139
|
+
*/
|
|
140
|
+
function quitAndInstall() {
|
|
141
|
+
if (updateDownloaded) {
|
|
142
|
+
autoUpdater.quitAndInstall();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 注册更新相关的 IPC 通道
|
|
148
|
+
*/
|
|
149
|
+
function setupUpdateIPC() {
|
|
150
|
+
// 渲染进程请求检查更新
|
|
151
|
+
ipcMain.handle('update:check', async () => {
|
|
152
|
+
await checkForUpdates();
|
|
153
|
+
return { checking: true };
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// 渲染进程请求获取当前更新状态
|
|
157
|
+
ipcMain.handle('update:status', () => {
|
|
158
|
+
return {
|
|
159
|
+
updateAvailable: updateAvailable
|
|
160
|
+
? {
|
|
161
|
+
version: updateAvailable.version,
|
|
162
|
+
releaseDate: updateAvailable.releaseDate,
|
|
163
|
+
}
|
|
164
|
+
: null,
|
|
165
|
+
updateDownloaded,
|
|
166
|
+
downloadProgress,
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// 渲染进程请求安装更新
|
|
171
|
+
ipcMain.handle('update:install', () => {
|
|
172
|
+
quitAndInstall();
|
|
173
|
+
return { installing: true };
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = {
|
|
178
|
+
initAutoUpdater,
|
|
179
|
+
checkForUpdates,
|
|
180
|
+
quitAndInstall,
|
|
181
|
+
};
|
package/package.json
CHANGED
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fdb2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
|
-
"main": "
|
|
6
|
+
"main": "electron/main.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"bin/",
|
|
9
9
|
"public/",
|
|
10
10
|
"view/",
|
|
11
11
|
"dist/",
|
|
12
|
+
"electron/",
|
|
12
13
|
"scripts/",
|
|
13
14
|
"server.js",
|
|
15
|
+
"bootstrap.js",
|
|
14
16
|
"package.json",
|
|
15
17
|
"README.md"
|
|
16
18
|
],
|
|
17
|
-
"homepage": "https://surl.fit/tools/tools/fdb2",
|
|
18
|
-
"icon": "public/favicon.ico",
|
|
19
|
-
"window": {
|
|
20
|
-
"title": "数据库管理工具",
|
|
21
|
-
"width": 1200,
|
|
22
|
-
"height": 800,
|
|
23
|
-
"min_width": 800,
|
|
24
|
-
"min_height": 600,
|
|
25
|
-
"resizable": true,
|
|
26
|
-
"show_in_taskbar": true,
|
|
27
|
-
"frame": true,
|
|
28
|
-
"icon": "public/favicon.ico"
|
|
29
|
-
},
|
|
30
19
|
"scripts": {
|
|
31
20
|
"start": "pm2 start server.js --name fdb2-server --disable-logs --",
|
|
32
21
|
"restart": "pm2 restart fdb2-server --disable-logs --",
|
|
@@ -43,11 +32,11 @@
|
|
|
43
32
|
"move-assets": "node move-assets.cjs",
|
|
44
33
|
"type-check": "vue-tsc --noEmit --composite false",
|
|
45
34
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
35
|
+
"electron:dev": "node scripts/electron-dev.js",
|
|
36
|
+
"electron:build": "node scripts/electron-build.js",
|
|
37
|
+
"electron:build:win": "node scripts/electron-build.js --platform=win",
|
|
38
|
+
"electron:build:mac": "node scripts/electron-build.js --platform=mac",
|
|
39
|
+
"electron:build:linux": "node scripts/electron-build.js --platform=linux",
|
|
51
40
|
"pkg:server": "cd d:/code/ycnull/db_tool && pkg dist/server/index.js -t node18-win-x64 -o dist/server/server.exe",
|
|
52
41
|
"build:server:pkg": "ncc build server/index.ts -o dist/server/pkg",
|
|
53
42
|
"vscode:dev": "cd packages/vscode && npm run watch",
|
|
@@ -55,15 +44,16 @@
|
|
|
55
44
|
},
|
|
56
45
|
"dependencies": {
|
|
57
46
|
"@fefeding/common": "^1.0.60",
|
|
58
|
-
"axios": "^1.
|
|
59
|
-
"dayjs": "^1.11.
|
|
47
|
+
"axios": "^1.17.0",
|
|
48
|
+
"dayjs": "^1.11.21",
|
|
49
|
+
"electron-updater": "^6.8.3",
|
|
60
50
|
"express": "^5.2.1",
|
|
61
|
-
"mysql2": "^3.22.
|
|
51
|
+
"mysql2": "^3.22.5",
|
|
62
52
|
"oracledb": "^6.10.0",
|
|
63
53
|
"pg": "^8.21.0",
|
|
64
54
|
"reflect-metadata": "^0.2.2",
|
|
65
55
|
"typeorm": "^0.3.30",
|
|
66
|
-
"vue-i18n": "^11.4.
|
|
56
|
+
"vue-i18n": "^11.4.5"
|
|
67
57
|
},
|
|
68
58
|
"optionalDependencies": {
|
|
69
59
|
"better-sqlite3": "^11.10.0"
|
|
@@ -76,14 +66,15 @@
|
|
|
76
66
|
"@codemirror/language": "^6.12.3",
|
|
77
67
|
"@codemirror/state": "^6.6.0",
|
|
78
68
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
79
|
-
"@codemirror/view": "^6.43.
|
|
69
|
+
"@codemirror/view": "^6.43.1",
|
|
70
|
+
"@electron/rebuild": "^4.0.4",
|
|
80
71
|
"@fefeding/eventemitter": "^1.0.5",
|
|
81
72
|
"@fefeding/vite-nunjucks-plugin": "^1.0.2",
|
|
82
73
|
"@popperjs/core": "^2.11.8",
|
|
83
|
-
"@rollup/pluginutils": "^5.
|
|
84
|
-
"@types/bootstrap": "^5.2.
|
|
74
|
+
"@rollup/pluginutils": "^5.4.0",
|
|
75
|
+
"@types/bootstrap": "^5.2.11",
|
|
85
76
|
"@types/jsdom": "^21.1.7",
|
|
86
|
-
"@types/node": "^24.
|
|
77
|
+
"@types/node": "^24.13.2",
|
|
87
78
|
"@types/vue": "^2.0.0",
|
|
88
79
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
89
80
|
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
|
@@ -96,12 +87,13 @@
|
|
|
96
87
|
"codemirror": "^6.0.2",
|
|
97
88
|
"dotenv": "^17.4.2",
|
|
98
89
|
"echarts": "^6.1.0",
|
|
90
|
+
"electron": "^35.5.1",
|
|
91
|
+
"electron-builder": "^26.0.12",
|
|
92
|
+
"electron-updater": "^6.8.3",
|
|
99
93
|
"exceljs": "^4.4.0",
|
|
100
|
-
"js-cookie": "^3.0.
|
|
94
|
+
"js-cookie": "^3.0.8",
|
|
101
95
|
"jsdom": "^26.1.0",
|
|
102
96
|
"jszip": "^3.10.1",
|
|
103
|
-
"nw": "^0.107.0",
|
|
104
|
-
"nw-builder": "^4.17.10",
|
|
105
97
|
"pinia": "^3.0.4",
|
|
106
98
|
"pinia-plugin-persistedstate": "^4.7.1",
|
|
107
99
|
"postcss": "^8.5.15",
|
|
@@ -111,10 +103,10 @@
|
|
|
111
103
|
"vconsole": "^3.15.1",
|
|
112
104
|
"vite": "latest",
|
|
113
105
|
"vite-plugin-files-copy": "^3.8.0",
|
|
114
|
-
"vitest": "^3.2.
|
|
115
|
-
"vue": "^3.5.
|
|
106
|
+
"vitest": "^3.2.6",
|
|
107
|
+
"vue": "^3.5.37",
|
|
116
108
|
"vue-router": "^4.6.4",
|
|
117
|
-
"vue-tsc": "^3.3.
|
|
109
|
+
"vue-tsc": "^3.3.4",
|
|
118
110
|
"vuex": "^4.1.0",
|
|
119
111
|
"xlsx": "^0.18.5"
|
|
120
112
|
},
|
|
@@ -135,7 +127,8 @@
|
|
|
135
127
|
},
|
|
136
128
|
"pnpm": {
|
|
137
129
|
"onlyBuiltDependencies": [
|
|
138
|
-
"better-sqlite3"
|
|
130
|
+
"better-sqlite3",
|
|
131
|
+
"electron"
|
|
139
132
|
]
|
|
140
133
|
}
|
|
141
134
|
}
|
|
Binary file
|
package/public/favicon.ico
CHANGED
|
Binary file
|