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(){ |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,2 @@ |
|||
tmp |
|||
|
@ -1,4 +1,4 @@ |
|||
package cmd |
|||
package sys |
|||
|
|||
import ( |
|||
"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