mirror of https://gitee.com/godoos/godoos.git
19 changed files with 346 additions and 248 deletions
@ -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 |
|
||||
} |
|
@ -1,8 +1,16 @@ |
|||||
export async function OpenDirDialog(){ |
export async function OpenDirDialog(){ |
||||
if((window as any).go) { |
if((window as any).go) { |
||||
//(window as any).go.OpenDirDialog();
|
return (window as any)['go']['app']['App']['OpenDirDialog'](); |
||||
return (window as any)['go']['main']['App']['OpenDirDialog'](); |
|
||||
}else { |
}else { |
||||
return "" |
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 { getSystemConfig,setSystemKey,parseJson } from '@/system/config' |
||||
//import manifest from '../../package.json';
|
import { RestartApp } from './goutil'; |
||||
import {isWindowsOS,getSystemConfig} from '@/system/config' |
import { Dialog } from '@/system'; |
||||
|
import { ElMessage } from 'element-plus' |
||||
export async function checkUpdate() { |
export async function checkUpdate() { |
||||
if(!(window as any).go) return; |
const config = getSystemConfig(); |
||||
const config = getSystemConfig(); |
const updateGiteeUrl = `${config.apiUrl}/system/updateInfo` |
||||
const updateGiteeUrl = `https://gitee.com/api/v5/repos/ruitao_admin/godoos/releases/` |
const releaseRes = await fetch(updateGiteeUrl) |
||||
const releaseRes = await fetch(updateGiteeUrl) |
if (!releaseRes.ok) return; |
||||
if(!releaseRes.ok) return; |
const releaseData = await releaseRes.json() |
||||
const releaseData = await releaseRes.json() |
const versionTag = releaseData.version; |
||||
const versionTag = releaseData.tag_name; |
if (!versionTag) return; |
||||
if(!versionTag) return; |
if (versionTag <= config.version) return; |
||||
if (versionTag.replace('v', '') <= config.version) return; |
const updateUrl = releaseData.url |
||||
const verifyUrl = `${updateGiteeUrl}tags/${versionTag}`; |
if (!updateUrl || updateUrl == '') return; |
||||
const verRes = await fetch(verifyUrl); |
const dialogRes: any = await Dialog.showMessageBox({ |
||||
if(!verRes.ok) return; |
title: '更新提示', |
||||
const verData = await verRes.json() |
message: `发现新版本:${versionTag},是否更新?` |
||||
if(!verData.assets || verData.assets.length <= 0) return; |
}) |
||||
const appName = "godoos"+ versionTag + (isWindowsOS() ? '.exe' : ''); |
//console.log(dialogRes)
|
||||
const updateUrl = `${updateGiteeUrl}download/${versionTag}/${appName}`; |
if (dialogRes.response !== -1) { |
||||
console.log(updateUrl) |
return; |
||||
// 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 { 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
@ -0,0 +1,2 @@ |
|||||
|
tmp |
||||
|
|
@ -1,4 +1,4 @@ |
|||||
package cmd |
package sys |
||||
|
|
||||
import ( |
import ( |
||||
"encoding/json" |
"encoding/json" |
@ -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) |
||||
|
|
||||
|
} |
||||
|
} |
@ -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 new issue