diff --git a/features/lm/file_export/controller.go b/features/lm/file_export/controller.go index 0fc8ee3c..54eeed7c 100644 --- a/features/lm/file_export/controller.go +++ b/features/lm/file_export/controller.go @@ -12,9 +12,6 @@ import ( "be.ems/src/framework/database/db" "be.ems/src/framework/i18n" "be.ems/src/framework/reqctx" - "be.ems/src/framework/resp" - "be.ems/src/framework/ssh" - systemService "be.ems/src/modules/system/service" "github.com/gin-gonic/gin" ) @@ -139,133 +136,3 @@ func (m *FileExport) Delete(c *gin.Context) { } c.JSON(http.StatusNoContent, nil) // 204 No Content } - -// 设置FTP配置 -// POST /table/ftp -func (m *SysJob) SetFTPConfig(c *gin.Context) { - language := reqctx.AcceptLanguage(c) - var body struct { - Password string `json:"password" ` - Username string `json:"username" binding:"required"` - ToIp string `json:"toIp" binding:"required"` - ToPort int64 `json:"toPort" binding:"required"` - Enable bool `json:"enable"` - Dir string `json:"dir" binding:"required"` - } - if err := c.ShouldBindBodyWithJSON(&body); err != nil { - c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - - // 获取配置 - cfg := systemService.NewSysConfig.FindByKey("neData.exportTableFTP") - if cfg.ConfigId > 0 { - byteData, err := json.Marshal(body) - if err != nil { - c.JSON(200, resp.ErrMsg(err.Error())) - return - } - cfg.ConfigValue = string(byteData) - cfg.UpdateBy = reqctx.LoginUserToUserName(c) - systemService.NewSysConfig.UpdateEncryptValue(cfg) - } - - c.JSON(200, resp.Ok(nil)) -} - -// 获取FTP配置 -// GET /table/ftp -func (m *SysJob) GetFTPConfig(c *gin.Context) { - // 获取配置 - cfg := systemService.NewSysConfig.FindByKeyDecryptValue("neData.exportTableFTP") - if cfg.ConfigId > 0 { - var body struct { - Password string `json:"password" ` - Username string `json:"username" binding:"required"` - ToIp string `json:"toIp" binding:"required"` - ToPort int64 `json:"toPort" binding:"required"` - Enable bool `json:"enable"` - Dir string `json:"dir" binding:"required"` - } - if err := json.Unmarshal([]byte(cfg.ConfigValue), &body); err != nil { - c.JSON(200, resp.ErrMsg(err.Error())) - return - } - c.JSON(200, resp.OkData(body)) - return - } - - c.JSON(200, resp.Ok(nil)) -} - -// FTP发送 -// PUT /table/ftp -func (m *SysJob) PutFTP(c *gin.Context) { - language := reqctx.AcceptLanguage(c) - var body struct { - FilePath string `json:"filePath" binding:"required"` - FileName string `json:"fileName" binding:"required"` - } - if err := c.ShouldBindBodyWithJSON(&body); err != nil { - c.JSON(400, resp.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) - return - } - - localFilePath := filepath.Join(body.FilePath, body.FileName) - - // 判断文件是否存在 - if _, err := os.Stat(localFilePath); os.IsNotExist(err) { - c.JSON(200, resp.ErrMsg(err.Error())) - return - } - - // 获取配置 - var cfgData struct { - Password string `json:"password" ` - Username string `json:"username" binding:"required"` - ToIp string `json:"toIp" binding:"required"` - ToPort int64 `json:"toPort" binding:"required"` - Enable bool `json:"enable"` - Dir string `json:"dir" binding:"required"` - } - cfg := systemService.NewSysConfig.FindByKeyDecryptValue("neData.exportTableFTP") - if cfg.ConfigId > 0 { - if err := json.Unmarshal([]byte(cfg.ConfigValue), &cfgData); err != nil { - c.JSON(200, resp.ErrMsg(err.Error())) - return - } - } - if !cfgData.Enable { - c.JSON(200, resp.ErrMsg("Setting Remote Backup is disabled")) - return - } - - connSSH := ssh.ConnSSH{ - User: cfgData.Username, - Password: cfgData.Password, - Addr: cfgData.ToIp, - Port: cfgData.ToPort, - AuthMode: "0", - } - sshClient, err := connSSH.NewClient() - if err != nil { - c.JSON(200, resp.ErrMsg(err.Error())) - return - } - defer sshClient.Close() - // 网元主机的SSH客户端进行文件传输 - sftpClient, err := sshClient.NewClientSFTP() - if err != nil { - c.JSON(200, resp.ErrMsg(err.Error())) - return - } - defer sftpClient.Close() - // 远程文件 - remotePath := filepath.Join(cfgData.Dir, "/backup", filepath.Base(localFilePath)) - // 复制到远程 - if err = sftpClient.CopyFileLocalToRemote(localFilePath, remotePath); err != nil { - c.JSON(200, resp.ErrMsg("error uploading file")) - return - } - c.JSON(200, resp.Ok(nil)) -} diff --git a/features/lm/file_export/route.go b/features/lm/file_export/route.go index d0f618f5..768a09b4 100644 --- a/features/lm/file_export/route.go +++ b/features/lm/file_export/route.go @@ -15,20 +15,6 @@ func Register(r *gin.RouterGroup) { middleware.PreAuthorize(nil), m.GetFileExportTable, ) - lmTable.POST("/ftp", - middleware.PreAuthorize(nil), - middleware.CryptoApi(true, false), - m.SetFTPConfig, - ) - lmTable.GET("/ftp", - middleware.PreAuthorize(nil), - middleware.CryptoApi(false, true), - m.GetFTPConfig, - ) - lmTable.PUT("/ftp", - middleware.PreAuthorize(nil), - m.PutFTP, - ) } lmFile := r.Group("/file") { diff --git a/src/modules/network_data/controller/all_backup.go b/src/modules/network_data/controller/all_backup.go new file mode 100644 index 00000000..86e08b25 --- /dev/null +++ b/src/modules/network_data/controller/all_backup.go @@ -0,0 +1,101 @@ +package controller + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "be.ems/src/framework/reqctx" + "be.ems/src/framework/resp" + "be.ems/src/modules/network_data/model" + "be.ems/src/modules/network_data/service" + + "github.com/gin-gonic/gin" +) + +// 实例化控制层 BackupController 结构体 +var NewBackup = &BackupController{ + backupService: service.NewBackup, +} + +// 备份数据 +// +// PATH /backup +type BackupController struct { + backupService *service.Backup // 备份相关服务 +} + +// 备份文件-更新FTP配置 +// +// PUT /ftp +func (s BackupController) FTPUpdate(c *gin.Context) { + var body model.BackupDataFTP + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(40422, errMsgs)) + return + } + + byteData, err := json.Marshal(body) + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + + up := s.backupService.FTPConfigUpdate(string(byteData), reqctx.LoginUserToUserName(c)) + if up <= 0 { + c.JSON(200, resp.ErrMsg("update failed")) + return + } + c.JSON(200, resp.Ok(nil)) +} + +// 备份文件-获取FTP配置 +// +// GET /ftp +func (s BackupController) FTPInfo(c *gin.Context) { + info := s.backupService.FTPConfigInfo() + c.JSON(200, resp.OkData(info)) +} + +// 备份文件-文件FTP发送 +// +// POST /ftp +func (s BackupController) FTPPush(c *gin.Context) { + var body struct { + Path string `form:"path" binding:"required"` // 路径必须是 BACKUP_DIR 开头的路径 + Filename string `form:"fileName" binding:"required"` + Tag string `form:"tag" binding:"required"` // 标签,用于区分不同的备份文件 + } + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err)) + c.JSON(422, resp.CodeMsg(40422, errMsgs)) + return + } + // 判断路径是否合法 + if !strings.HasPrefix(body.Path, s.backupService.BACKUP_DIR) { + c.JSON(200, resp.ErrMsg("operation path is not within the allowed range")) + return + } + + // 判断文件是否存在 + localFilePath := filepath.Join(body.Path, body.Filename) + if runtime.GOOS == "windows" { + localFilePath = fmt.Sprintf("C:%s", localFilePath) + } + if _, err := os.Stat(localFilePath); os.IsNotExist(err) { + c.JSON(200, resp.ErrMsg("file does not exist")) + return + } + + // 发送文件 + err := s.backupService.FTPPushFile(localFilePath, body.Tag) + if err != nil { + c.JSON(200, resp.ErrMsg(err.Error())) + return + } + c.JSON(200, resp.Ok(nil)) +} diff --git a/src/modules/network_data/model/backup.go b/src/modules/network_data/model/backup.go new file mode 100644 index 00000000..2b51e687 --- /dev/null +++ b/src/modules/network_data/model/backup.go @@ -0,0 +1,11 @@ +package model + +// BackupDataFTP 备份数据FTP服务参数结构体 +type BackupDataFTP struct { + Password string `json:"password"` // FTP密码 + Username string `json:"username" binding:"required"` // FTP用户名 + ToIp string `json:"toIp" binding:"required"` // FTP服务器IP + ToPort int64 `json:"toPort" binding:"required"` // FTP服务器端口 + Dir string `json:"dir" binding:"required"` // FTP服务器目录 + Enable bool `json:"enable"` // 是否启用 +} diff --git a/src/modules/network_data/network_data.go b/src/modules/network_data/network_data.go index 3749e197..7385c57d 100644 --- a/src/modules/network_data/network_data.go +++ b/src/modules/network_data/network_data.go @@ -206,6 +206,25 @@ func Setup(router *gin.Engine) { ) } + // 备份数据 + backupGroup := neDataGroup.Group("/backup") + { + backupGroup.GET("/ftp", + middleware.PreAuthorize(nil), + controller.NewBackup.FTPInfo, + ) + backupGroup.PUT("/ftp", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.backup", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewBackup.FTPUpdate, + ) + backupGroup.POST("/ftp", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.backup", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewBackup.FTPPush, + ) + } + // 网元UDM 鉴权用户信息 udmAuthGroup := neDataGroup.Group("/udm/auth") { diff --git a/src/modules/network_data/service/backup.go b/src/modules/network_data/service/backup.go new file mode 100644 index 00000000..f6cbd08f --- /dev/null +++ b/src/modules/network_data/service/backup.go @@ -0,0 +1,86 @@ +package service + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + + "be.ems/src/framework/ssh" + "be.ems/src/modules/network_data/model" + neService "be.ems/src/modules/network_element/service" + systemService "be.ems/src/modules/system/service" +) + +// 实例化数据层 Backup 结构体 +var NewBackup = &Backup{ + BACKUP_DIR: "/usr/local/omc/backup", + neInfoService: neService.NewNeInfo, + sysConfigService: systemService.NewSysConfig, +} + +// Backup 备份相关 服务层处理 +type Backup struct { + BACKUP_DIR string // 备份目录 + neInfoService *neService.NeInfo // 网元信息服务 + sysConfigService *systemService.SysConfig // 参数配置服务 +} + +// FTPConfigUpdate 更新FTP配置信息 +func (r Backup) FTPConfigUpdate(value, updateBy string) int64 { + cfg := r.sysConfigService.FindByKey("neData.backupDataFTP") + if cfg.ConfigId == 0 { + return 0 + } + cfg.ConfigValue = value + cfg.UpdateBy = updateBy + return r.sysConfigService.UpdateEncryptValue(cfg) +} + +// FTPConfigInfo 获取FTP配置信息 +func (r Backup) FTPConfigInfo() model.BackupDataFTP { + info := model.BackupDataFTP{} + // 获取配置 + cfg := r.sysConfigService.FindByKeyDecryptValue("neData.backupDataFTP") + if cfg.ConfigId > 0 && cfg.ConfigValue != "" { + if err := json.Unmarshal([]byte(cfg.ConfigValue), &info); err != nil { + return info + } + } + return info +} + +// FTPPushFile 推送文件到FTP +func (r Backup) FTPPushFile(localFilePath, tag string) error { + cfgData := r.FTPConfigInfo() + if !cfgData.Enable { + return fmt.Errorf("setting remote backup ftp is disabled") + } + + connSSH := ssh.ConnSSH{ + User: cfgData.Username, + Password: cfgData.Password, + Addr: cfgData.ToIp, + Port: cfgData.ToPort, + AuthMode: "0", + } + sshClient, err := connSSH.NewClient() + if err != nil { + return err + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return err + } + defer sftpClient.Close() + + remotePath := strings.Replace(localFilePath, r.BACKUP_DIR, tag, 1) + remotePath = filepath.Join(cfgData.Dir, remotePath) + // 复制到远程 + if err = sftpClient.CopyFileLocalToRemote(localFilePath, remotePath); err != nil { + return fmt.Errorf("error uploading file") + } + return nil +}