add update
This commit is contained in:
parent
86e49a26aa
commit
382fca2364
19 changed files with 346 additions and 248 deletions
|
|
@ -13,8 +13,7 @@ import (
|
|||
|
||||
// App struct
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
exDir string
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
|
|
|
|||
102
app/update.go
102
app/update.go
|
|
@ -1,102 +0,0 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/selfupdate"
|
||||
wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
type ProgressReader struct {
|
||||
reader io.Reader
|
||||
total int64
|
||||
err error
|
||||
}
|
||||
type DownloadStatus struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Url string `json:"url"`
|
||||
Transferred int64 `json:"transferred"`
|
||||
Size int64 `json:"size"`
|
||||
Speed float64 `json:"speed"`
|
||||
Progress float64 `json:"progress"`
|
||||
Downloading bool `json:"downloading"`
|
||||
Done bool `json:"done"`
|
||||
}
|
||||
|
||||
func (pr *ProgressReader) Read(p []byte) (n int, err error) {
|
||||
n, err = pr.reader.Read(p)
|
||||
pr.err = err
|
||||
pr.total += int64(n)
|
||||
return
|
||||
}
|
||||
func (a *App) UpdateApp(url string) (broken bool, err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
pr := &ProgressReader{reader: resp.Body}
|
||||
|
||||
ticker := time.NewTicker(250 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
// update progress
|
||||
go func() {
|
||||
for {
|
||||
<-ticker.C
|
||||
wruntime.EventsEmit(a.ctx, "updateApp", &DownloadStatus{
|
||||
Name: filepath.Base(url),
|
||||
Path: "",
|
||||
Url: url,
|
||||
Transferred: pr.total,
|
||||
Size: resp.ContentLength,
|
||||
Speed: 0,
|
||||
Progress: 100 * (float64(pr.total) / float64(resp.ContentLength)),
|
||||
Downloading: pr.err == nil && pr.total < resp.ContentLength,
|
||||
Done: pr.total == resp.ContentLength,
|
||||
})
|
||||
if pr.err != nil || pr.total == resp.ContentLength {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var updateFile io.Reader = pr
|
||||
// extract macos binary from zip
|
||||
if strings.HasSuffix(url, ".zip") && runtime.GOOS == "darwin" {
|
||||
zipBytes, err := io.ReadAll(pr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
archive, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
file, err := archive.Open("godoos.app/Contents/MacOS/godoos")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
updateFile = file
|
||||
}
|
||||
|
||||
// apply update
|
||||
err = selfupdate.Apply(updateFile, selfupdate.Options{})
|
||||
if err != nil {
|
||||
if rerr := selfupdate.RollbackError(err); rerr != nil {
|
||||
return true, rerr
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
// restart app
|
||||
a.RestartApp()
|
||||
return false, nil
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
<div class="setting-item" v-if="config.storeType === 'local'">
|
||||
<label>存储地址</label>
|
||||
<el-input v-model="config.storePath" @click="selectFile" />
|
||||
<el-input v-model="config.storePath" @click="selectFile()" />
|
||||
</div>
|
||||
<template v-if="config.storeType === 'net'">
|
||||
<div class="setting-item">
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export const getSystemConfig = (ifset = false) => {
|
|||
// 初始化配置对象的各项属性,若本地存储中已存在则不进行覆盖
|
||||
if (!config.version) {
|
||||
config.version = '1.0.0';
|
||||
//config.version = '0.0.9';
|
||||
}
|
||||
if (!config.isFirstRun) {
|
||||
config.isFirstRun = false;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ import { Tray, TrayOptions } from './menu/Tary';
|
|||
import { InitSystemFile, InitUserFile } from './core/SystemFileConfig';
|
||||
import { createInitFile } from './core/createInitFile';
|
||||
import { getSystemConfig, getSystemKey, setSystemKey, setSystemConfig, clearSystemConfig } from './config'
|
||||
|
||||
import { checkUpdate } from '@/util/update';
|
||||
import { RestartApp } from '@/util/goutil';
|
||||
|
||||
export type OsPlugin = (system: System) => void;
|
||||
export type FileOpener = {
|
||||
|
|
@ -71,8 +72,6 @@ export class System {
|
|||
this._eventer = this.initEvent();
|
||||
this.firstRun();
|
||||
this.initSystem();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,11 +111,17 @@ export class System {
|
|||
this.initBackground(); // 初始化壁纸
|
||||
this.emit('start');
|
||||
|
||||
// setTimeout(() => {
|
||||
// if (this._rootState.magnet?.length < 1) {
|
||||
// this.refershAppList()
|
||||
// }
|
||||
// }, 6000);
|
||||
setTimeout(() => {
|
||||
if (this._rootState.magnet?.length < 1) {
|
||||
this.refershAppList()
|
||||
}
|
||||
checkUpdate();
|
||||
setTimeout(() => {
|
||||
if (this._rootState.magnet?.length < 1) {
|
||||
this.recover()
|
||||
}
|
||||
}, 3000);
|
||||
}, 6000);
|
||||
|
||||
}
|
||||
/**
|
||||
|
|
@ -382,7 +387,7 @@ export class System {
|
|||
}
|
||||
reboot() {
|
||||
this._rootState.state = SystemStateEnum.close;
|
||||
window.location.reload();
|
||||
RestartApp();
|
||||
}
|
||||
recover() {
|
||||
clearSystemConfig()
|
||||
|
|
@ -390,7 +395,7 @@ export class System {
|
|||
this.fs.removeFileSystem().then(() => {
|
||||
window.indexedDB.deleteDatabase("GodoDatabase");
|
||||
|
||||
window.location.reload();
|
||||
RestartApp();
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class Dialog {
|
|||
|
||||
return {
|
||||
setProgress,
|
||||
dialogwin
|
||||
};
|
||||
}
|
||||
public static showMessageBox(option: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,16 @@
|
|||
export async function OpenDirDialog(){
|
||||
if((window as any).go) {
|
||||
//(window as any).go.OpenDirDialog();
|
||||
return (window as any)['go']['main']['App']['OpenDirDialog']();
|
||||
return (window as any)['go']['app']['App']['OpenDirDialog']();
|
||||
}else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
export function RestartApp(){
|
||||
if(!(window as any).go){
|
||||
window.location.reload();
|
||||
}else{
|
||||
return (window as any)['go']['app']['App']['RestartApp']();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,62 @@
|
|||
//import { EventsOff, EventsOn } from '~/runtime';
|
||||
//import manifest from '../../package.json';
|
||||
import {isWindowsOS,getSystemConfig} from '@/system/config'
|
||||
import { getSystemConfig,setSystemKey,parseJson } from '@/system/config'
|
||||
import { RestartApp } from './goutil';
|
||||
import { Dialog } from '@/system';
|
||||
import { ElMessage } from 'element-plus'
|
||||
export async function checkUpdate() {
|
||||
if(!(window as any).go) return;
|
||||
const config = getSystemConfig();
|
||||
const updateGiteeUrl = `https://gitee.com/api/v5/repos/ruitao_admin/godoos/releases/`
|
||||
const releaseRes = await fetch(updateGiteeUrl)
|
||||
if(!releaseRes.ok) return;
|
||||
const releaseData = await releaseRes.json()
|
||||
const versionTag = releaseData.tag_name;
|
||||
if(!versionTag) return;
|
||||
if (versionTag.replace('v', '') <= config.version) return;
|
||||
const verifyUrl = `${updateGiteeUrl}tags/${versionTag}`;
|
||||
const verRes = await fetch(verifyUrl);
|
||||
if(!verRes.ok) return;
|
||||
const verData = await verRes.json()
|
||||
if(!verData.assets || verData.assets.length <= 0) return;
|
||||
const appName = "godoos"+ versionTag + (isWindowsOS() ? '.exe' : '');
|
||||
const updateUrl = `${updateGiteeUrl}download/${versionTag}/${appName}`;
|
||||
console.log(updateUrl)
|
||||
// fetch(`${updateGiteeUrl}latest`).then((r) => {
|
||||
// if (r.ok) {
|
||||
// r.json().then((data) => {
|
||||
// if (data.tag_name) {
|
||||
// const versionTag = data.tag_name;
|
||||
// console.log(versionTag)
|
||||
// if (versionTag.replace('v', '') > manifest.version) {
|
||||
// const verifyUrl = `${updateGiteeUrl}tags/${versionTag}`;
|
||||
// }
|
||||
/*
|
||||
if (versionTag.replace('v', '') > manifest.version) {
|
||||
const verifyUrl = `${updateGiteeUrl}tags/${versionTag}`;
|
||||
|
||||
fetch(verifyUrl).then((r) => {
|
||||
if (r.ok) {
|
||||
r.json().then((data) => {
|
||||
if (data.assets && data.assets.length > 0) {
|
||||
const asset = data.assets.find((a: any) => a.name.toLowerCase().includes(commonStore.platform.toLowerCase().replace('darwin', 'macos')));
|
||||
if (asset) {
|
||||
const updateUrl = `${updateGiteeUrl}download/${versionTag}/${asset.name}`;
|
||||
toastWithButton(t('New Version Available') + ': ' + versionTag, t('Update'), () => {
|
||||
DeleteFile('cache.json');
|
||||
const progressId = 'update_app';
|
||||
const progressEvent = 'updateApp';
|
||||
const updateProgress = (ds: DownloadStatus | null) => {
|
||||
const content =
|
||||
t('Downloading update, please wait. If it is not completed, please manually download the program from GitHub and replace the original program.')
|
||||
+ (ds ? ` (${ds.progress.toFixed(2)}% ${bytesToReadable(ds.transferred)}/${bytesToReadable(ds.size)})` : '');
|
||||
const options: ToastOptions = {
|
||||
type: 'info',
|
||||
position: 'bottom-left',
|
||||
autoClose: false,
|
||||
toastId: progressId,
|
||||
hideProgressBar: false,
|
||||
progress: ds ? ds.progress / 100 : 0
|
||||
};
|
||||
if (toast.isActive(progressId))
|
||||
toast.update(progressId, {
|
||||
render: content,
|
||||
...options
|
||||
});
|
||||
else
|
||||
toast(content, options);
|
||||
};
|
||||
updateProgress(null);
|
||||
EventsOn(progressEvent, updateProgress);
|
||||
UpdateApp(updateUrl).then(() => {
|
||||
toast(t('Update completed, please restart the program.'), {
|
||||
type: 'success',
|
||||
position: 'bottom-left',
|
||||
autoClose: false
|
||||
}
|
||||
);
|
||||
}).catch((e) => {
|
||||
toast(t('Update Error') + ' - ' + (e.message || e), {
|
||||
type: 'error',
|
||||
position: 'bottom-left',
|
||||
autoClose: false
|
||||
});
|
||||
}).finally(() => {
|
||||
toast.dismiss(progressId);
|
||||
EventsOff(progressEvent);
|
||||
});
|
||||
}, {
|
||||
autoClose: false,
|
||||
position: 'bottom-left'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error('Verify response was not ok.');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (notifyEvenLatest) {
|
||||
toast(t('This is the latest version'), { type: 'success', position: 'bottom-left', autoClose: 2000 });
|
||||
}
|
||||
}
|
||||
*/
|
||||
// } else {
|
||||
// throw new Error('Invalid response.');
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// throw new Error('Network response was not ok.');
|
||||
// }
|
||||
// }
|
||||
// ).catch((e) => {
|
||||
// //toast(t('Updates Check Error') + ' - ' + (e.message || e), { type: 'error', position: 'bottom-left' });
|
||||
// });
|
||||
const config = getSystemConfig();
|
||||
const updateGiteeUrl = `${config.apiUrl}/system/updateInfo`
|
||||
const releaseRes = await fetch(updateGiteeUrl)
|
||||
if (!releaseRes.ok) return;
|
||||
const releaseData = await releaseRes.json()
|
||||
const versionTag = releaseData.version;
|
||||
if (!versionTag) return;
|
||||
if (versionTag <= config.version) return;
|
||||
const updateUrl = releaseData.url
|
||||
if (!updateUrl || updateUrl == '') return;
|
||||
const dialogRes: any = await Dialog.showMessageBox({
|
||||
title: '更新提示',
|
||||
message: `发现新版本:${versionTag},是否更新?`
|
||||
})
|
||||
//console.log(dialogRes)
|
||||
if (dialogRes.response !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { setProgress,dialogwin } = Dialog.showProcessDialog({
|
||||
message: '正在更新',
|
||||
});
|
||||
const upUrl = `${config.apiUrl}/system/update?url=${updateUrl}`
|
||||
const upRes = await fetch(upUrl)
|
||||
if (!upRes.ok) return;
|
||||
const reader: any = upRes.body?.getReader();
|
||||
if (!reader) {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: "the system has not stream!"
|
||||
})
|
||||
}
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
reader.releaseLock();
|
||||
break;
|
||||
}
|
||||
const rawjson = new TextDecoder().decode(value);
|
||||
const json = parseJson(rawjson);
|
||||
console.log(json)
|
||||
if(json){
|
||||
if(json.progress){
|
||||
setProgress(json.progress)
|
||||
}
|
||||
if(json.updateCompleted){
|
||||
dialogwin.close()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: "update completed!"
|
||||
})
|
||||
setSystemKey('version',versionTag)
|
||||
RestartApp()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
frontend/wailsjs/go/app/App.d.ts
vendored
2
frontend/wailsjs/go/app/App.d.ts
vendored
|
|
@ -4,5 +4,3 @@
|
|||
export function OpenDirDialog():Promise<string>;
|
||||
|
||||
export function RestartApp():Promise<void>;
|
||||
|
||||
export function UpdateApp(arg1:string):Promise<boolean>;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,3 @@ export function OpenDirDialog() {
|
|||
export function RestartApp() {
|
||||
return window['go']['app']['App']['RestartApp']();
|
||||
}
|
||||
|
||||
export function UpdateApp(arg1) {
|
||||
return window['go']['app']['App']['UpdateApp'](arg1);
|
||||
}
|
||||
|
|
|
|||
6
go.mod
6
go.mod
|
|
@ -2,15 +2,13 @@ module godoos
|
|||
|
||||
go 1.22.5
|
||||
|
||||
require (
|
||||
github.com/minio/selfupdate v0.6.0
|
||||
github.com/wailsapp/wails/v2 v2.9.1
|
||||
)
|
||||
require github.com/wailsapp/wails/v2 v2.9.1
|
||||
|
||||
require (
|
||||
aead.dev/minisign v0.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/minio/selfupdate v0.6.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
|
|
|
|||
2
godo/.gitignore
vendored
Normal file
2
godo/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
tmp
|
||||
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"godo/libs"
|
||||
"godo/localchat"
|
||||
"godo/progress"
|
||||
"godo/sys"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
|
@ -38,8 +39,11 @@ func OsStart() {
|
|||
progressRouter.HandleFunc("/app/{name}/{subpath:.*}", progress.ForwardRequest).Methods(http.MethodGet, http.MethodPost)
|
||||
router.HandleFunc("/ping", progress.Ping).Methods(http.MethodGet)
|
||||
router.HandleFunc("/", progress.Ping).Methods(http.MethodGet)
|
||||
router.HandleFunc("/system/info", files.HandleSystemInfo).Methods(http.MethodGet)
|
||||
router.HandleFunc("/system/setting", HandleSetConfig).Methods(http.MethodPost)
|
||||
router.HandleFunc("/system/updateInfo", sys.GetUpdateUrlHandler).Methods(http.MethodGet)
|
||||
router.HandleFunc("/system/update", sys.UpdateAppHandler).Methods(http.MethodGet)
|
||||
router.HandleFunc("/system/storeList", sys.GetStoreInfoHandler).Methods(http.MethodGet)
|
||||
router.HandleFunc("/system/setting", sys.HandleSetConfig).Methods(http.MethodPost)
|
||||
router.HandleFunc("/files/info", files.HandleSystemInfo).Methods(http.MethodGet)
|
||||
router.HandleFunc("/file/read", files.HandleReadDir).Methods(http.MethodGet)
|
||||
router.HandleFunc("/file/stat", files.HandleStat).Methods(http.MethodGet)
|
||||
router.HandleFunc("/file/chmod", files.HandleChmod).Methods(http.MethodPost)
|
||||
|
|
|
|||
|
|
@ -73,10 +73,10 @@ func corsMiddleware() mux.MiddlewareFunc {
|
|||
w.Header().Set("Access-Control-Allow-Headers", allowHeaders)
|
||||
|
||||
// 如果是预检请求(OPTIONS),直接返回 200 OK
|
||||
// if r.Method == http.MethodOptions {
|
||||
// w.WriteHeader(http.StatusOK)
|
||||
// return
|
||||
// }
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,18 +3,21 @@ module godo
|
|||
go 1.22.5
|
||||
|
||||
require (
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/minio/selfupdate v0.6.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 // indirect
|
||||
aead.dev/minisign v0.2.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
)
|
||||
|
|
|
|||
21
godo/go.sum
21
godo/go.sum
|
|
@ -1,3 +1,5 @@
|
|||
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
|
||||
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
|
@ -8,6 +10,8 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
|
||||
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
|
@ -22,8 +26,25 @@ github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYg
|
|||
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b h1:QAqMVf3pSa6eeTsuklijukjXBlj7Es2QQplab+/RbQ4=
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package cmd
|
||||
package sys
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
41
godo/sys/store.go
Normal file
41
godo/sys/store.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package sys
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"godo/libs"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func GetStoreInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
cate := r.URL.Query().Get("cate")
|
||||
os := runtime.GOOS
|
||||
arch := runtime.GOARCH
|
||||
if cate == "" {
|
||||
libs.ErrorMsg(w, "cate is required")
|
||||
return
|
||||
}
|
||||
pluginUrl := "https://gitee.com/ruitao_admin/godoos-image/raw/master/store/" + os + "/" + arch + "/" + cate + ".json"
|
||||
res, err := http.Get(pluginUrl)
|
||||
if err != nil {
|
||||
libs.ErrorMsg(w, err.Error())
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode == 200 {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
libs.ErrorMsg(w, err.Error())
|
||||
return
|
||||
}
|
||||
var info interface{}
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
fmt.Println("Error unmarshalling JSON:", err)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(info)
|
||||
|
||||
}
|
||||
}
|
||||
175
godo/sys/update.go
Normal file
175
godo/sys/update.go
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package sys
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"godo/libs"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/minio/selfupdate"
|
||||
)
|
||||
|
||||
type OSInfo struct {
|
||||
Amd64 string `json:"amd64"`
|
||||
Arm64 string `json:"arm64"`
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
Version string `json:"version"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Changelog string `json:"changelog"`
|
||||
Windows OSInfo `json:"windows"`
|
||||
Linux OSInfo `json:"linux"`
|
||||
Darwin OSInfo `json:"darwin"`
|
||||
}
|
||||
|
||||
type ProgressReader struct {
|
||||
reader io.Reader
|
||||
total int64
|
||||
err error
|
||||
}
|
||||
type DownloadStatus struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Url string `json:"url"`
|
||||
Current int64 `json:"current"`
|
||||
Size int64 `json:"size"`
|
||||
Speed float64 `json:"speed"`
|
||||
Progress float64 `json:"progress"`
|
||||
Downloading bool `json:"downloading"`
|
||||
Done bool `json:"done"`
|
||||
}
|
||||
|
||||
func (pr *ProgressReader) Read(p []byte) (n int, err error) {
|
||||
n, err = pr.reader.Read(p)
|
||||
pr.err = err
|
||||
pr.total += int64(n)
|
||||
return
|
||||
}
|
||||
func UpdateAppHandler(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL.Query().Get("url")
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
pr := &ProgressReader{reader: resp.Body}
|
||||
|
||||
ticker := time.NewTicker(250 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
log.Printf("Streaming unsupported")
|
||||
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// update progress
|
||||
go func() {
|
||||
for {
|
||||
<-ticker.C
|
||||
rp := &DownloadStatus{
|
||||
Name: filepath.Base(url),
|
||||
Path: "",
|
||||
Url: url,
|
||||
Current: pr.total,
|
||||
Size: resp.ContentLength,
|
||||
Speed: 0,
|
||||
Progress: 100 * (float64(pr.total) / float64(resp.ContentLength)),
|
||||
Downloading: pr.err == nil && pr.total < resp.ContentLength,
|
||||
Done: pr.total == resp.ContentLength,
|
||||
}
|
||||
if pr.err != nil || pr.total == resp.ContentLength {
|
||||
break
|
||||
}
|
||||
if w != nil {
|
||||
jsonBytes, err := json.Marshal(rp)
|
||||
if err != nil {
|
||||
log.Printf("Error marshaling FileProgress to JSON: %v", err)
|
||||
continue
|
||||
}
|
||||
io.WriteString(w, string(jsonBytes))
|
||||
w.Write([]byte("\n"))
|
||||
flusher.Flush()
|
||||
} else {
|
||||
log.Println("ResponseWriter is nil, cannot send progress")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var updateFile io.Reader = pr
|
||||
// apply update
|
||||
err = selfupdate.Apply(updateFile, selfupdate.Options{})
|
||||
if err != nil {
|
||||
if rerr := selfupdate.RollbackError(err); rerr != nil {
|
||||
http.Error(w, "update error:"+rerr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
// 更新完成后发送响应给前端
|
||||
json.NewEncoder(w).Encode(map[string]bool{"updateCompleted": true})
|
||||
}
|
||||
|
||||
func GetUpdateUrlHandler(w http.ResponseWriter, r *http.Request) {
|
||||
updateUrl := "https://gitee.com/ruitao_admin/godoos-image/raw/master/version/version.json"
|
||||
res, err := http.Get(updateUrl)
|
||||
if err != nil {
|
||||
libs.ErrorMsg(w, err.Error())
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode == 200 {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
libs.ErrorMsg(w, err.Error())
|
||||
return
|
||||
}
|
||||
var info VersionInfo
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
fmt.Println("Error unmarshalling JSON:", err)
|
||||
return
|
||||
}
|
||||
//log.Printf("info: %v", info)
|
||||
// 根据操作系统和架构获取路径
|
||||
path := getPathForOSAndArch(&info)
|
||||
// 将结果以 JSON 格式返回给前端
|
||||
response := map[string]string{"url": path, "version": info.Version}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 根据操作系统和架构获取路径
|
||||
func getPathForOSAndArch(info *VersionInfo) string {
|
||||
os := runtime.GOOS
|
||||
arch := runtime.GOARCH
|
||||
switch os {
|
||||
case "windows":
|
||||
if arch == "amd64" {
|
||||
return info.Windows.Amd64
|
||||
} else if arch == "arm64" {
|
||||
return info.Windows.Arm64
|
||||
}
|
||||
case "linux":
|
||||
if arch == "amd64" {
|
||||
return info.Linux.Amd64
|
||||
} else if arch == "arm64" {
|
||||
return info.Linux.Arm64
|
||||
}
|
||||
case "darwin":
|
||||
if arch == "amd64" {
|
||||
return info.Darwin.Amd64
|
||||
} else if arch == "arm64" {
|
||||
return info.Darwin.Arm64
|
||||
}
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Loading…
Reference in a new issue