package service
|
|
import (
|
"bufio"
|
"encoding/json"
|
"errors"
|
"fmt"
|
"io/ioutil"
|
"os"
|
"os/exec"
|
"path"
|
"strconv"
|
"strings"
|
"syscall"
|
"time"
|
"vamicro/config"
|
"vamicro/extend/util"
|
"vamicro/system-service/models"
|
versionControlS "vamicro/version-control/service"
|
|
"basic.com/fileServer/WeedFSClient.git"
|
"basic.com/valib/bhomeclient.git"
|
"basic.com/valib/bhomedbapi.git"
|
"basic.com/valib/logger.git"
|
uuid "github.com/satori/go.uuid"
|
)
|
|
var (
|
AuthInfo util.AuthorizationInfo
|
)
|
|
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 *bhomeclient.FileArg //当前分片的文件内容
|
}
|
|
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
|
}
|
//判断分块文件是否存在
|
chunkAlignNum := util.FormatNum(arg.TotalChunks, arg.ChunkNumber)
|
chunkFilePath := fileTmpPath + "/" + arg.Identifier + "_" + chunkAlignNum
|
if !util.Exists(chunkFilePath) {
|
return false
|
}
|
if arg.ChunkNumber == arg.TotalChunks {
|
dirFiles, _ := ioutil.ReadDir(fileTmpPath)
|
if dirFiles != nil && len(dirFiles) == arg.TotalChunks {
|
//表示所有分块都上传了,需要merge
|
if b, _ := MergeChunks(fileTmpPath, mergedFilePath); !b {
|
return false
|
}
|
}
|
}
|
return true
|
}
|
|
func (sv SysService) PatchUpload(arg *FileUploadVo) (bool, bool, string) {
|
configPatchPath := ""
|
if config.Server.PatchPath != "" {
|
configPatchPath = config.Server.PatchPath
|
} else {
|
configPatchPath = "/opt/vasystem/patch"
|
}
|
|
if !util.CreateDirectory(configPatchPath) {
|
return false, false, "创建文件夹失败"
|
}
|
|
filenameWithSuffix := path.Base(arg.FileName)
|
subfix := path.Ext(filenameWithSuffix)
|
MD5Str := arg.Identifier
|
logger.Debug("Identifier:", MD5Str)
|
fileTmpPath := configPatchPath + "/" + MD5Str
|
if !util.Exists(fileTmpPath) {
|
if !util.CreateDirectory(fileTmpPath) {
|
return false, false, "创建补丁文件夹失败"
|
}
|
}
|
chunkAlignNum := util.FormatNum(arg.TotalChunks, arg.ChunkNumber)
|
fileSavePath := fileTmpPath + "/" + MD5Str + "_" + chunkAlignNum
|
if util.Exists(fileSavePath) {
|
rmErr := os.Remove(fileSavePath)
|
if rmErr != nil {
|
logger.Debug("rmErr:", rmErr)
|
return false, false, rmErr.Error()
|
}
|
}
|
file, e := os.Create(fileSavePath)
|
if e != nil {
|
logger.Debug("os.Create err:", e, "fileSavePath:", fileSavePath)
|
return false, false, e.Error()
|
}
|
defer file.Close()
|
writer := bufio.NewWriter(file)
|
|
nn, err2 := writer.Write(arg.File.Bytes)
|
if nn == 0 || err2 != nil {
|
logger.Debug("write chunkData err:", err2, "nn:", nn)
|
return false, false, "写入补丁包失败"
|
}
|
if err2 = writer.Flush(); err2 != nil {
|
logger.Debug("write flush err:", err2)
|
return false, false, err2.Error()
|
}
|
isComplete := false
|
dirFiles, _ := ioutil.ReadDir(fileTmpPath)
|
if dirFiles != nil && len(dirFiles) == arg.TotalChunks {
|
isComplete = true
|
}
|
if isComplete {
|
if mergeB, mergeE := MergeChunks(fileTmpPath, fileTmpPath+subfix); mergeB {
|
logger.Debug("merge all chunks success,identifier:", MD5Str, "fileName:", arg.FileName)
|
unPackB, unPackErr := unPackPatchPackage(MD5Str, subfix)
|
logger.Debug("unPackB:", unPackB, "unPackErr:", unPackErr)
|
if unPackB {
|
return true, isComplete, "解压补丁包失败,错误的补丁包格式"
|
}
|
} else {
|
return false, isComplete, mergeE.Error()
|
}
|
}
|
return true, isComplete, ""
|
}
|
|
// upgrade
|
func (sv SysService) Upgrade(identifier string, filename string) (bool, error) {
|
if !bakBeforeUpgrade() {
|
return false, errors.New("更新前备份失败")
|
}
|
configPatchPath := ""
|
if config.Server.PatchPath != "" {
|
configPatchPath = config.Server.PatchPath
|
} else {
|
configPatchPath = "/opt/vasystem/patch"
|
}
|
|
filenameWithSuffix := path.Base(filename)
|
ext := path.Ext(filenameWithSuffix)
|
|
zipFilePath := configPatchPath + "/" + identifier + ext
|
if util.Exists(zipFilePath) {
|
//校验md5
|
strMd5, e := util.FileMd5(zipFilePath)
|
if e != nil || strMd5 == "" {
|
return false, errors.New("获取升级压缩包md5失败")
|
}
|
if strMd5 == identifier {
|
if !updatePatch(identifier, ext) {
|
return false, errors.New("执行升级过程异常,请确定上传的补丁是tar.gz格式")
|
}
|
return true, nil
|
|
} else {
|
logger.Debug("strMd5 is", strMd5, "identifier is", identifier, "not equal")
|
return false, errors.New("校验升级文件失败")
|
}
|
} else {
|
return false, errors.New("升级文件已丢失,请重新上传")
|
}
|
}
|
|
func bakBeforeUpgrade() bool {
|
configBakPath := ""
|
if config.Server.BakPath != "" {
|
configBakPath = config.Server.BakPath
|
} else {
|
configBakPath = "/opt/vasystem/bak"
|
}
|
if util.Exists(configBakPath) {
|
//只保留最新的版本
|
if err := os.RemoveAll(configBakPath); err != nil {
|
return false
|
}
|
}
|
if !util.CreateDirectory(configBakPath) {
|
return false
|
}
|
b, err := ExecCmd("cp -r /opt/vasystem/bin /opt/vasystem/bak")
|
if err != nil {
|
logger.Debug("bakBeforeUpgrade result:", string(b), "err:", err)
|
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.解压缩更新包
|
unPackPath := configPatchPath + "/" + identifier + "_basic/"
|
if util.Exists(unPackPath) {
|
//此版本已经更新过
|
rmErr := os.RemoveAll(unPackPath)
|
if rmErr != nil {
|
return false
|
}
|
}
|
if !util.CreateDirectory(unPackPath) {
|
return false
|
}
|
|
unPackFilePath := configPatchPath + "/" + identifier + ext
|
utzOut, err := util.UnTarGzByCmd(unPackFilePath, unPackPath)
|
if err != nil {
|
logger.Debug("UnPack err:", err, "unPackFile:", unPackFilePath, " out:", utzOut)
|
return false
|
}
|
|
//如果通用脚本有更新,则更新通用脚本
|
if util.Exists(unPackPath + "updatePatch.sh") {
|
cpStr := fmt.Sprintf("cp %s /opt/vasystem/bin", unPackPath+"updatePatch.sh")
|
b, err := ExecCmd(cpStr)
|
if err != nil {
|
logger.Debug("cp updatePatch.sh to bin err:", err, "result:", string(b))
|
return false
|
}
|
}
|
|
//判断更新包里是否有补丁脚本,如果有则执行,否则执行updatePatch.sh
|
updateCmd := fmt.Sprintf("./updatePatch.sh %s %s %s &", unPackPath, unPackFilePath, configPatchPath+"/"+identifier)
|
if util.Exists(unPackPath + "upgrade.sh") {
|
updateCmd = fmt.Sprintf("%supgrade.sh %s %s %s &", unPackPath, unPackPath, unPackFilePath, configPatchPath+"/"+identifier)
|
}
|
//2.更新系统
|
b, err := ExecCmd(updateCmd)
|
if err != nil {
|
logger.Debug("upgrade err:", err, "result:", string(b), "cmd:", updateCmd)
|
return false
|
} else {
|
logger.Debug("upgrade result:", string(b), "cmd:", updateCmd)
|
}
|
return true
|
}
|
|
func unPackPatchPackage(identifier string, ext string) (bool, error) {
|
if ext != ".gz" {
|
return false, errors.New("非法的安装包文件,安装包为.tar.gz文件")
|
}
|
configPatchPath := ""
|
if config.Server.PatchPath != "" {
|
configPatchPath = config.Server.PatchPath
|
} else {
|
configPatchPath = "../patch"
|
}
|
//1.解压缩更新包
|
unPackTargetPath := configPatchPath + "/" + identifier + "_basic/"
|
unPackFilePath := configPatchPath + "/" + identifier + ext
|
|
if util.Exists(unPackTargetPath) {
|
//此版本已经更新过
|
rmErr := os.RemoveAll(unPackTargetPath)
|
if rmErr != nil {
|
return false, rmErr
|
}
|
}
|
if !util.CreateDirectory(unPackTargetPath) {
|
return false, errors.New("创建压缩文件夹失败")
|
}
|
logger.Debug("unPackFilePath:", unPackFilePath, "unPackPath:", unPackTargetPath)
|
_, err := util.UnTarGzByCmd(unPackFilePath, unPackTargetPath)
|
if err != nil {
|
logger.Debug("UnPack err:", err, "unPackFile:", unPackFilePath)
|
return false, err
|
}
|
return true, nil
|
}
|
|
func MergeChunks(chunkPath string, storePath string) (bool, error) {
|
var cmd *exec.Cmd
|
cmd = exec.Command("/bin/sh", "-c", fmt.Sprintf(`
|
filepath=%s
|
filestore=%s
|
echo "filepath: " $filepath
|
echo "filestorepath: " $filestore
|
if [ ! -f $filestore ]; then
|
echo "$filestore not exist"
|
else
|
rm -f $filestore
|
fi
|
|
for item in $(ls $filepath | sort -n)
|
do
|
$(cat ${filepath}/${item} >> ${filestore})
|
echo "merge ${filepath}/${item} to $filestore ok"
|
done
|
|
echo "file store ok"`, chunkPath, storePath))
|
|
if b, err := cmd.Output(); err != nil {
|
logger.Debug("mergeChunks err:", err, "result:", string(b))
|
return false, errors.New("合成压缩包失败")
|
} else {
|
logger.Debug("mergeChunks result:", string(b))
|
return true, nil
|
}
|
}
|
|
func ExecCmd(cmdStr string) ([]byte, error) {
|
var cmd *exec.Cmd
|
cmd = exec.Command("/bin/sh", "-c", cmdStr)
|
return cmd.Output()
|
}
|
|
// 上传声音文件
|
func (sv SysService) UploadVoice(fileBytes []byte, filename string) (string, error) {
|
fileExt := path.Ext(filename)
|
fileExt = strings.ToLower(fileExt)
|
if fileExt != ".mp3" && fileExt != ".wma" && fileExt != ".wav" {
|
return "", errors.New("audio format error")
|
}
|
var sApi bhomedbapi.SysSetApi
|
flag, localConf := sApi.GetServerInfo()
|
if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 {
|
logger.Debug("localConf err")
|
return "", errors.New("localConf err")
|
}
|
|
var weedfsUri = "http://" + localConf.WebPicIp + ":" + strconv.Itoa(int(localConf.WebPicPort)) + "/submit?collection=voice"
|
logger.Debug("weedfsUri:", weedfsUri)
|
weedFilePath, err := WeedFSClient.UploadFile(weedfsUri, filename, fileBytes, 30*time.Second)
|
if err != nil {
|
logger.Debug("WeedFSClient.UploadFile err:", err, filename)
|
return "", errors.New("WeedFSClient.UploadFile err")
|
}
|
v := models.Voice{
|
Name: filename,
|
Path: weedFilePath,
|
}
|
v.Id = uuid.NewV4().String()
|
b, err := v.Insert()
|
if nil == err && b {
|
return weedFilePath, nil
|
}
|
return weedFilePath, err
|
}
|
|
// 获取所有配置
|
func (sv SysService) GetAllSetting() (settings []models.SysSetting, err error) {
|
settingModel := models.SysSetting{}
|
return settingModel.GetAllSetting()
|
}
|
|
// 更新配置
|
func (sv SysService) SaveSetting(setting models.SysSetting) (err error) {
|
return setting.SaveSetting(true)
|
}
|
|
// 获取硬盘剩余空间
|
func (sv SysService) DiskInfo(dev string) (uint64, uint64) {
|
var stat syscall.Statfs_t
|
err := syscall.Statfs(dev, &stat)
|
if err != nil {
|
return 0, 0
|
}
|
All := stat.Blocks * uint64(stat.Bsize)
|
Free := stat.Bfree * uint64(stat.Bsize)
|
|
return All, Free
|
}
|
|
// 同步更新设置
|
func PersistentWrapper(topic string, payloads []byte) {
|
if versionControlS.AuthorizationUpdateTopic == topic {
|
if err := json.Unmarshal(payloads, &AuthInfo); nil != err {
|
logger.Error("handleSubMsg failed to persistent:", topic, string(payloads))
|
}
|
}
|
|
if "sync-proc-message-to-serf" == topic {
|
logger.Debug("handleSubMsg sync-proc-message-to-serf")
|
ClusterSyncProcMessage(payloads)
|
}
|
}
|
|
// 获取授权方式和授权密码
|
func (sv SysService) ShowAuthInfo() (interface{}, error) {
|
local_configs := models.LocalConfig{}
|
// 获取表中的第一条记录(只有一条)
|
err := local_configs.Select()
|
if err == nil && local_configs.Id != "" {
|
ret := map[string]interface{}{
|
"id": local_configs.Id,
|
"need_auth_pwd": local_configs.NeedAuthPwd,
|
"auth_pwd": local_configs.AuthPwd,
|
}
|
return ret, nil
|
} else {
|
return nil, err
|
}
|
}
|
|
// 设置授权方式和授权密码
|
func (sv SysService) SetAuthInfo(req *models.LocalConfig) error {
|
err := req.UpdateAuth()
|
if err == nil {
|
return nil
|
} else {
|
return err
|
}
|
}
|