@@ -19,6 +19,7 @@ import (
1919 "github.com/gin-contrib/gzip"
2020 "github.com/gin-gonic/gin"
2121 "github.com/gowvp/owl/internal/core/sms"
22+ "github.com/gowvp/owl/pkg/ota"
2223 "github.com/gowvp/owl/plugin/stat"
2324 "github.com/gowvp/owl/plugin/stat/statapi"
2425 "github.com/ixugo/goddd/domain/version/versionapi"
@@ -94,6 +95,8 @@ func setupRouter(r *gin.Engine, uc *Usecase) {
9495 auth := web .AuthMiddleware (uc .Conf .Server .HTTP .JwtSecret )
9596 r .GET ("/health" , web .WrapH (uc .getHealth ))
9697 r .GET ("/app/metrics/api" , web .WrapH (uc .getMetricsAPI ))
98+ r .GET ("/app/version/check" , web .WrapH (uc .checkVersion ))
99+ r .POST ("/app/upgrade" , auth , uc .upgradeApp )
97100
98101 versionapi .Register (r , uc .Version , auth )
99102 statapi .Register (r )
@@ -202,6 +205,109 @@ func sortExpvarMap(data *expvar.Map, top int) []KV {
202205 return kvs [:idx ]
203206}
204207
208+ const repoName = "gowvp/owl"
209+
210+ type checkVersionOutput struct {
211+ HasNewVersion bool `json:"has_new_version"`
212+ CurrentVersion string `json:"current_version"`
213+ NewVersion string `json:"new_version"`
214+ Description string `json:"description"`
215+ }
216+
217+ // checkVersion 检查是否有新版本
218+ // 通过 GitHub API 获取最新 release 信息,与当前版本比较
219+ func (uc * Usecase ) checkVersion (_ * gin.Context , _ * struct {}) (checkVersionOutput , error ) {
220+ currentVersion := uc .Conf .BuildVersion
221+ newVersion , body , err := ota .GetLastVersion (repoName )
222+ if err != nil {
223+ return checkVersionOutput {}, err
224+ }
225+
226+ hasNew := compareVersion (currentVersion , newVersion ) < 0
227+
228+ return checkVersionOutput {
229+ HasNewVersion : hasNew ,
230+ CurrentVersion : currentVersion ,
231+ NewVersion : newVersion ,
232+ Description : body ,
233+ }, nil
234+ }
235+
236+ // compareVersion 比较两个版本号
237+ // 返回值: -1 表示 v1 < v2, 0 表示相等, 1 表示 v1 > v2
238+ func compareVersion (v1 , v2 string ) int {
239+ v1 = strings .TrimPrefix (v1 , "v" )
240+ v2 = strings .TrimPrefix (v2 , "v" )
241+
242+ parts1 := strings .Split (v1 , "." )
243+ parts2 := strings .Split (v2 , "." )
244+
245+ maxLen := len (parts1 )
246+ if len (parts2 ) > maxLen {
247+ maxLen = len (parts2 )
248+ }
249+
250+ for i := 0 ; i < maxLen ; i ++ {
251+ var n1 , n2 int
252+ if i < len (parts1 ) {
253+ fmt .Sscanf (parts1 [i ], "%d" , & n1 )
254+ }
255+ if i < len (parts2 ) {
256+ fmt .Sscanf (parts2 [i ], "%d" , & n2 )
257+ }
258+ if n1 < n2 {
259+ return - 1
260+ }
261+ if n1 > n2 {
262+ return 1
263+ }
264+ }
265+ return 0
266+ }
267+
268+ // upgradeApp 执行应用升级
269+ // 通过 SSE 返回下载进度,下载完成后由回调决定如何升级
270+ func (uc * Usecase ) upgradeApp (c * gin.Context ) {
271+ c .Header ("Content-Type" , "text/event-stream" )
272+ c .Header ("Cache-Control" , "no-cache" )
273+ c .Header ("Connection" , "keep-alive" )
274+ c .Header ("Access-Control-Allow-Origin" , "*" )
275+
276+ flusher , ok := c .Writer .(http.Flusher )
277+ if ! ok {
278+ c .JSON (http .StatusInternalServerError , gin.H {"msg" : "不支持 SSE" })
279+ return
280+ }
281+
282+ sendEvent := func (event , data string ) {
283+ fmt .Fprintf (c .Writer , "event: %s\n data: %s\n \n " , event , data )
284+ flusher .Flush ()
285+ }
286+
287+ sendEvent ("start" , `{"msg":"开始下载升级包"}` )
288+
289+ filename := "linux_amd64"
290+ if runtime .GOARCH == "arm64" {
291+ filename = "linux_arm64"
292+ }
293+
294+ o := ota .NewOTA (repoName , filename )
295+ o .SetProgressCallback (func (current , total int64 ) {
296+ percent := 0
297+ if total > 0 {
298+ percent = int (current * 100 / total )
299+ }
300+ sendEvent ("progress" , fmt .Sprintf (`{"current":%d,"total":%d,"percent":%d}` , current , total , percent ))
301+ })
302+
303+ if err := o .Download ().Error (); err != nil {
304+ sendEvent ("error" , fmt .Sprintf (`{"msg":"%s"}` , err .Error ()))
305+ return
306+ }
307+
308+ sendEvent ("complete" , `{"msg":"下载完成,请手动重启服务"}` )
309+ }
310+
205311func (uc * Usecase ) proxySMS (c * gin.Context ) {
206312 defer func () {
207313 _ = recover ()
0 commit comments