Skip to content

Commit 674e7df

Browse files
committed
Implement OTA functionality for version checking
1 parent 7153059 commit 674e7df

9 files changed

Lines changed: 619 additions & 8 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ cover/
4242
*.jpg
4343
__pycache__/
4444
*.pt
45-
*.onnx
45+
*.onnx
46+
*.remember/

analysis/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def setup_logging(level_str: str = "INFO", retention_days: int = 3):
5959
)
6060

6161
# 设置后缀格式,例如 app.log.2023-12-31
62-
file_handler.suffix = "%Y-%m-%d"
62+
file_handler.suffix = "%Y-%m-%d.log"
6363
file_handler.setFormatter(formatter)
6464
root_logger.addHandler(file_handler)
6565

configs/config.toml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,26 @@
22
Debug = false
33
# rtmp 推流秘钥
44
RTMPSecret = '123'
5-
5+
# 登录用户名
66
Username = 'admin'
7+
# 登录密码
78
Password = 'admin'
89

10+
# ai 分析服务
11+
[Server.AI]
12+
# 是否禁用 ai 分析服务
13+
Disabled = false
14+
# 保留天数
15+
RetainDays = 0
16+
917
# 对外提供的服务,建议由 nginx 代理
1018
[Server.HTTP]
1119
# http 端口
1220
Port = 15123
1321
# 请求超时时间
1422
Timeout = '1m0s'
1523
# jwt 秘钥,空串时,每次启动程序将随机赋值
16-
JwtSecret = ''
24+
JwtSecret = '6caOiETMs8SPWNHgEKA1Jhmn9wxpjAj9'
1725

1826
[Server.HTTP.PProf]
1927
# 是否启用 pprof, 建议设置为 true
@@ -22,7 +30,7 @@
2230
AccessIps = ['::1', '127.0.0.1']
2331

2432
[Data]
25-
# 数据库支持 sqlite/postgres/mysql使用 sqlite 时 dsn 应当填写文件存储路径
33+
# 数据库支持 sqlite/postgres/mysql, 使用 sqlite 时 dsn 应当填写文件存储路径
2634
# postgres://postgres:123456@127.0.0.1:5432/gb28181?sslmode=disable
2735
# mysql://root:123456@127.0.0.1:5432/gb28181?sslmode=disable
2836
[Data.Database]
@@ -61,9 +69,11 @@
6169
HTTPPort = 8080
6270
# 媒体服务器密钥
6371
Secret = 'jvRqCAzEg7AszBi4gm1cfhwXpmnVmJMG'
72+
# 媒体服务器类型 zlm/lalmax
73+
Type = 'zlm'
6474
# 用于流媒体 webhook 回调
65-
WebHookIP = '192.168.10.10'
75+
WebHookIP = '192.168.1.3'
6676
# 媒体服务器 RTP 端口范围
6777
RTPPortRange = '20000-20100'
6878
# 媒体服务器 SDP IP
69-
SDPIP = '192.168.10.10'
79+
SDPIP = '192.168.1.3'

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ services:
33
# 如果拉不到 docker hub 镜像,也可以尝试
44
# registry.cn-shanghai.aliyuncs.com/ixugo/homenvr:latest
55
image: gospace/gowvp:latest
6-
restart: unless-stopped
6+
restart: always
77
# linux 解开下行注释,并将 ports 全部注释
88
# network_mode: host
99
ports:

internal/web/api/api.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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\ndata: %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+
205311
func (uc *Usecase) proxySMS(c *gin.Context) {
206312
defer func() {
207313
_ = recover()

0 commit comments

Comments
 (0)