| | |
| | | channelCount: 0 |
| | | #硬盘个数 |
| | | diskCount: 2 |
| | | #Exec root command |
| | | sudoPassword: 123 |
| | | sysThresholds: |
| | | - value: 60 |
| | | color: '#13ce66' |
| | | - value: 80 |
| | | color: '#FF9C4A' |
| | | - value: 95 |
| | | color: '#f53d3d' |
| | | - value: 100 |
| | | color: '#5d0000' |
| | | ptzSpeed: 50 |
| | | patchPath: /opt/vasystem/patch |
| | | database: |
| | | driver: sqlite |
| | | name: sqlite3 |
| | |
| | | channelCount: 0 |
| | | #硬盘个数 |
| | | diskCount: 2 |
| | | #Exec root command |
| | | sudoPassword: 123 |
| | | sysThresholds: |
| | | - value: 60 |
| | | color: '#13ce66' |
| | | - value: 80 |
| | | color: '#FF9C4A' |
| | | - value: 95 |
| | | color: '#f53d3d' |
| | | - value: 100 |
| | | color: '#5d0000' |
| | | ptzSpeed: 50 |
| | | patchPath: /opt/vasystem/patch |
| | | database: |
| | | driver: sqlite |
| | | name: sqlite3 |
| | |
| | | value: 100 |
| | | color: '#5d0000' |
| | | ptzSpeed: 50 |
| | | patchPath: /opt/vasystem/patch |
| | | database: |
| | | driver: sqlite |
| | | name: sqlite3 |
| | |
| | | package controllers |
| | | |
| | | import ( |
| | | "net/http" |
| | | "regexp" |
| | | "strconv" |
| | | "webserver/extend/code" |
| | | "webserver/extend/config" |
| | | "webserver/extend/sys" |
| | | "webserver/extend/util" |
| | | "webserver/middlewares/auth" |
| | | |
| | | "basic.com/dbapi.git" |
| | | "github.com/gin-gonic/gin" |
| | |
| | | |
| | | util.ResponseFormat(c, code.Success, "配置成功") |
| | | } |
| | | |
| | | // @Security ApiKeyAuth |
| | | // @Summary 系统更新包上传(分块检查) |
| | | // @Description 系统更新包上传(分块检查) |
| | | // @Accept multipart/form-data |
| | | // @Produce json |
| | | // @Tags sysset |
| | | // @Param chunkNumber formData int true "当前是第几个分块" |
| | | // @Param chunkSize formData int true "每一块的大小" |
| | | // @Param currentChunkSize formData int true "当前块的大小" |
| | | // @Param identifier formData string true "整个文件的唯一标识,目前是md5" |
| | | // @Param filename formData string true "文件名称" |
| | | // @Param relativePath formData string true "文件在客户端电脑的路径" |
| | | // @Param totalChunks formData int true "文件总块数" |
| | | // @Success 200 {string} json "{"code":200, msg:"", success:true}" |
| | | // @Failure 500 {string} json "{"code":500, msg:"", success:false}" |
| | | // @Router /data/api-v/sysset/patchUpdate [get] |
| | | func (sset SysSetController) PatchUpdateCheck(c *gin.Context) { |
| | | authDriver := auth.GenerateAuthDriver() |
| | | userM := (*authDriver).User(c) |
| | | if userM ==nil { |
| | | util.ResponseFormat(c,code.TokenNotFound,"登录过期,请登录") |
| | | return |
| | | } |
| | | chunkNumber, e1 := strconv.Atoi(c.Request.FormValue("chunkNumber")) |
| | | chunkSize, e2 := strconv.Atoi(c.Request.FormValue("chunkSize")) |
| | | currentChunkSize, e3 := strconv.Atoi(c.Request.FormValue("currentChunkSize")) |
| | | identifier := c.Request.FormValue("identifier") |
| | | filename := c.Request.FormValue("filename") |
| | | relativePath := c.Request.FormValue("relativePath") |
| | | totalChunks, e5 := strconv.Atoi(c.Request.FormValue("totalChunks")) |
| | | if identifier == "" || e1 != nil || e2 != nil || e3 != nil || e5 !=nil { |
| | | util.ResponseFormat(c,code.RequestParamError,"参数有误") |
| | | return |
| | | } |
| | | var arg = service.FileChunkCheckVo { |
| | | UserId: userM["id"].(string), |
| | | FileName: filename, |
| | | Identifier: identifier, |
| | | RelativePath: relativePath, |
| | | TotalChunks: totalChunks, |
| | | ChunkNumber: chunkNumber, |
| | | ChunkSize: chunkSize, |
| | | CurrentChunkSize:currentChunkSize, |
| | | } |
| | | var sv service.SysService |
| | | if b := sv.CheckUpdateFile(&arg);b { |
| | | c.String(http.StatusOK,"found") |
| | | } else { |
| | | c.String(http.StatusNoContent,"") |
| | | } |
| | | } |
| | | |
| | | // @Security ApiKeyAuth |
| | | // @Summary 系统更新包上传 |
| | | // @Description 系统更新包上传 |
| | | // @Accept multipart/form-data |
| | | // @Produce json |
| | | // @Tags sysset |
| | | // @Param chunkNumber formData int true "当前是第几个分块" |
| | | // @Param chunkSize formData int true "每一块的大小" |
| | | // @Param currentChunkSize formData int true "当前块的大小" |
| | | // @Param totalSize formData string true "文件总大小" |
| | | // @Param identifier formData string true "整个文件的唯一标识,目前是md5" |
| | | // @Param filename formData string true "文件名称" |
| | | // @Param relativePath formData string true "文件在客户端电脑的路径" |
| | | // @Param totalChunks formData int true "文件总块数" |
| | | // @Param file formData file true "文件分片内容" |
| | | // @Success 200 {string} json "{"code":200, msg:"", success:true}" |
| | | // @Failure 500 {string} json "{"code":500, msg:"", success:false}" |
| | | // @Router /data/api-v/sysset/patchUpdate [post] |
| | | func (sset SysSetController) PatchUpdate(c *gin.Context) { |
| | | authDriver := auth.GenerateAuthDriver() |
| | | userM := (*authDriver).User(c) |
| | | if userM == nil { |
| | | util.ResponseFormat(c, code.TokenNotFound, "登录过期,请登录") |
| | | return |
| | | } |
| | | chunkNumber, e1 := strconv.Atoi(c.Request.FormValue("chunkNumber")) |
| | | chunkSize, e2 := strconv.Atoi(c.Request.FormValue("chunkSize")) |
| | | currentChunkSize, e3 := strconv.Atoi(c.Request.FormValue("currentChunkSize")) |
| | | totalSize, e4 := strconv.ParseInt(c.Request.FormValue("totalSize"), 10, 64) |
| | | identifier := c.Request.FormValue("identifier") |
| | | filename := c.Request.FormValue("filename") |
| | | relativePath := c.Request.FormValue("relativePath") |
| | | totalChunks, e5 := strconv.Atoi(c.Request.FormValue("totalChunks")) |
| | | file, header, e6 := c.Request.FormFile("file") |
| | | if identifier == "" || e1 != nil || e2 != nil || e3 != nil || e4 != nil || e5 != nil || e6 != nil { |
| | | util.ResponseFormat(c, code.RequestParamError, "参数有误") |
| | | return |
| | | } |
| | | var arg = service.FileUploadVo{ |
| | | UserId: userM["id"].(string), |
| | | FileName: filename, |
| | | Identifier: identifier, |
| | | RelativePath: relativePath, |
| | | TotalSize: totalSize, |
| | | TotalChunks: totalChunks, |
| | | ChunkNumber: chunkNumber, |
| | | ChunkSize: chunkSize, |
| | | CurrentChunkSize: currentChunkSize, |
| | | File: &file, |
| | | Header: header, |
| | | } |
| | | var sv service.SysService |
| | | if b := sv.PatchUpload(&arg); b { |
| | | util.ResponseFormat(c, code.Success, "") |
| | | } else { |
| | | util.ResponseFormat(c, code.ComError, "") |
| | | } |
| | | } |
| | |
| | | SudoPassword string `mapstructure: "sudoPassword"` //系统密码 |
| | | SysThresholds []threshold `mapstructure: "sysThresholds"` |
| | | PTZSpeed int `mapstructure: "ptzSpeed"` // 云台移动速度 |
| | | PatchPath string `mapstructure:"patchPath"`//系统更新包路径 |
| | | } |
| | | |
| | | var Server = &server{} |
| | |
| | | package util |
| | | |
| | | import ( |
| | | "archive/zip" |
| | | "bytes" |
| | | "encoding/json" |
| | | "image" |
| | | "io" |
| | | "io/ioutil" |
| | | "net/http" |
| | | "os" |
| | | "path/filepath" |
| | | "strconv" |
| | | "time" |
| | | "webserver/extend/code" |
| | | |
| | | "basic.com/pubsub/protomsg.git" |
| | |
| | | out = out[:n] // compressed data |
| | | return out, nil |
| | | } |
| | | |
| | | // 判断所给路径文件/文件夹是否存在 |
| | | func Exists(path string) bool { |
| | | if path == "" { |
| | | return false |
| | | } |
| | | _, err := os.Stat(path) //os.Stat获取文件信息 |
| | | if err != nil { |
| | | if os.IsExist(err) { |
| | | return true |
| | | } |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | func CreateDirectory(path string) bool { |
| | | if path == "" { |
| | | return false |
| | | } |
| | | if Exists(path) { |
| | | return true |
| | | } |
| | | err := os.MkdirAll(path,os.ModePerm) |
| | | if err !=nil { |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | |
| | | //日期转字符串 |
| | | func FormatDate(date time.Time, layout string) string { |
| | | layout = strings.Replace(layout, "yyyy", "2006", 1) |
| | | layout = strings.Replace(layout, "yy", "06", 1) |
| | | layout = strings.Replace(layout, "MM", "01", 1) |
| | | layout = strings.Replace(layout, "dd", "02", 1) |
| | | layout = strings.Replace(layout, "HH", "15", 1) |
| | | layout = strings.Replace(layout, "mm", "04", 1) |
| | | layout = strings.Replace(layout, "ss", "05", 1) |
| | | layout = strings.Replace(layout, "SSS", "000", -1) |
| | | return date.Format(layout) |
| | | } |
| | | func ZipCheck(zipPath string) bool { |
| | | f, err := os.Open(zipPath) |
| | | if err != nil { |
| | | return false |
| | | } |
| | | defer f.Close() |
| | | buf := make([]byte, 4) |
| | | if n, err := f.Read(buf); err != nil || n < 4 { |
| | | return false |
| | | } |
| | | return bytes.Equal(buf, []byte("PK\x03\x04")) |
| | | } |
| | | func UnZip(archive, target string) error { |
| | | reader, err := zip.OpenReader(archive) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | if err := os.MkdirAll(target, 0755); err != nil { |
| | | return err |
| | | } |
| | | for _, file := range reader.File { |
| | | path := filepath.Join(target, file.Name) |
| | | if file.FileInfo().IsDir() { |
| | | os.MkdirAll(path, file.Mode()) |
| | | continue |
| | | } |
| | | fileReader, err := file.Open() |
| | | if err != nil { |
| | | return err |
| | | } |
| | | defer fileReader.Close() |
| | | targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | defer targetFile.Close() |
| | | if _, err := io.Copy(targetFile, fileReader); err != nil { |
| | | return err |
| | | } |
| | | } |
| | | |
| | | return nil |
| | | } |
| | |
| | | vsset.GET("/reboot", ssController.RebootOS) |
| | | vsset.GET("/rebootTask", ssController.GetRebootTask) |
| | | vsset.POST("/rebootTask", ssController.SetRebootTask) |
| | | |
| | | vsset.GET("/patchUpdate", ssController.PatchUpdateCheck) |
| | | vsset.POST("/patchUpdate", ssController.PatchUpdate) |
| | | } |
| | | |
| | | //算法库操作 |
New file |
| | |
| | | package service |
| | | import ( |
| | | "basic.com/valib/logger.git" |
| | | "bufio" |
| | | "fmt" |
| | | "io/ioutil" |
| | | "mime/multipart" |
| | | "os" |
| | | "os/exec" |
| | | "strconv" |
| | | "strings" |
| | | "webserver/extend/config" |
| | | "webserver/extend/util" |
| | | ) |
| | | |
| | | type SysService struct { |
| | | |
| | | } |
| | | |
| | | type FileChunkCheckVo struct { |
| | | UserId string |
| | | ChunkNumber int //当前分片下标,从1开始 |
| | | ChunkSize int //每一块的大小 |
| | | CurrentChunkSize int //当前分块的大小 |
| | | FileName string //文件名称 |
| | | Identifier string //整个文件唯一标识,md5 |
| | | RelativePath string //文件客户端路径 |
| | | TotalSize int64 //文件总大小 |
| | | TotalChunks int //总分片数量 |
| | | } |
| | | |
| | | type FileUploadVo struct { |
| | | Id string |
| | | UserId string |
| | | ChunkNumber int //当前分片下标,从1开始 |
| | | ChunkSize int //每一块的大小 |
| | | CurrentChunkSize int //当前分块的大小 |
| | | FileName string //文件名称 |
| | | Identifier string //整个文件唯一标识,md5 |
| | | RelativePath string //文件客户端路径 |
| | | TotalSize int64 //文件总大小 |
| | | TotalChunks int //总分片数量 |
| | | File *multipart.File //当前分片的文件内容 |
| | | Header *multipart.FileHeader |
| | | } |
| | | |
| | | func (sv SysService) CheckUpdateFile(arg *FileChunkCheckVo) bool { |
| | | configPatchPath := "" |
| | | if config.Server.PatchPath != "" { |
| | | configPatchPath = config.Server.PatchPath |
| | | } else { |
| | | configPatchPath = "/opt/vasystem/patch" |
| | | } |
| | | fileTmpPath := configPatchPath + "/"+arg.Identifier |
| | | if !util.Exists(fileTmpPath) { |
| | | return false |
| | | } |
| | | //判断合成的文件是否存在 |
| | | index := strings.LastIndex(arg.FileName, ".") |
| | | subfix := "" |
| | | if index >-1 {//有后缀 |
| | | subfix = arg.FileName[index:] |
| | | } |
| | | mergedFilePath := fileTmpPath+subfix |
| | | if util.Exists(mergedFilePath) { |
| | | return true |
| | | } |
| | | //判断分块文件是否存在 |
| | | chunkFilePath := fileTmpPath+"/"+arg.Identifier+"_"+strconv.Itoa(arg.ChunkNumber) |
| | | if !util.Exists(chunkFilePath) { |
| | | return false |
| | | } |
| | | if arg.ChunkNumber == arg.TotalChunks { |
| | | dirFiles, _ := ioutil.ReadDir(fileTmpPath) |
| | | if dirFiles != nil && len(dirFiles) == arg.TotalChunks { |
| | | //表示所有分块都上传了,需要merge |
| | | if sv.MergeChunks(fileTmpPath, mergedFilePath) { |
| | | if util.ZipCheck(mergedFilePath) { |
| | | if !updatePatch(arg.Identifier, subfix) { |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return true |
| | | } |
| | | |
| | | func (sv SysService) PatchUpload(arg *FileUploadVo) bool { |
| | | configPatchPath := "" |
| | | if config.Server.PatchPath != "" { |
| | | configPatchPath = config.Server.PatchPath |
| | | } else { |
| | | configPatchPath = "/opt/vasystem/patch" |
| | | } |
| | | defer (*arg.File).Close() |
| | | if !util.CreateDirectory(configPatchPath) { |
| | | return false |
| | | } |
| | | index := strings.LastIndex(arg.Header.Filename, ".") |
| | | if index < 0 { |
| | | return false |
| | | } |
| | | subfix := arg.Header.Filename[index:] |
| | | MD5Str := arg.Identifier |
| | | logger.Debug("Identifier:",MD5Str) |
| | | fileTmpPath := configPatchPath + "/"+MD5Str |
| | | if !util.Exists(fileTmpPath) { |
| | | if !util.CreateDirectory(fileTmpPath) { |
| | | return false |
| | | } |
| | | } |
| | | fileSavePath := fileTmpPath+"/"+MD5Str+"_"+strconv.Itoa(arg.ChunkNumber) |
| | | if util.Exists(fileSavePath) { |
| | | rmErr := os.Remove(fileSavePath) |
| | | if rmErr != nil { |
| | | logger.Debug("rmErr:",rmErr) |
| | | return false |
| | | } |
| | | } |
| | | file, e := os.Create(fileSavePath) |
| | | if e !=nil { |
| | | logger.Debug("os.Create err:",e,"fileSavePath:",fileSavePath) |
| | | return false |
| | | } |
| | | defer file.Close() |
| | | writer := bufio.NewWriter(file) |
| | | chunkData := make([]byte, arg.Header.Size) |
| | | n, err := (*arg.File).ReadAt(chunkData, 0) |
| | | if n ==0 || err !=nil { |
| | | logger.Debug("read chunkData err:",err,"n:",n) |
| | | return false |
| | | } |
| | | nn, err2 := writer.Write(chunkData) |
| | | if nn ==0 || err2 !=nil { |
| | | logger.Debug("write chunkData err:",err2,"nn:",nn) |
| | | return false |
| | | } |
| | | isComplete := false |
| | | dirFiles, _ := ioutil.ReadDir(fileTmpPath) |
| | | if dirFiles != nil && len(dirFiles) == arg.TotalChunks { |
| | | isComplete = true |
| | | } |
| | | if isComplete { |
| | | if sv.MergeChunks(fileTmpPath,fileTmpPath + subfix) { |
| | | logger.Debug("merge all chunks success,identifier:",MD5Str,"fileName:",arg.FileName) |
| | | if util.ZipCheck(fileTmpPath + subfix) { |
| | | if !updatePatch(arg.Identifier, subfix) { |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return true |
| | | } |
| | | |
| | | //更新系统程序 |
| | | func updatePatch(identifier string, ext string) bool { |
| | | configPatchPath := "" |
| | | if config.Server.PatchPath != "" { |
| | | configPatchPath = config.Server.PatchPath |
| | | } else { |
| | | configPatchPath = "/opt/vasystem/patch" |
| | | } |
| | | //1.解压缩更新包 |
| | | unZipPath := configPatchPath+"/"+identifier+"_basic/" |
| | | if util.Exists(unZipPath) { |
| | | //此版本已经更新过 |
| | | return true |
| | | } else { |
| | | if !util.CreateDirectory(unZipPath) { |
| | | return false |
| | | } |
| | | } |
| | | err := util.UnZip(configPatchPath+"/"+identifier+ext, unZipPath) |
| | | if err !=nil { |
| | | logger.Debug("UnZip err:",err,"zipFile:",configPatchPath+"/"+identifier+ext) |
| | | return false |
| | | } |
| | | //2.更新系统 |
| | | var cmd *exec.Cmd |
| | | cmd = exec.Command("/bin/sh","-c",fmt.Sprintf("./updatePatch.sh %s",unZipPath)) |
| | | logger.Debug("called sh updatePatch.sh ", unZipPath) |
| | | if b, err := cmd.Output(); err != nil { |
| | | logger.Debug("updatePatch err:",err,"result:",string(b)) |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | |
| | | func (sv SysService) MergeChunks(chunkPath string, storePath string) bool { |
| | | var cmd *exec.Cmd |
| | | cmd = exec.Command("/bin/sh", "-c", fmt.Sprintf("./mergeAll.sh %s %s", chunkPath, storePath)) |
| | | logger.Debug("called sh mergeAll.sh ", chunkPath, storePath) |
| | | if b, err := cmd.Output(); err != nil { |
| | | logger.Debug("mergeChunks err:", err, "result:", string(b)) |
| | | return false |
| | | } else { |
| | | return true |
| | | } |
| | | } |
New file |
| | |
| | | #!/bin/bash |
| | | cd /opt/vasystem/ |
| | | #stop |
| | | ./kill.sh |
| | | sleep 3 |
| | | patchPath=$1 |
| | | files=$(ls $patchPath) |
| | | echo "patchPath: " $patchPath |
| | | if [ ! -f $patchPath ];then |
| | | echo "$patchPath not exist" |
| | | else |
| | | for item in $files |
| | | do |
| | | if [ -f $item ];then |
| | | cp $patchPath/$item /opt/vasystem/bin |
| | | else |
| | | echo "directory name is ${item}" |
| | | if [[ "$item"=="dist" ]];then |
| | | echo "update web dist" |
| | | cp -r $patchPath/$item /opt/vasystem/web/dist |
| | | fi |
| | | fi |
| | | done |
| | | fi |
| | | ./daemon.sh -shm |
| | | sleep 3 |
| | | echo "patch update success" |