feat(core): nwjs下添加对 手动更新 的支持
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
<link rel="stylesheet" href="./files/typography.css">
|
<link rel="stylesheet" href="./files/typography.css">
|
||||||
<link rel="stylesheet" href="./files/style.css">
|
<link rel="stylesheet" href="./files/style.css">
|
||||||
<link rel="stylesheet" href="./files/responsive.css">
|
<link rel="stylesheet" href="./files/responsive.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="./common/ui/fomantic-ui/semantic.min.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="./common/ui/layui/css/layui.css" />
|
<link rel="stylesheet" type="text/css" href="./common/ui/layui/css/layui.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="./common/ui/layui/css/layui-theme-dark.css" />
|
<link rel="stylesheet" type="text/css" href="./common/ui/layui/css/layui-theme-dark.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="./common/css/fontello.css" />
|
<link rel="stylesheet" type="text/css" href="./common/css/fontello.css" />
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Config.init = () => {
|
|||||||
const boardPageConfig = Url.getConfig();
|
const boardPageConfig = Url.getConfig();
|
||||||
Config.BOARD_PAGE = boardPageConfig ?? {};
|
Config.BOARD_PAGE = boardPageConfig ?? {};
|
||||||
console.log('Config.BOARD_PAGE:', Config.BOARD_PAGE);
|
console.log('Config.BOARD_PAGE:', Config.BOARD_PAGE);
|
||||||
document.title = Config.SOFTWARE.version ?? 'Mixly 2.0';
|
document.title = Config.SOFTWARE.version ?? 'Mixly 3.0';
|
||||||
|
|
||||||
Config.USER = {
|
Config.USER = {
|
||||||
...Config.USER,
|
...Config.USER,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ goog.require('ace.ExtLanguageTools');
|
|||||||
goog.require('layui');
|
goog.require('layui');
|
||||||
goog.require('store');
|
goog.require('store');
|
||||||
goog.require('$.select2');
|
goog.require('$.select2');
|
||||||
|
goog.require('$.fomanticUI');
|
||||||
goog.require('Mixly.XML');
|
goog.require('Mixly.XML');
|
||||||
goog.require('Mixly.LayerExt');
|
goog.require('Mixly.LayerExt');
|
||||||
goog.require('Mixly.Msg');
|
goog.require('Mixly.Msg');
|
||||||
@@ -167,13 +168,14 @@ Setting.onclick = () => {
|
|||||||
Setting.addOnchangeOptionListener = () => {
|
Setting.addOnchangeOptionListener = () => {
|
||||||
element.on('tab(setting-menu-filter)', function(data) {
|
element.on('tab(setting-menu-filter)', function(data) {
|
||||||
const { index } = data;
|
const { index } = data;
|
||||||
if (index === 1) {
|
const type = $(data.elem.prevObject).data('type');
|
||||||
|
if (type === 'import-board') {
|
||||||
if (data.index !== Setting.nowIndex) {
|
if (data.index !== Setting.nowIndex) {
|
||||||
goog.isElectron && BoardManager.onclickImportBoards();
|
goog.isElectron && BoardManager.onclickImportBoards();
|
||||||
} else {
|
} else {
|
||||||
layui.table.resize('cloud-boards-table');
|
layui.table.resize('cloud-boards-table');
|
||||||
}
|
}
|
||||||
} else if (index === 2) {
|
} else if (type === 'ws-update') {
|
||||||
if (data.index !== Setting.nowIndex) {
|
if (data.index !== Setting.nowIndex) {
|
||||||
$('#setting-menu-update').loading({
|
$('#setting-menu-update').loading({
|
||||||
background: USER.theme === 'dark' ? '#807b7b' : '#fff',
|
background: USER.theme === 'dark' ? '#807b7b' : '#fff',
|
||||||
@@ -189,6 +191,26 @@ Setting.addOnchangeOptionListener = () => {
|
|||||||
args: [ SOFTWARE.configUrl ]
|
args: [ SOFTWARE.configUrl ]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (type === 'nw-update') {
|
||||||
|
fetch('/api/check-update', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((result) => {
|
||||||
|
const {
|
||||||
|
localVersion,
|
||||||
|
cloudVersion,
|
||||||
|
needsUpdate,
|
||||||
|
cloudFile
|
||||||
|
} = result;
|
||||||
|
Setting.refreshUpdateMenuStatus(localVersion, cloudVersion, needsUpdate, cloudFile);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Setting.nowIndex = index;
|
Setting.nowIndex = index;
|
||||||
});
|
});
|
||||||
@@ -229,58 +251,60 @@ Setting.configMenuGetValue = (obj) => {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
Setting.refreshUpdateMenuStatus = (config) => {
|
Setting.refreshUpdateMenuStatus = (localVersion, cloudVersion, needsUpdate, url) => {
|
||||||
console.log(config);
|
const $serverDiv = $('#setting-menu-update-server');
|
||||||
const {
|
const $btnDiv = $('#setting-menu-update > div:nth-child(2)');
|
||||||
serverVersion
|
const $mixlyProgress = $serverDiv.find('.mixly-progress');
|
||||||
} = config;
|
|
||||||
let $serverDiv = $('#setting-menu-update-server');
|
|
||||||
let $btnDiv = $('#setting-menu-update > div:nth-child(2)');
|
|
||||||
$serverDiv.find('span').css('display', 'none');
|
$serverDiv.find('span').css('display', 'none');
|
||||||
let needUpdateServer = false;
|
$mixlyProgress.hide();
|
||||||
if (serverVersion && serverVersion !== SOFTWARE.serverVersion) {
|
$mixlyProgress.find('.progress').show();
|
||||||
$serverDiv.find('span[value="obsolete"]').css('display', 'inline-block');
|
$mixlyProgress.removeClass('swinging indeterminate');
|
||||||
needUpdateServer = true;
|
if (needsUpdate) {
|
||||||
$serverDiv.find('text').text(`${SOFTWARE.serverVersion} → ${serverVersion}`);
|
$serverDiv.find('span.obsolete').css('display', 'inline-block');
|
||||||
|
$serverDiv.find('text').text(`${localVersion} → ${cloudVersion}`);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$serverDiv.find('span[value="latest"]').css('display', 'inline-block');
|
$serverDiv.find('span.latest').css('display', 'inline-block');
|
||||||
$serverDiv.find('text').text(SOFTWARE.serverVersion);
|
$serverDiv.find('text').text(localVersion);
|
||||||
}
|
}
|
||||||
if (needUpdateServer) {
|
if (needsUpdate) {
|
||||||
$btnDiv.css('display', 'flex');
|
$btnDiv.css('display', 'flex');
|
||||||
$btnDiv.children('button').off().click((event) => {
|
$btnDiv.children('button').off().one('click', (event) => {
|
||||||
LayerExt.open({
|
const eventSource = new EventSource(`/api/download?url=${encodeURIComponent(url)}&cloudVersion=${cloudVersion}`);
|
||||||
title: Msg.getLang('PROGRESS'),
|
$mixlyProgress.show();
|
||||||
id: 'setting-menu-update-layer',
|
eventSource.onmessage = function(event) {
|
||||||
shade: LayerExt.SHADE_ALL,
|
const data = JSON.parse(event.data);
|
||||||
area: ['40%', '60%'],
|
if (data.type === 'progress') {
|
||||||
max: ['800px', '300px'],
|
$mixlyProgress.progress({
|
||||||
min: ['500px', '100px'],
|
percent: data.progress
|
||||||
success: (layero, index) => {
|
|
||||||
$('#setting-menu-update-layer').css('overflow', 'hidden');
|
|
||||||
layero.find('.layui-layer-setwin').css('display', 'none');
|
|
||||||
Setting.ace = Setting.createAceEditor('setting-menu-update-layer');
|
|
||||||
Setting.ace.resize();
|
|
||||||
const { Socket } = Mixly.WebSocket;
|
|
||||||
Socket.sendCommand({
|
|
||||||
obj: 'Socket',
|
|
||||||
func: 'updateSW',
|
|
||||||
args: []
|
|
||||||
});
|
});
|
||||||
},
|
} else if (data.type === 'unzip') {
|
||||||
resizing: (layero) => {
|
$mixlyProgress.addClass('swinging indeterminate');
|
||||||
Setting.ace.resize();
|
$mixlyProgress.progress({
|
||||||
},
|
percent: 100
|
||||||
end: () => {
|
});
|
||||||
|
$mixlyProgress.find('.progress').hide();
|
||||||
|
layer.msg('解压中...', { time: 1000 });
|
||||||
|
} else if (data.type === 'complete') {
|
||||||
|
$mixlyProgress.removeClass('swinging indeterminate');
|
||||||
|
layer.msg('更新完成!5秒后自动刷新...', { time: 1000 });
|
||||||
|
eventSource.close();
|
||||||
|
setTimeout(function(){
|
||||||
|
window.location.reload();
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
eventSource.onerror = function(error) {
|
||||||
|
layer.msg('下载失败!5秒后自动刷新...', { time: 1000 });
|
||||||
|
setTimeout(function(){
|
||||||
|
window.location.reload();
|
||||||
|
}, 5000);
|
||||||
|
eventSource.close();
|
||||||
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$btnDiv.css('display', 'none');
|
$btnDiv.css('display', 'none');
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
|
||||||
$('#setting-menu-update').loading('destroy');
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Setting.showUpdateMessage = (data) => {
|
Setting.showUpdateMessage = (data) => {
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ if (Env.hasSocketServer) {
|
|||||||
if (env === 'electron' && !goog.isElectron) {
|
if (env === 'electron' && !goog.isElectron) {
|
||||||
env = 'web';
|
env = 'web';
|
||||||
}
|
}
|
||||||
|
if (typeof nw === 'object') {
|
||||||
|
env = 'nw';
|
||||||
|
}
|
||||||
|
|
||||||
XML.TEMPLATE_CONFIG = [
|
XML.TEMPLATE_CONFIG = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
"layui",
|
"layui",
|
||||||
"store",
|
"store",
|
||||||
"$.select2",
|
"$.select2",
|
||||||
|
"$.fomanticUI",
|
||||||
"Mixly.XML",
|
"Mixly.XML",
|
||||||
"Mixly.LayerExt",
|
"Mixly.LayerExt",
|
||||||
"Mixly.Msg",
|
"Mixly.Msg",
|
||||||
|
|||||||
@@ -167,22 +167,52 @@
|
|||||||
#setting-menu-update .layui-panel {
|
#setting-menu-update .layui-panel {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setting-menu-update .latest,
|
||||||
|
#setting-menu-update .obsolete {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setting-menu-update-server .mixly-progress {
|
||||||
|
display: none;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setting-menu-update-server .bar {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setting-menu-update-server .bar > .progress {
|
||||||
|
/*display: none;*/
|
||||||
|
height: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 1px;
|
||||||
|
-webkit-box-shadow: unset;
|
||||||
|
box-shadow: unset;
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="setting-menu" class="layui-layer-wrap">
|
<div id="setting-menu" class="layui-layer-wrap">
|
||||||
<div id="setting-menu-top">
|
<div id="setting-menu-top">
|
||||||
<div id="setting-menu-left">
|
<div id="setting-menu-left">
|
||||||
<ul id="setting-menu-options" class="layui-nav layui-nav-tree layui-bg-cyan layui-inline"
|
<ul id="setting-menu-options" class="layui-nav layui-nav-tree layui-bg-cyan layui-inline"
|
||||||
lay-filter="setting-menu-filter">
|
lay-filter="setting-menu-filter">
|
||||||
<li class="layui-nav-item layui-this" lay-id="0">
|
<li class="layui-nav-item layui-this" lay-id="0" data-type="config">
|
||||||
<a m-id="0">{{ d.personalise }}</a>
|
<a m-id="0">{{ d.personalise }}</a>
|
||||||
</li>
|
</li>
|
||||||
{{# if(d.env === 'electron'){ }}
|
{{# if(d.env === 'electron'){ }}
|
||||||
<li class="layui-nav-item" lay-id="1">
|
<li class="layui-nav-item" lay-id="1" data-type="import-board">
|
||||||
<a m-id="1" href="javascript:;">{{ d.importBoard }}</a>
|
<a m-id="1" href="javascript:;">{{ d.importBoard }}</a>
|
||||||
</li>
|
</li>
|
||||||
{{# } }}
|
{{# } }}
|
||||||
{{# if(d.env === 'web-socket'){ }}
|
{{# if(d.env === 'web-socket'){ }}
|
||||||
<li class="layui-nav-item" lay-id="2">
|
<li class="layui-nav-item" lay-id="2" data-type="ws-update">
|
||||||
|
<a m-id="2">{{ d.checkForUpdates }}</a>
|
||||||
|
</li>
|
||||||
|
{{# } }}
|
||||||
|
{{# if(d.env === 'nw'){ }}
|
||||||
|
<li class="layui-nav-item" lay-id="3" data-type="nw-update">
|
||||||
<a m-id="2">{{ d.checkForUpdates }}</a>
|
<a m-id="2">{{ d.checkForUpdates }}</a>
|
||||||
</li>
|
</li>
|
||||||
{{# } }}
|
{{# } }}
|
||||||
@@ -341,19 +371,24 @@
|
|||||||
<div class="layui-hide" id="import-board-page" lay-filter="import-board-page-filter"></div>
|
<div class="layui-hide" id="import-board-page" lay-filter="import-board-page-filter"></div>
|
||||||
</div>
|
</div>
|
||||||
{{# } }}
|
{{# } }}
|
||||||
{{# if(d.env === 'web-socket'){ }}
|
{{# if(d.env === 'web-socket' || d.env === 'nw'){ }}
|
||||||
<div class="menu-body">
|
<div class="menu-body">
|
||||||
<div id="setting-menu-update" class="setting-menu-info">
|
<div id="setting-menu-update" class="setting-menu-info">
|
||||||
<div>
|
<div>
|
||||||
<div id="setting-menu-update-server" class="layui-card layui-panel">
|
<div id="setting-menu-update-server" class="layui-card layui-panel">
|
||||||
<div class="layui-card-header">
|
<div class="layui-card-header">
|
||||||
<label>{{ d.server }}</label>
|
<label>{{ d.server }}</label>
|
||||||
<span class="layui-badge layui-bg-green" value="latest">{{ d.latest }}</span>
|
<span class="latest layui-badge layui-bg-green" value="latest">{{ d.latest }}</span>
|
||||||
<span class="layui-badge" value="obsolete">{{ d.obsolete }}</span>
|
<span class="obsolete layui-badge" value="obsolete">{{ d.obsolete }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-card-body" class="">
|
<div class="layui-card-body" class="">
|
||||||
<label>{{ d.version }}: </label>
|
<label>{{ d.version }}: </label>
|
||||||
<text>1.0.0</text>
|
<text></text>
|
||||||
|
<div class="mixly-progress ui teal progress">
|
||||||
|
<div class="bar">
|
||||||
|
<div class="progress"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
"author": "Mixly",
|
"author": "Mixly",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"adm-zip": "^0.5.16",
|
||||||
|
"axios": "^0.27.2",
|
||||||
"commander": "^11.1.0",
|
"commander": "^11.1.0",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.8.1",
|
"css-loader": "^6.8.1",
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
"nwjs-builder-phoenix": "^1.15.0"
|
"nwjs-builder-phoenix": "^1.15.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"adm-zip": "^0.5.16",
|
||||||
|
"axios": "^0.27.2",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"http": "^0.0.1-security",
|
"http": "^0.0.1-security",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
|
|||||||
245
static-server/api.js
Normal file
245
static-server/api.js
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const express = require('express');
|
||||||
|
const axios = require('axios');
|
||||||
|
const AdmZip = require('adm-zip');
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncAdmZip {
|
||||||
|
constructor(zipPath) {
|
||||||
|
this.zipPath = zipPath;
|
||||||
|
this.zip = new AdmZip(zipPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步解压到目录
|
||||||
|
async extractAllTo(targetPath, overwrite = true) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// 确保目标目录存在
|
||||||
|
fs.promises.mkdir(targetPath, { recursive: true })
|
||||||
|
.then(() => {
|
||||||
|
this.zip.extractAllTo(targetPath, overwrite);
|
||||||
|
resolve({
|
||||||
|
success: true,
|
||||||
|
targetPath,
|
||||||
|
fileCount: this.zip.getEntries().length
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步解压单个文件
|
||||||
|
async extractEntry(entryName, targetPath, overwrite = true) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const entry = this.zip.getEntry(entryName);
|
||||||
|
if (!entry) {
|
||||||
|
reject(new Error(`条目不存在: ${entryName}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.isDirectory) {
|
||||||
|
reject(new Error(`条目是目录: ${entryName}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保目标目录存在
|
||||||
|
const targetDir = path.dirname(targetPath);
|
||||||
|
fs.promises.mkdir(targetDir, { recursive: true })
|
||||||
|
.then(() => {
|
||||||
|
this.zip.extractEntryTo(entry, targetDir, false, overwrite);
|
||||||
|
resolve({
|
||||||
|
success: true,
|
||||||
|
entryName,
|
||||||
|
targetPath
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步获取zip文件信息
|
||||||
|
async getZipInfo() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const entries = this.zip.getEntries();
|
||||||
|
const info = {
|
||||||
|
fileCount: entries.length,
|
||||||
|
totalSize: entries.reduce((sum, entry) => sum + entry.header.size, 0),
|
||||||
|
entries: entries.map(entry => ({
|
||||||
|
name: entry.entryName,
|
||||||
|
size: entry.header.size,
|
||||||
|
isDirectory: entry.isDirectory,
|
||||||
|
compressedSize: entry.header.compressedSize
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
resolve(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
|
||||||
|
const TEMP_FOLDER_PATH = path.resolve(__dirname, '../temp');;
|
||||||
|
const VERSION_FILE = path.resolve(__dirname, '../version.json');
|
||||||
|
|
||||||
|
function getLocalVersion() {
|
||||||
|
if(fs.existsSync(TEMP_FOLDER_PATH)) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(VERSION_FILE)) {
|
||||||
|
const data = fs.readFileSync(VERSION_FILE, 'utf8');
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('读取版本文件失败:', error);
|
||||||
|
}
|
||||||
|
return '2025.09.06';
|
||||||
|
}
|
||||||
|
return 'None';
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveVersionInfo(version) {
|
||||||
|
fs.writeFileSync(VERSION_FILE, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCloudVersion() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('http://update.mixly.cn/index.php');
|
||||||
|
return response.data.mixly.all;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取云端版本信息失败:', error);
|
||||||
|
return {
|
||||||
|
file: 'mixly.zip',
|
||||||
|
version: '2025.09.06'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkUpdate() {
|
||||||
|
try {
|
||||||
|
const localVersion = getLocalVersion();
|
||||||
|
const cloudVersions = await getCloudVersion();
|
||||||
|
const cloudVersion = cloudVersions['version'];
|
||||||
|
const cloudFile = 'http://update.mixly.cn/download.php?file=' + cloudVersions['file']
|
||||||
|
return {
|
||||||
|
needsUpdate: localVersion !== cloudVersion,
|
||||||
|
localVersion: localVersion,
|
||||||
|
cloudVersion: cloudVersion,
|
||||||
|
cloudFile: cloudFile,
|
||||||
|
error: ''
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
needsUpdate: false,
|
||||||
|
localVersion: '',
|
||||||
|
cloudVersion: '',
|
||||||
|
cloudFile: '',
|
||||||
|
error: error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查更新接口
|
||||||
|
router.post('/check-update', async (req, res) => {
|
||||||
|
const updateInfo = await checkUpdate();
|
||||||
|
res.json(updateInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 下载进度返回
|
||||||
|
function deleteFolderRecursive(dirPath) {
|
||||||
|
if (fs.existsSync(dirPath)) {
|
||||||
|
fs.readdirSync(dirPath).forEach(file => {
|
||||||
|
const curPath = path.join(dirPath, file);
|
||||||
|
if (fs.lstatSync(curPath).isDirectory()) {
|
||||||
|
deleteFolderRecursive(curPath);
|
||||||
|
} else {
|
||||||
|
fs.unlinkSync(curPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fs.rmdirSync(dirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get('/download', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { url, cloudVersion } = req.query;
|
||||||
|
|
||||||
|
if(fs.existsSync(TEMP_FOLDER_PATH)) {
|
||||||
|
deleteFolderRecursive(TEMP_FOLDER_PATH);
|
||||||
|
}
|
||||||
|
fs.mkdirSync(TEMP_FOLDER_PATH);
|
||||||
|
const filePath = path.resolve(TEMP_FOLDER_PATH, 'mixly.zip');
|
||||||
|
const fileStream = fs.createWriteStream(filePath);
|
||||||
|
|
||||||
|
// 设置响应头
|
||||||
|
res.setHeader('Content-Type', 'text/event-stream');
|
||||||
|
res.setHeader('Transfer-Encoding', 'chunked');
|
||||||
|
|
||||||
|
// 发起下载请求
|
||||||
|
const response = await axios({
|
||||||
|
method: 'GET',
|
||||||
|
url: url,
|
||||||
|
responseType: 'stream'
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalSize = parseInt(response.headers['content-length'], 10);
|
||||||
|
let downloadedSize = 0;
|
||||||
|
let lastProgress = 0;
|
||||||
|
// 发送进度信息
|
||||||
|
const sendProgress = (progress) => {
|
||||||
|
if(progress !== lastProgress) {
|
||||||
|
res.write('data:' + JSON.stringify({ type: 'progress', progress }) + '\n\n');
|
||||||
|
lastProgress = progress;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送解压信息
|
||||||
|
const sendUnzip = () => {
|
||||||
|
res.write('data:' + JSON.stringify({ type: 'unzip' }) + '\n\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送完成信息
|
||||||
|
const sendComplete = (version) => {
|
||||||
|
res.write('data:' + JSON.stringify({ type: 'complete', version }) + '\n\n');
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
response.data.pipe(fileStream);
|
||||||
|
// 处理数据流
|
||||||
|
response.data.on('data', (chunk) => {
|
||||||
|
downloadedSize += chunk.length;
|
||||||
|
const progress = Math.round((downloadedSize / totalSize) * 100);
|
||||||
|
sendProgress(progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileStream.on('finish', async () => {
|
||||||
|
// 获取版本信息并保存
|
||||||
|
saveVersionInfo(cloudVersion);
|
||||||
|
sendUnzip();
|
||||||
|
try {
|
||||||
|
const asyncZip = new AsyncAdmZip(filePath);
|
||||||
|
await asyncZip.extractAllTo(path.resolve(__dirname, '../'));
|
||||||
|
sendComplete(cloudVersion);
|
||||||
|
if(fs.existsSync(TEMP_FOLDER_PATH)) {
|
||||||
|
deleteFolderRecursive(TEMP_FOLDER_PATH);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(500).json({ error: '解压失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error.message);
|
||||||
|
res.status(500).json({ error: '下载失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
const http = require('http');
|
const http = require('http');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const apiRoutes = require('./api.js');
|
||||||
|
|
||||||
const StaticServer = {};
|
const StaticServer = {};
|
||||||
|
|
||||||
StaticServer.run = (port) => {
|
StaticServer.run = (port) => {
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.static(path.resolve(__dirname, '../')));
|
app.use(express.static(path.resolve(__dirname, '../')));
|
||||||
|
app.use('/api/', apiRoutes);
|
||||||
const httpServer = http.createServer(app);
|
const httpServer = http.createServer(app);
|
||||||
httpServer.listen(port);
|
httpServer.listen(port);
|
||||||
console.log('Static服务器正在运行 [端口 - ' + port + ', http]...');
|
console.log('Static服务器正在运行 [端口 - ' + port + ', http]...');
|
||||||
|
|||||||
Reference in New Issue
Block a user