diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..e63ef5a
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,6 @@
+# GitHub Personal Access Token for private repository access
+# Required for auto-updates from private repositories
+GH_TOKEN=
+
+# For private repositories, you might also need:
+# GITHUB_TOKEN= # Alternative token name that some tools might use
\ No newline at end of file
diff --git a/README.md b/README.md
index 8cb20ed..bb87da0 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ Quick Start Templates for **[Vite](https://vitejs.dev)** + **[Vue 3](https://vue
- ⚡️ Basic layout manager
- ⚡️ Global state management through the Pinia store
- ⚡️ Quick support through the GitHub community and official documentation
+- ⚡️ Auto-update support (including private repositories with proper authentication)
## [Documentation (Getting Started)](https://vutron.cdget.com/installation-and-build/getting-started)
diff --git a/buildAssets/builder/config.js b/buildAssets/builder/config.js
index 6b425ae..3d0dbb7 100644
--- a/buildAssets/builder/config.js
+++ b/buildAssets/builder/config.js
@@ -2,12 +2,23 @@
const dotenv = require('dotenv')
const packageJson = require('../../package.json')
+dotenv.config()
+
const baseConfig = {
productName: packageJson.name,
appId: packageJson.appId,
asar: true,
extends: null,
compression: 'maximum',
+ publish: [
+ {
+ provider: 'github',
+ owner: 'Jasery',
+ repo: 'gdt-tool',
+ releaseType: 'release',
+ token: process.env.GH_TOKEN || '' // 可选:从环境变量获取 GitHub Token,私有仓库必需
+ }
+ ],
artifactName: '${productName} ${version}_${arch}.${ext}',
directories: {
output: './release/${version}'
@@ -90,8 +101,6 @@ const baseConfig = {
}
}
-dotenv.config()
-
baseConfig.copyright = `ⓒ ${new Date().getFullYear()} $\{author}`
baseConfig.files = [
/* A list of files not to be included in the build. */
diff --git a/docs/auto-update-config.md b/docs/auto-update-config.md
new file mode 100644
index 0000000..a5fa769
--- /dev/null
+++ b/docs/auto-update-config.md
@@ -0,0 +1,61 @@
+# 自动更新配置
+
+本项目使用 `electron-updater` 库实现自动更新功能,支持从私有 GitHub 仓库获取更新。
+
+## 私有仓库配置
+
+要启用私有仓库的自动更新功能,请按以下步骤操作:
+
+### 1. 创建 GitHub Personal Access Token
+
+1. 访问 GitHub 设置页面 (https://github.com/settings/tokens)
+2. 点击 "Generate new token" (Classic tokens)
+3. 选择适当的权限(至少需要 `repo` 权限来访问私有仓库)
+4. 生成并复制 token
+
+### 2. 配置环境变量
+
+在项目根目录创建 `.env` 文件:
+
+```bash
+cp .env.example .env
+```
+
+编辑 `.env` 文件,添加你的 GitHub token:
+
+```env
+GH_TOKEN=your_github_personal_access_token
+```
+
+### 3. 构建配置
+
+构建配置文件 `buildAssets/builder/config.js` 已经配置为使用环境变量中的 token:
+
+```javascript
+publish: [
+ {
+ provider: 'github',
+ owner: 'your_username',
+ repo: 'your_repository',
+ releaseType: 'release',
+ token: process.env.GH_TOKEN || '' // 从环境变量获取
+ }
+]
+```
+
+## 发布更新
+
+1. 更新 `package.json` 中的版本号
+2. 提交更改并推送至 GitHub
+3. 创建新的 GitHub Release
+4. 运行构建命令:
+
+```bash
+pnpm build:all
+```
+
+## 注意事项
+
+- 请确保 GitHub token 权限最小化,仅授予必要的访问权限
+- 对于分发给公众的私有应用,用户需要有访问仓库的权限才能下载更新
+- 在 CI/CD 环境中,确保在环境变量中配置了相应的 token
\ No newline at end of file
diff --git a/package.json b/package.json
index 7e3d8c8..cde556d 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"private": true,
"repository": {
"type": "git",
- "url": "https://github.com/jooy2/vutron.git"
+ "url": "https://github.com/Jasery/gdt-tool.git"
},
"bugs": {
"url": "https://github.com/jooy2/vutron/issues"
diff --git a/src/main/AutoUpdater.ts b/src/main/AutoUpdater.ts
new file mode 100644
index 0000000..550e90b
--- /dev/null
+++ b/src/main/AutoUpdater.ts
@@ -0,0 +1,89 @@
+import { dialog, ipcMain, app } from 'electron'
+import { autoUpdater } from 'electron-updater'
+import log from 'electron-log/main'
+
+// 配置日志
+autoUpdater.logger = log
+log.transports.file.level = 'info'
+
+export class AutoUpdater {
+ private mainWindow: Electron.BrowserWindow | null = null
+
+ constructor(mainWindow: Electron.BrowserWindow) {
+ this.mainWindow = mainWindow
+
+ // 配置更新检查间隔
+ autoUpdater.autoDownload = false
+ autoUpdater.allowPrerelease = false
+ autoUpdater.channel = 'latest'
+
+ // 设置事件监听器
+ this.setupEventListeners()
+ }
+
+ private setupEventListeners() {
+ // 检查更新
+ autoUpdater.on('checking-for-update', () => {
+ log.info('正在检查更新...')
+ this.mainWindow?.webContents.send('update-checking')
+ })
+
+ // 发现新版本
+ autoUpdater.on('update-available', (info) => {
+ log.info('发现新版本:', info.version)
+ this.mainWindow?.webContents.send('update-available', info)
+ })
+
+ // 没有新版本
+ autoUpdater.on('update-not-available', (info) => {
+ log.info('当前已是最新版本')
+ this.mainWindow?.webContents.send('update-not-available', info)
+ })
+
+ // 更新下载进度
+ autoUpdater.on('download-progress', (progress) => {
+ log.info('下载进度:', progress.percent, '%')
+ this.mainWindow?.webContents.send('update-download-progress', progress)
+ })
+
+ // 更新下载完成
+ autoUpdater.on('update-downloaded', (info) => {
+ log.info('更新下载完成')
+ this.mainWindow?.webContents.send('update-downloaded', info)
+
+ // 提示用户是否重启应用安装更新
+ dialog.showMessageBox({
+ type: 'info',
+ title: '应用更新',
+ message: `新版本 ${info.version} 已下载`,
+ detail: '是否现在重启应用以安装更新?',
+ buttons: ['稍后重启', '立即重启'],
+ defaultId: 1,
+ cancelId: 0
+ }).then((result) => {
+ if (result.response === 1) {
+ // 安装更新并重启应用
+ setImmediate(() => {
+ autoUpdater.quitAndInstall()
+ })
+ }
+ })
+ })
+
+ // 更新错误
+ autoUpdater.on('error', (error) => {
+ log.error('自动更新出错:', error)
+ this.mainWindow?.webContents.send('update-error', error)
+ })
+ }
+
+ public checkForUpdates() {
+ log.info('开始检查更新...')
+ autoUpdater.checkForUpdates()
+ }
+
+ public downloadUpdate() {
+ log.info('开始下载更新...')
+ autoUpdater.downloadUpdate()
+ }
+}
\ No newline at end of file
diff --git a/src/main/IPCs.ts b/src/main/IPCs.ts
index 4524462..d72290f 100644
--- a/src/main/IPCs.ts
+++ b/src/main/IPCs.ts
@@ -8,6 +8,7 @@ import {
ensureDirSync,
copyFileSync
} from 'fs-extra'
+import { autoUpdater } from 'electron-updater'
/*
* IPC Communications
@@ -222,5 +223,84 @@ export default class IPCs {
}
}
)
+
+ // 检查更新
+ ipcMain.handle('msgCheckForUpdates', async () => {
+ if (Constants.IS_DEV_ENV) {
+ // 在开发环境中模拟更新检查
+ console.log('开发环境中模拟检查更新...')
+ return { updateAvailable: false, currentVersion: Constants.APP_VERSION }
+ }
+
+ return new Promise((resolve) => {
+ // 监听更新事件
+ const removeListeners = () => {
+ autoUpdater.removeListener('update-available', onUpdateAvailable)
+ autoUpdater.removeListener('update-not-available', onUpdateNotAvailable)
+ autoUpdater.removeListener('error', onError)
+ }
+
+ const onUpdateAvailable = () => {
+ removeListeners()
+ resolve({ updateAvailable: true, currentVersion: Constants.APP_VERSION })
+ }
+
+ const onUpdateNotAvailable = () => {
+ removeListeners()
+ resolve({ updateAvailable: false, currentVersion: Constants.APP_VERSION })
+ }
+
+ const onError = (error: Error) => {
+ removeListeners()
+ console.error('检查更新时出错:', error)
+ resolve({ updateAvailable: false, currentVersion: Constants.APP_VERSION, error: error.message })
+ }
+
+ // 添加事件监听器
+ autoUpdater.once('update-available', onUpdateAvailable)
+ autoUpdater.once('update-not-available', onUpdateNotAvailable)
+ autoUpdater.once('error', onError)
+
+ // 开始检查更新
+ autoUpdater.checkForUpdates()
+ })
+ })
+
+ // 下载更新
+ ipcMain.handle('msgDownloadUpdate', async () => {
+ if (Constants.IS_DEV_ENV) {
+ // 在开发环境中模拟下载更新
+ console.log('开发环境中模拟下载更新...')
+ return { success: true }
+ }
+
+ try {
+ await autoUpdater.downloadUpdate()
+ return { success: true }
+ } catch (error) {
+ console.error('下载更新时出错:', error)
+ return { success: false, error: error instanceof Error ? error.message : String(error) }
+ }
+ })
+
+ // 安装更新
+ ipcMain.handle('msgInstallUpdate', async () => {
+ if (Constants.IS_DEV_ENV) {
+ // 在开发环境中模拟安装更新
+ console.log('开发环境中模拟安装更新...')
+ return { success: true }
+ }
+
+ try {
+ // 立即退出并安装更新
+ setImmediate(() => {
+ autoUpdater.quitAndInstall()
+ })
+ return { success: true }
+ } catch (error) {
+ console.error('安装更新时出错:', error)
+ return { success: false, error: error instanceof Error ? error.message : String(error) }
+ }
+ })
}
}
diff --git a/src/main/index.ts b/src/main/index.ts
index 4ea3068..9f4adf0 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -3,9 +3,11 @@ import Constants from './utils/Constants'
import { createErrorWindow, createMainWindow } from './MainRunner'
import log from 'electron-log/main'
import { join } from 'path'
+import { AutoUpdater } from './AutoUpdater'
let mainWindow
let errorWindow
+let autoUpdater: AutoUpdater | null = null
const initializeMainLogger = () => {
log.initialize({
@@ -52,6 +54,11 @@ app.on('ready', async () => {
initializeMainLogger()
mainWindow = await createMainWindow()
+
+ // Initialize auto updater after main window is created
+ if (!Constants.IS_DEV_ENV) {
+ autoUpdater = new AutoUpdater(mainWindow)
+ }
})
app.on('activate', async () => {
diff --git a/src/preload/index.ts b/src/preload/index.ts
index a812abc..b707727 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -14,9 +14,19 @@ const mainAvailChannels: string[] = [
'msgLoadData',
'msgSaveData',
'msgExecuteTemplate',
- 'msgFindFilesInFolder'
+ 'msgFindFilesInFolder',
+ 'msgCheckForUpdates',
+ 'msgDownloadUpdate',
+ 'msgInstallUpdate'
+]
+const rendererAvailChannels: string[] = [
+ 'update-checking',
+ 'update-available',
+ 'update-not-available',
+ 'update-download-progress',
+ 'update-downloaded',
+ 'update-error'
]
-const rendererAvailChannels: string[] = []
contextBridge.exposeInMainWorld('mainApi', {
send: (channel: string, ...data: any[]): void => {
diff --git a/src/renderer/App.vue b/src/renderer/App.vue
index 9af8785..cd1d162 100644
--- a/src/renderer/App.vue
+++ b/src/renderer/App.vue
@@ -1,6 +1,72 @@
@@ -8,6 +74,7 @@ import GlobalDialog from '@/renderer/components/GlobalDialog.vue'
+