mirror of
https://github.com/earthjasonlin/zzz-signal-search-export.git
synced 2025-04-21 07:50:19 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
f1e3b76d85
|
|||
2736ee0398
|
|||
66188231bc
|
|||
5624af3fb2
|
|||
3c848a97f8
|
|||
6ddc29af5a
|
|||
e83fe42268
|
|||
f9e74b4fb8
|
|||
84179ccc8d
|
|||
8b725053ce
|
51
.electron-vite/update.js
Normal file
51
.electron-vite/update.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const AdmZip = require('adm-zip')
|
||||||
|
const { version } = require('../package.json')
|
||||||
|
|
||||||
|
const hash = (data, type = 'sha256') => {
|
||||||
|
const hmac = crypto.createHmac(type, 'nap')
|
||||||
|
hmac.update(data)
|
||||||
|
return hmac.digest('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
const createZip = (filePath, dest) => {
|
||||||
|
const zip = new AdmZip()
|
||||||
|
zip.addLocalFolder(filePath)
|
||||||
|
zip.toBuffer()
|
||||||
|
zip.writeZip(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = async () => {
|
||||||
|
copyAppZip()
|
||||||
|
const appPath = './build/win-ia32-unpacked/resources/app'
|
||||||
|
const name = 'app.zip'
|
||||||
|
const outputPath = path.resolve('./build/update/update/')
|
||||||
|
const zipPath = path.resolve(outputPath, name)
|
||||||
|
await fs.ensureDir(outputPath)
|
||||||
|
await fs.emptyDir(outputPath)
|
||||||
|
createZip(appPath, zipPath)
|
||||||
|
const buffer = await fs.readFile(zipPath)
|
||||||
|
const sha256 = hash(buffer)
|
||||||
|
const hashName = sha256.slice(7, 12)
|
||||||
|
await fs.copy(zipPath, path.resolve(outputPath, `${hashName}.zip`))
|
||||||
|
await fs.remove(zipPath)
|
||||||
|
await fs.outputJSON(path.join(outputPath, 'manifest.json'), {
|
||||||
|
active: true,
|
||||||
|
version,
|
||||||
|
from: '0.0.1',
|
||||||
|
name: `${hashName}.zip`,
|
||||||
|
hash: sha256
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyAppZip = () => {
|
||||||
|
try {
|
||||||
|
const dir = path.resolve('./build')
|
||||||
|
const filePath = path.resolve(dir, `ZzzSignalSearchExport-${version}-ia32-win.zip`)
|
||||||
|
fs.copySync(filePath, path.join(dir, 'app.zip'))
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
55
.github/workflows/release.yml
vendored
Normal file
55
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
# Sequence of patterns matched against refs/tag
|
||||||
|
tags:
|
||||||
|
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||||
|
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Release
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '16.x'
|
||||||
|
- name: Build App
|
||||||
|
run: |
|
||||||
|
yarn --frozen-lockfile
|
||||||
|
yarn build:win32
|
||||||
|
yarn build-update
|
||||||
|
- name: Create Release
|
||||||
|
if: success()
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ZzzSignalSearchExport ${{ github.ref }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
- name: Upload Release Asset
|
||||||
|
if: success()
|
||||||
|
id: upload-release-asset
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||||
|
asset_path: ./build/app.zip
|
||||||
|
asset_name: ZzzSignalSearchExport.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- name: Deploy update
|
||||||
|
if: success()
|
||||||
|
uses: crazy-max/ghaction-github-pages@v2
|
||||||
|
with:
|
||||||
|
commit_message: Update app
|
||||||
|
build_dir: ./build/update
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
@ -1,6 +1,6 @@
|
|||||||
# 绝区零记录导出工具
|
# 绝区零调频记录导出工具
|
||||||
|
|
||||||
中文 | [English](https://git.loliquq.cn/earthjasonlin/zzz-signal-search-export/blob/main/docs/README_EN.md)
|
中文 | [English](https://github.com/earthjasonlin/zzz-signal-search-export/blob/main/docs/README_EN.md)
|
||||||
|
|
||||||
这个项目由[star-rail-warp-export](https://github.com/biuuu/star-rail-warp-export/)修改而来,功能基本一致。
|
这个项目由[star-rail-warp-export](https://github.com/biuuu/star-rail-warp-export/)修改而来,功能基本一致。
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 42 KiB |
@ -1,6 +1,6 @@
|
|||||||
# Zenless Zone Zero Signal Search History Exporter
|
# Zenless Zone Zero Signal Search History Exporter
|
||||||
|
|
||||||
[中文](https://git.loliquq.cn/earthjasonlin/zzz-signal-search-export/blob/main/README.md) | English
|
[中文](https://github.com/earthjasonlin/zzz-signal-search-export) | English
|
||||||
|
|
||||||
This project is modified from the [star-rail-warp-export](https://github.com/biuuu/star-rail-warp-export/) repository, and its functions are basically the same.
|
This project is modified from the [star-rail-warp-export](https://github.com/biuuu/star-rail-warp-export/) repository, and its functions are basically the same.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "zzz-signal-search-export",
|
"name": "zzz-signal-search-export",
|
||||||
"version": "1.0.4",
|
"version": "1.0.7",
|
||||||
"main": "./dist/electron/main/main.js",
|
"main": "./dist/electron/main/main.js",
|
||||||
"author": "earthjasonlin <https://git.loliquq.cn/earthjasonlin>",
|
"author": "earthjasonlin <https://git.loliquq.cn/earthjasonlin>",
|
||||||
"homepage": "https://github.com/earthjasonlin/zzz-signal-search-export",
|
"homepage": "https://github.com/earthjasonlin/zzz-signal-search-export",
|
||||||
@ -16,6 +16,7 @@
|
|||||||
"build:dir": "cross-env BUILD_TARGET=clean node .electron-vite/build.js && electron-builder --dir",
|
"build:dir": "cross-env BUILD_TARGET=clean node .electron-vite/build.js && electron-builder --dir",
|
||||||
"build:clean": "cross-env BUILD_TARGET=onlyClean node .electron-vite/build.js",
|
"build:clean": "cross-env BUILD_TARGET=onlyClean node .electron-vite/build.js",
|
||||||
"build:web": "cross-env BUILD_TARGET=web node .electron-vite/build.js",
|
"build:web": "cross-env BUILD_TARGET=web node .electron-vite/build.js",
|
||||||
|
"build-update": "node .electron-vite/update.js",
|
||||||
"dev:web": "cross-env TARGET=web node .electron-vite/dev-runner.js",
|
"dev:web": "cross-env TARGET=web node .electron-vite/dev-runner.js",
|
||||||
"start": "electron ./src/main/main.js",
|
"start": "electron ./src/main/main.js",
|
||||||
"dep:upgrade": "yarn upgrade-interactive --latest",
|
"dep:upgrade": "yarn upgrade-interactive --latest",
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
"log.proxy.hint": "Using proxy mode [${ip}:${port}] to get URL,please reopen warp history inside the game client.",
|
"log.proxy.hint": "Using proxy mode [${ip}:${port}] to get URL,please reopen warp history inside the game client.",
|
||||||
"log.url.notFound2": "Unable to find URL, please make sure you already opened warp history inside the game client",
|
"log.url.notFound2": "Unable to find URL, please make sure you already opened warp history inside the game client",
|
||||||
"log.url.incorrect": "Unable to get URL parameters",
|
"log.url.incorrect": "Unable to get URL parameters",
|
||||||
|
"log.autoUpdate.success": "Auto update successful,please restart the program",
|
||||||
"excel.header.time": "time",
|
"excel.header.time": "time",
|
||||||
"excel.header.name": "name",
|
"excel.header.name": "name",
|
||||||
"excel.header.type": "type",
|
"excel.header.type": "type",
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
"log.proxy.hint": "正在使用代理模式[${ip}:${port}]获取URL,请重新打开游戏抽卡记录。",
|
"log.proxy.hint": "正在使用代理模式[${ip}:${port}]获取URL,请重新打开游戏抽卡记录。",
|
||||||
"log.url.notFound2": "未找到URL,请确认是否已打开游戏抽卡记录",
|
"log.url.notFound2": "未找到URL,请确认是否已打开游戏抽卡记录",
|
||||||
"log.url.incorrect": "获取URL参数失败",
|
"log.url.incorrect": "获取URL参数失败",
|
||||||
|
"log.autoUpdate.success": "自动更新已完成,重启工具后生效",
|
||||||
"excel.header.time": "时间",
|
"excel.header.time": "时间",
|
||||||
"excel.header.name": "名称",
|
"excel.header.name": "名称",
|
||||||
"excel.header.type": "类别",
|
"excel.header.type": "类别",
|
||||||
|
@ -82,11 +82,17 @@ const start = async () => {
|
|||||||
arr.push(log.time)
|
arr.push(log.time)
|
||||||
arr.push(log.name)
|
arr.push(log.name)
|
||||||
arr.push(log.item_type)
|
arr.push(log.item_type)
|
||||||
arr.push(log.rank_type)
|
if(log.rank_type === '2') {
|
||||||
|
arr.push(i18n.ui.data.star2)
|
||||||
|
} else if(log.rank_type === '3') {
|
||||||
|
arr.push(i18n.ui.data.star3)
|
||||||
|
} else {
|
||||||
|
arr.push(i18n.ui.data.star4)
|
||||||
|
}
|
||||||
arr.push(total)
|
arr.push(total)
|
||||||
arr.push(pity)
|
arr.push(pity)
|
||||||
temp.push(arr)
|
temp.push(arr)
|
||||||
if (log.rank_type === '5') {
|
if (log.rank_type === '4') {
|
||||||
pity = 0
|
pity = 0
|
||||||
}
|
}
|
||||||
// if (key === '301') {
|
// if (key === '301') {
|
||||||
@ -133,14 +139,14 @@ const start = async () => {
|
|||||||
}
|
}
|
||||||
// rare rank background color
|
// rare rank background color
|
||||||
const rankColor = {
|
const rankColor = {
|
||||||
3: "ff8e8e8e",
|
2: "ff8e8e8e",
|
||||||
4: "ffa256e1",
|
3: "ffa256e1",
|
||||||
5: "ffbd6932",
|
4: "ffbd6932",
|
||||||
}
|
}
|
||||||
sheet.getCell(`${c}${i + 2}`).font = {
|
sheet.getCell(`${c}${i + 2}`).font = {
|
||||||
name: customFont,
|
name: customFont,
|
||||||
color: { argb: rankColor[v.rank_type] },
|
color: { argb: rankColor[v.rank_type] },
|
||||||
bold : v.rank_type != "3"
|
bold : v.rank_type != "2"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,7 @@ require('./getData')
|
|||||||
require('./bridge')
|
require('./bridge')
|
||||||
require('./excel')
|
require('./excel')
|
||||||
require('./SRGFJson')
|
require('./SRGFJson')
|
||||||
|
const { getUpdateInfo } = require('./update/index')
|
||||||
|
|
||||||
const isDev = !app.isPackaged
|
const isDev = !app.isPackaged
|
||||||
let win = null
|
let win = null
|
||||||
@ -53,6 +54,12 @@ if (!isFirstInstance) {
|
|||||||
if (proxyStatus.started) {
|
if (proxyStatus.started) {
|
||||||
disableProxy()
|
disableProxy()
|
||||||
}
|
}
|
||||||
|
if (getUpdateInfo().status === 'moving') {
|
||||||
|
e.preventDefault()
|
||||||
|
setTimeout(() => {
|
||||||
|
app.quit()
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.on('quit', () => {
|
app.on('quit', () => {
|
||||||
|
185
src/main/module/extract-zip.js
Normal file
185
src/main/module/extract-zip.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright (c) 2014 Max Ogden and other contributors
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
// * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
// list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
// https://github.com/maxogden/extract-zip
|
||||||
|
// eslint-disable-next-line node/no-unsupported-features/node-builtins
|
||||||
|
const { createWriteStream, promises: fs } = require('original-fs')
|
||||||
|
const getStream = require('get-stream')
|
||||||
|
const path = require('path')
|
||||||
|
const { promisify } = require('util')
|
||||||
|
const stream = require('stream')
|
||||||
|
const yauzl = require('yauzl')
|
||||||
|
|
||||||
|
const openZip = promisify(yauzl.open)
|
||||||
|
const pipeline = promisify(stream.pipeline)
|
||||||
|
|
||||||
|
class Extractor {
|
||||||
|
constructor (zipPath, opts) {
|
||||||
|
this.zipPath = zipPath
|
||||||
|
this.opts = opts
|
||||||
|
}
|
||||||
|
|
||||||
|
async extract () {
|
||||||
|
|
||||||
|
this.zipfile = await openZip(this.zipPath, { lazyEntries: true })
|
||||||
|
this.canceled = false
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.zipfile.on('error', err => {
|
||||||
|
this.canceled = true
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
this.zipfile.readEntry()
|
||||||
|
|
||||||
|
this.zipfile.on('close', () => {
|
||||||
|
if (!this.canceled) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.zipfile.on('entry', async entry => {
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (this.canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (entry.fileName.startsWith('__MACOSX/')) {
|
||||||
|
this.zipfile.readEntry()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const destDir = path.dirname(path.join(this.opts.dir, entry.fileName))
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.mkdir(destDir, { recursive: true })
|
||||||
|
|
||||||
|
const canonicalDestDir = await fs.realpath(destDir)
|
||||||
|
const relativeDestDir = path.relative(this.opts.dir, canonicalDestDir)
|
||||||
|
|
||||||
|
if (relativeDestDir.split(path.sep).includes('..')) {
|
||||||
|
throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${entry.fileName}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.extractEntry(entry)
|
||||||
|
this.zipfile.readEntry()
|
||||||
|
} catch (err) {
|
||||||
|
this.canceled = true
|
||||||
|
this.zipfile.close()
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async extractEntry (entry) {
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (this.canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.opts.onEntry) {
|
||||||
|
this.opts.onEntry(entry, this.zipfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = path.join(this.opts.dir, entry.fileName)
|
||||||
|
|
||||||
|
// convert external file attr int into a fs stat mode int
|
||||||
|
const mode = (entry.externalFileAttributes >> 16) & 0xFFFF
|
||||||
|
// check if it's a symlink or dir (using stat mode constants)
|
||||||
|
const IFMT = 61440
|
||||||
|
const IFDIR = 16384
|
||||||
|
const IFLNK = 40960
|
||||||
|
const symlink = (mode & IFMT) === IFLNK
|
||||||
|
let isDir = (mode & IFMT) === IFDIR
|
||||||
|
|
||||||
|
// Failsafe, borrowed from jsZip
|
||||||
|
if (!isDir && entry.fileName.endsWith('/')) {
|
||||||
|
isDir = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for windows weird way of specifying a directory
|
||||||
|
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
|
||||||
|
const madeBy = entry.versionMadeBy >> 8
|
||||||
|
if (!isDir) isDir = (madeBy === 0 && entry.externalFileAttributes === 16)
|
||||||
|
|
||||||
|
|
||||||
|
const procMode = this.getExtractedMode(mode, isDir) & 0o777
|
||||||
|
|
||||||
|
// always ensure folders are created
|
||||||
|
const destDir = isDir ? dest : path.dirname(dest)
|
||||||
|
|
||||||
|
const mkdirOptions = { recursive: true }
|
||||||
|
if (isDir) {
|
||||||
|
mkdirOptions.mode = procMode
|
||||||
|
}
|
||||||
|
await fs.mkdir(destDir, mkdirOptions)
|
||||||
|
if (isDir) return
|
||||||
|
|
||||||
|
const readStream = await promisify(this.zipfile.openReadStream.bind(this.zipfile))(entry)
|
||||||
|
|
||||||
|
if (symlink) {
|
||||||
|
const link = await getStream(readStream)
|
||||||
|
await fs.symlink(link, dest)
|
||||||
|
} else {
|
||||||
|
await pipeline(readStream, createWriteStream(dest, { mode: procMode }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getExtractedMode (entryMode, isDir) {
|
||||||
|
let mode = entryMode
|
||||||
|
// Set defaults, if necessary
|
||||||
|
if (mode === 0) {
|
||||||
|
if (isDir) {
|
||||||
|
if (this.opts.defaultDirMode) {
|
||||||
|
mode = parseInt(this.opts.defaultDirMode, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mode) {
|
||||||
|
mode = 0o755
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.opts.defaultFileMode) {
|
||||||
|
mode = parseInt(this.opts.defaultFileMode, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mode) {
|
||||||
|
mode = 0o644
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function (zipPath, opts) {
|
||||||
|
|
||||||
|
if (!path.isAbsolute(opts.dir)) {
|
||||||
|
throw new Error('Target directory is expected to be absolute')
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.mkdir(opts.dir, { recursive: true })
|
||||||
|
opts.dir = await fs.realpath(opts.dir)
|
||||||
|
return new Extractor(zipPath, opts).extract()
|
||||||
|
}
|
65
src/main/update/index.js
Normal file
65
src/main/update/index.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
const { app } = require('electron')
|
||||||
|
const fetch = require('electron-fetch').default
|
||||||
|
const semver = require('semver')
|
||||||
|
const util = require('util')
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const extract = require('../module/extract-zip')
|
||||||
|
const { version } = require('../../../package.json')
|
||||||
|
const { hash, sendMsg } = require('../utils')
|
||||||
|
const config = require('../config')
|
||||||
|
const i18n = require('../i18n')
|
||||||
|
const streamPipeline = util.promisify(require('stream').pipeline)
|
||||||
|
|
||||||
|
async function download(url, filePath) {
|
||||||
|
const response = await fetch(url)
|
||||||
|
if (!response.ok) throw new Error(`unexpected response ${response.statusText}`)
|
||||||
|
await streamPipeline(response.body, fs.createWriteStream(filePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateInfo = {
|
||||||
|
status: 'init'
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDev = !app.isPackaged
|
||||||
|
const appPath = isDev ? path.resolve(__dirname, '../../', 'update-dev/app'): app.getAppPath()
|
||||||
|
const updatePath = isDev ? path.resolve(__dirname, '../../', 'update-dev/download') : path.resolve(appPath, '..', '..', 'update')
|
||||||
|
|
||||||
|
const update = async () => {
|
||||||
|
if (isDev) return
|
||||||
|
try {
|
||||||
|
const url = 'https://earthjasonlin.github.io/zzz-signal-search-export/update'
|
||||||
|
const res = await fetch(`${url}/manifest.json?t=${Math.floor(Date.now() / (1000 * 60 * 10))}`)
|
||||||
|
const data = await res.json()
|
||||||
|
if (!data.active) return
|
||||||
|
if (semver.gt(data.version, version) && semver.gte(version, data.from)) {
|
||||||
|
await fs.emptyDir(updatePath)
|
||||||
|
const filePath = path.join(updatePath, data.name)
|
||||||
|
if (!config.autoUpdate) {
|
||||||
|
sendMsg(data.version, 'NEW_VERSION')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateInfo.status = 'downloading'
|
||||||
|
await download(`${url}/${data.name}`, filePath)
|
||||||
|
const buffer = await fs.readFile(filePath)
|
||||||
|
const sha256 = hash(buffer)
|
||||||
|
if (sha256 !== data.hash) return
|
||||||
|
const appPathTemp = path.join(updatePath, 'app')
|
||||||
|
await extract(filePath, { dir: appPathTemp })
|
||||||
|
updateInfo.status = 'moving'
|
||||||
|
await fs.emptyDir(appPath)
|
||||||
|
await fs.copy(appPathTemp, appPath)
|
||||||
|
updateInfo.status = 'finished'
|
||||||
|
sendMsg(i18n.log.autoUpdate.success, 'UPDATE_HINT')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
updateInfo.status = 'failed'
|
||||||
|
sendMsg(e, 'ERROR')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUpdateInfo = () => updateInfo
|
||||||
|
|
||||||
|
setTimeout(update, 1000)
|
||||||
|
|
||||||
|
exports.getUpdateInfo = getUpdateInfo
|
@ -142,12 +142,12 @@ const readJSON = async (dataPath, name) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hash = (data, type = 'sha256') => {
|
const hash = (data, type = 'sha256') => {
|
||||||
const hmac = crypto.createHmac(type, 'hk4e')
|
const hmac = crypto.createHmac(type, 'nap')
|
||||||
hmac.update(data)
|
hmac.update(data)
|
||||||
return hmac.digest('hex')
|
return hmac.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
const scryptKey = crypto.scryptSync(userPath, 'hk4e', 24)
|
const scryptKey = crypto.scryptSync(userPath, 'nap', 24)
|
||||||
const cipherAes = (data) => {
|
const cipherAes = (data) => {
|
||||||
const algorithm = 'aes-192-cbc'
|
const algorithm = 'aes-192-cbc'
|
||||||
const iv = Buffer.alloc(16, 0)
|
const iv = Buffer.alloc(16, 0)
|
||||||
|
@ -327,6 +327,11 @@ onMounted(async () => {
|
|||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcRenderer.on('UPDATE_HINT', (event, message) => {
|
||||||
|
state.log = message
|
||||||
|
state.status = 'updated'
|
||||||
|
})
|
||||||
|
|
||||||
ipcRenderer.on('AUTHKEY_TIMEOUT', (event, message) => {
|
ipcRenderer.on('AUTHKEY_TIMEOUT', (event, message) => {
|
||||||
state.authkeyTimeout = message
|
state.authkeyTimeout = message
|
||||||
})
|
})
|
||||||
|
@ -23,6 +23,12 @@
|
|||||||
<el-button type="primary" plain @click="state.showDataDialog = true">{{common.dataManage}}</el-button>
|
<el-button type="primary" plain @click="state.showDataDialog = true">{{common.dataManage}}</el-button>
|
||||||
<p class="text-gray-400 text-xs m-1.5">{{text.dataManagerHint}}</p>
|
<p class="text-gray-400 text-xs m-1.5">{{text.dataManagerHint}}</p>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="text.autoUpdate">
|
||||||
|
<el-switch
|
||||||
|
@change="saveSetting"
|
||||||
|
v-model="settingForm.autoUpdate">
|
||||||
|
</el-switch>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="text.fetchFullHistory">
|
<el-form-item :label="text.fetchFullHistory">
|
||||||
<el-switch
|
<el-switch
|
||||||
@change="saveSetting"
|
@change="saveSetting"
|
||||||
@ -92,6 +98,7 @@ const settingForm = reactive({
|
|||||||
lang: 'zh-cn',
|
lang: 'zh-cn',
|
||||||
logType: 1,
|
logType: 1,
|
||||||
proxyMode: true,
|
proxyMode: true,
|
||||||
|
autoUpdate: true,
|
||||||
fetchFullHistory: false,
|
fetchFullHistory: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -105,7 +112,7 @@ const text = computed(() => props.i18n.ui.setting)
|
|||||||
const about = computed(() => props.i18n.ui.about)
|
const about = computed(() => props.i18n.ui.about)
|
||||||
|
|
||||||
const saveSetting = async () => {
|
const saveSetting = async () => {
|
||||||
const keys = ['lang', 'logType', 'proxyMode', 'fetchFullHistory']
|
const keys = ['lang', 'logType', 'proxyMode', 'autoUpdate', 'fetchFullHistory']
|
||||||
for (let key of keys) {
|
for (let key of keys) {
|
||||||
await ipcRenderer.invoke('SAVE_CONFIG', [key, settingForm[key]])
|
await ipcRenderer.invoke('SAVE_CONFIG', [key, settingForm[key]])
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user