perf: 客户端自动下载新版本更新
This commit is contained in:
parent
92793b8ff8
commit
79a94d25bd
111
electron/main.js
vendored
111
electron/main.js
vendored
@ -1,47 +1,23 @@
|
||||
const fs = require('fs')
|
||||
const os = require("os");
|
||||
const path = require('path')
|
||||
const XLSX = require('xlsx');
|
||||
const {app, BrowserWindow, ipcMain, dialog} = require('electron')
|
||||
const utils = require('./utils');
|
||||
const log = require("electron-log");
|
||||
|
||||
let mainWindow = null,
|
||||
subWindow = [],
|
||||
downloadList = [],
|
||||
willQuitApp = false,
|
||||
inheritClose = false,
|
||||
devloadCachePath = path.resolve(__dirname, ".devload"),
|
||||
devloadUrl = "";
|
||||
|
||||
if (fs.existsSync(devloadCachePath)) {
|
||||
devloadUrl = fs.readFileSync(devloadCachePath, 'utf8')
|
||||
}
|
||||
|
||||
function runNum(str, fixed) {
|
||||
let _s = Number(str);
|
||||
if (_s + "" === "NaN") {
|
||||
_s = 0;
|
||||
}
|
||||
if (/^[0-9]*[1-9][0-9]*$/.test(fixed)) {
|
||||
_s = _s.toFixed(fixed);
|
||||
let rs = _s.indexOf('.');
|
||||
if (rs < 0) {
|
||||
_s += ".";
|
||||
for (let i = 0; i < fixed; i++) {
|
||||
_s += "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
return _s;
|
||||
}
|
||||
|
||||
function randomString(len) {
|
||||
len = len || 32;
|
||||
let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678oOLl9gqVvUuI1';
|
||||
let maxPos = $chars.length;
|
||||
let pwd = '';
|
||||
for (let i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return pwd;
|
||||
}
|
||||
|
||||
function createMainWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1280,
|
||||
@ -54,7 +30,7 @@ function createMainWindow() {
|
||||
contextIsolation: false
|
||||
}
|
||||
})
|
||||
mainWindow.webContents.setUserAgent(mainWindow.webContents.getUserAgent() + " MainTaskWindow/1.0");
|
||||
mainWindow.webContents.setUserAgent(mainWindow.webContents.getUserAgent() + " MainTaskWindow/" + process.platform + "/" + os.arch() + "/1.0");
|
||||
|
||||
if (devloadUrl) {
|
||||
mainWindow.loadURL(devloadUrl).then(r => {
|
||||
@ -86,6 +62,42 @@ function createMainWindow() {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
mainWindow.webContents.session.on('will-download', (event, item) => {
|
||||
item.setSavePath(path.join(app.getPath('temp'), item.getFilename()));
|
||||
item.on('done', (event, state) => {
|
||||
try {
|
||||
const info = {
|
||||
state,
|
||||
name: item.getFilename(),
|
||||
url: item.getURL(),
|
||||
chain: item.getURLChain(),
|
||||
savePath: item.getSavePath(),
|
||||
mimeType: item.getMimeType(),
|
||||
totalBytes: item.getTotalBytes(),
|
||||
};
|
||||
mainWindow.webContents.send("downloadDone", info)
|
||||
//
|
||||
if (info.state == "completed") {
|
||||
// 下载完成
|
||||
info.chain.some(url => {
|
||||
let download = downloadList.find(item => item.url == url)
|
||||
if (download) {
|
||||
download.status = "completed"
|
||||
download.info = info
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 下载失败
|
||||
info.chain.some(url => {
|
||||
downloadList = downloadList.filter(item => item.url != url)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createSubWindow(args) {
|
||||
@ -100,7 +112,7 @@ function createSubWindow(args) {
|
||||
}
|
||||
}
|
||||
|
||||
let name = args.name || "auto_" + randomString(6);
|
||||
let name = args.name || "auto_" + utils.randomString(6);
|
||||
let item = subWindow.find(item => item.name == name);
|
||||
let browser = item ? item.browser : null;
|
||||
if (browser) {
|
||||
@ -139,7 +151,7 @@ function createSubWindow(args) {
|
||||
})
|
||||
subWindow.push({ name, browser })
|
||||
}
|
||||
browser.webContents.setUserAgent(browser.webContents.getUserAgent() + " SubTaskWindow/1.0" + (args.userAgent ? (" " + args.userAgent) : ""));
|
||||
browser.webContents.setUserAgent(browser.webContents.getUserAgent() + " SubTaskWindow/" + process.platform + "/" + os.arch() + "/1.0" + (args.userAgent ? (" " + args.userAgent) : ""));
|
||||
|
||||
if (devloadUrl) {
|
||||
browser.loadURL(devloadUrl + '#' + (args.hash || args.path)).then(r => {
|
||||
@ -177,6 +189,39 @@ ipcMain.on('inheritClose', (event) => {
|
||||
event.returnValue = "ok"
|
||||
})
|
||||
|
||||
ipcMain.on('downloadURL', (event, args) => {
|
||||
const download = downloadList.find(({url}) => url == args.url);
|
||||
if (download) {
|
||||
if (download.status == "completed") {
|
||||
if (fs.existsSync(download.info.savePath)) {
|
||||
log.warn("已下载完成", args)
|
||||
mainWindow.webContents.send("downloadDone", download.info)
|
||||
} else {
|
||||
log.info("开始重新下载", args)
|
||||
download.status = "progressing"
|
||||
mainWindow.webContents.downloadURL(args.url);
|
||||
}
|
||||
} else {
|
||||
log.warn("已在下载列表中", args)
|
||||
}
|
||||
} else {
|
||||
log.info("开始下载", args)
|
||||
downloadList.push(Object.assign(args, { status: "progressing" }))
|
||||
mainWindow.webContents.downloadURL(args.url);
|
||||
}
|
||||
event.returnValue = "ok"
|
||||
})
|
||||
|
||||
ipcMain.on('openFile', (event, args) => {
|
||||
utils.openFile(args.path)
|
||||
event.returnValue = "ok"
|
||||
})
|
||||
|
||||
ipcMain.on('windowQuit', (event) => {
|
||||
event.returnValue = "ok"
|
||||
app.quit();
|
||||
})
|
||||
|
||||
ipcMain.on('windowRouter', (event, args) => {
|
||||
createSubWindow(args)
|
||||
event.returnValue = "ok"
|
||||
@ -278,7 +323,7 @@ ipcMain.on('setDockBadge', (event, args) => {
|
||||
// Mac only
|
||||
return;
|
||||
}
|
||||
if (runNum(args) > 0) {
|
||||
if (utils.runNum(args) > 0) {
|
||||
app.dock.setBadge(String(args))
|
||||
} else {
|
||||
app.dock.setBadge("")
|
||||
|
@ -25,7 +25,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/kuaifan/dootask.git"
|
||||
"url": "https://github.com/kuaifan/dootask.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.61",
|
||||
@ -36,11 +36,12 @@
|
||||
"dmg-license": "^1.0.10",
|
||||
"dotenv": "^10.0.0",
|
||||
"electron": "^16.0.5",
|
||||
"electron-builder": "^22.14.5",
|
||||
"electron-log": "^4.4.3"
|
||||
"electron-builder": "^22.14.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"electron-log": "^4.4.3",
|
||||
"fs-extra": "^10.0.0",
|
||||
"xlsx": "^0.17.2"
|
||||
},
|
||||
|
106
electron/utils.js
vendored
Normal file
106
electron/utils.js
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
const fs = require("fs");
|
||||
const {shell} = require("electron");
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* 随机数字
|
||||
* @param str
|
||||
* @param fixed
|
||||
* @returns {number}
|
||||
*/
|
||||
runNum(str, fixed) {
|
||||
let _s = Number(str);
|
||||
if (_s + "" === "NaN") {
|
||||
_s = 0;
|
||||
}
|
||||
if (/^[0-9]*[1-9][0-9]*$/.test(fixed)) {
|
||||
_s = _s.toFixed(fixed);
|
||||
let rs = _s.indexOf('.');
|
||||
if (rs < 0) {
|
||||
_s += ".";
|
||||
for (let i = 0; i < fixed; i++) {
|
||||
_s += "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
return _s;
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机字符串
|
||||
* @param len
|
||||
* @returns {string}
|
||||
*/
|
||||
randomString(len) {
|
||||
len = len || 32;
|
||||
let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678oOLl9gqVvUuI1';
|
||||
let maxPos = $chars.length;
|
||||
let pwd = '';
|
||||
for (let i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return pwd;
|
||||
},
|
||||
|
||||
/**
|
||||
* 字符串包含
|
||||
* @param string
|
||||
* @param find
|
||||
* @param lower
|
||||
* @returns {boolean}
|
||||
*/
|
||||
strExists(string, find, lower = false) {
|
||||
string += "";
|
||||
find += "";
|
||||
if (lower !== true) {
|
||||
string = string.toLowerCase();
|
||||
find = find.toLowerCase();
|
||||
}
|
||||
return (string.indexOf(find) !== -1);
|
||||
},
|
||||
|
||||
/**
|
||||
* 字符串是否左边包含
|
||||
* @param string
|
||||
* @param find
|
||||
* @param lower
|
||||
* @returns {boolean}
|
||||
*/
|
||||
leftExists(string, find, lower = false) {
|
||||
string += "";
|
||||
find += "";
|
||||
if (lower !== true) {
|
||||
string = string.toLowerCase();
|
||||
find = find.toLowerCase();
|
||||
}
|
||||
return (string.substring(0, find.length) === find);
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除左边字符串
|
||||
* @param string
|
||||
* @param find
|
||||
* @param lower
|
||||
* @returns {string}
|
||||
*/
|
||||
leftDelete(string, find, lower = false) {
|
||||
string += "";
|
||||
find += "";
|
||||
if (this.leftExists(string, find, lower)) {
|
||||
string = string.substring(find.length)
|
||||
}
|
||||
return string ? string : '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开文件
|
||||
* @param path
|
||||
*/
|
||||
openFile(path) {
|
||||
if (!fs.existsSync(path)) {
|
||||
return
|
||||
}
|
||||
shell.openPath(path).then(() => {
|
||||
})
|
||||
},
|
||||
}
|
@ -6,15 +6,17 @@
|
||||
</keep-alive>
|
||||
</transition>
|
||||
<Spinner/>
|
||||
<AppDown/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Spinner from "./components/Spinner";
|
||||
import AppDown from "./components/AppDown";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
components: {Spinner},
|
||||
components: {AppDown, Spinner},
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div v-if="repoStatus && !$store.state.windowMax768" class="common-app-down">
|
||||
<div v-if="$Electron" class="common-app-down-link" @click="openExternal(repoData.html_url)">
|
||||
<div v-if="status && !$store.state.windowMax768" class="common-app-down">
|
||||
<div v-if="$Electron" class="common-app-down-link" @click="installApplication">
|
||||
<Icon type="md-download"/> {{$L(repoTitle)}}
|
||||
</div>
|
||||
<a v-else class="common-app-down-link" :href="repoData.html_url" target="_blank">
|
||||
<a v-else class="common-app-down-link" :href="releases.html_url" target="_blank">
|
||||
<Icon type="md-download"/> {{$L(repoTitle)}}
|
||||
</a>
|
||||
</div>
|
||||
@ -12,9 +12,9 @@
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import MarkdownPreview from "./MDEditor/components/preview";
|
||||
import axios from "axios";
|
||||
Vue.component('MarkdownPreview', MarkdownPreview)
|
||||
|
||||
import axios from "axios";
|
||||
import { Notification } from 'element-ui';
|
||||
|
||||
export default {
|
||||
@ -23,34 +23,19 @@ export default {
|
||||
return {
|
||||
repoName: 'kuaifan/dootask',
|
||||
repoData: {},
|
||||
repoStatus: 0, // 0 没有,1有客户端,2客户端有新版本
|
||||
|
||||
status: 0, // 0 没有,1有客户端,2客户端有新版本
|
||||
releases: {},
|
||||
downInfo: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getReleases();
|
||||
},
|
||||
computed: {
|
||||
repoTitle() {
|
||||
return this.repoStatus == 2 ? '更新客户端' : '客户端下载';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
repoData: {
|
||||
handler(data) {
|
||||
if (!data.tag_name) {
|
||||
this.repoStatus = 0;
|
||||
return;
|
||||
}
|
||||
if (!this.$Electron) {
|
||||
// 网页只提示有客户端下载
|
||||
this.repoStatus = 1;
|
||||
return;
|
||||
}
|
||||
// 客户端提示更新
|
||||
let currentVersion = window.systemInformation.version;
|
||||
let latestVersion = $A.leftDelete(data.tag_name.toLowerCase(), "v")
|
||||
if (this.compareVersion(latestVersion, currentVersion) === 1) {
|
||||
// 有新版本
|
||||
//
|
||||
if (this.$Electron) {
|
||||
this.$Electron.ipcRenderer.on('downloadDone', (event, args) => {
|
||||
if (args.name == this.repoData.name) {
|
||||
this.downInfo = args;
|
||||
const h = this.$createElement;
|
||||
window.__appNotification && window.__appNotification.close();
|
||||
window.__appNotification = Notification({
|
||||
@ -59,7 +44,7 @@ export default {
|
||||
position: "bottom-right",
|
||||
customClass: "common-app-down-notification",
|
||||
onClose: () => {
|
||||
this.repoStatus = 2;
|
||||
this.status = 2;
|
||||
},
|
||||
message: h('span', [
|
||||
h('span', [
|
||||
@ -68,19 +53,19 @@ export default {
|
||||
props: {
|
||||
color: 'volcano'
|
||||
}
|
||||
}, data.tag_name)
|
||||
}, this.releases.tag_name)
|
||||
]),
|
||||
h('MarkdownPreview', {
|
||||
class: 'common-app-down-body',
|
||||
props: {
|
||||
initialValue: data.body
|
||||
initialValue: this.releases.body
|
||||
}
|
||||
}),
|
||||
h('div', {
|
||||
class: 'common-app-down-link',
|
||||
on: {
|
||||
click: () => {
|
||||
this.openExternal(data.html_url);
|
||||
this.installApplication();
|
||||
}
|
||||
},
|
||||
}, [
|
||||
@ -92,20 +77,25 @@ export default {
|
||||
marginRight: '5px'
|
||||
}
|
||||
}),
|
||||
h('span', this.$L('立即升级'))
|
||||
h('span', this.$L('立即安装'))
|
||||
]),
|
||||
])
|
||||
});
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
repoTitle() {
|
||||
return this.status == 2 ? '更新客户端' : '客户端下载';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getReleases() {
|
||||
let appdown = $A.getStorageJson("cacheAppdown");
|
||||
if (appdown.time && appdown.time + 3600 > Math.round(new Date().getTime() / 1000)) {
|
||||
this.chackReleases(appdown.data)
|
||||
let cache = $A.getStorageJson("cacheAppdown");
|
||||
if (cache.time && cache.time + 3600 > Math.round(new Date().getTime() / 1000)) {
|
||||
this.releases = cache.data;
|
||||
this.chackReleases()
|
||||
return;
|
||||
}
|
||||
;(() => {
|
||||
@ -117,23 +107,52 @@ export default {
|
||||
time: Math.round(new Date().getTime() / 1000),
|
||||
data: data
|
||||
});
|
||||
this.chackReleases(data)
|
||||
this.releases = data;
|
||||
this.chackReleases()
|
||||
}
|
||||
});
|
||||
})();
|
||||
},
|
||||
|
||||
chackReleases(data) {
|
||||
let hostname = window.location.hostname;
|
||||
if (hostname == '127.0.0.1') {
|
||||
hostname = "www.dootask.com"
|
||||
chackReleases() {
|
||||
let hostName = window.location.hostname;
|
||||
if (hostName == '127.0.0.1') {
|
||||
hostName = "www.dootask.com"
|
||||
}
|
||||
let assets = data.assets || [];
|
||||
let asset = assets.find(({browser_download_url}) => {
|
||||
return $A.strExists(browser_download_url, hostname)
|
||||
});
|
||||
if (asset) {
|
||||
this.repoData = data;
|
||||
if (this.$Electron) {
|
||||
// 客户端(更新)
|
||||
let match = (window.navigator.userAgent + "").match(/\s+(Main|Sub)TaskWindow\/(.*?)\/(.*?)\//)
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
let artifactName = null;
|
||||
if (match[2] === 'darwin') {
|
||||
artifactName = `${hostName}-${this.releases.tag_name}-mac-${match[3]}.pkg`;
|
||||
} else if (match[2] === 'win32') {
|
||||
artifactName = `${hostName}-${this.releases.tag_name}-win-${match[3]}.exe`;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
this.repoData = (this.releases.assets || []).find(({name}) => name == artifactName);
|
||||
if (!this.repoData) {
|
||||
return;
|
||||
}
|
||||
let currentVersion = window.systemInformation.version;
|
||||
let latestVersion = $A.leftDelete(this.releases.tag_name.toLowerCase(), "v")
|
||||
if (this.compareVersion(latestVersion, currentVersion) === 1) {
|
||||
// 有新版本
|
||||
console.log("有新版本");
|
||||
this.$Electron.ipcRenderer.send('downloadURL', {
|
||||
url: this.repoData.browser_download_url
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 网页版(提示有客户端下载)
|
||||
this.repoData = (this.releases.assets || []).find(({name}) => $A.strExists(name, hostName));
|
||||
if (this.repoData) {
|
||||
console.log("有客户端");
|
||||
this.status = 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -181,12 +200,14 @@ export default {
|
||||
return 0;
|
||||
},
|
||||
|
||||
openExternal(url) {
|
||||
try {
|
||||
this.$Electron.shell.openExternal(url);
|
||||
} catch (e) {
|
||||
window.location.href = url;
|
||||
installApplication() {
|
||||
if (!this.$Electron) {
|
||||
return;
|
||||
}
|
||||
this.$Electron.ipcRenderer.send('openFile', {
|
||||
path: this.downInfo.savePath
|
||||
});
|
||||
this.$Electron.ipcRenderer.send('windowQuit');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -44,17 +44,14 @@
|
||||
</div>
|
||||
<div class="login-right-bottom">
|
||||
<Button v-if="$Electron" icon="ios-globe-outline" type="primary" @click="onServerUrlInput">{{$L('自定义服务器')}}</Button>
|
||||
<AppDown/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppDown from "../components/AppDown";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
components: {AppDown},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
|
@ -70,18 +70,15 @@
|
||||
</ul>
|
||||
</template>
|
||||
</div>
|
||||
<AppDown/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapState} from "vuex";
|
||||
import AppDown from "../../components/AppDown";
|
||||
import {Store} from "le5le-store";
|
||||
import TaskMenu from "./components/TaskMenu";
|
||||
|
||||
export default {
|
||||
components: {TaskMenu, AppDown},
|
||||
components: {TaskMenu},
|
||||
data() {
|
||||
return {
|
||||
nowTime: $A.Time(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user