From e754c4714c10088c3ed31a943f51e097d03f85e6 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 20 Jan 2025 17:20:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=97=A5=E5=BF=97=E5=A4=87=E4=BB=BD?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8C=E6=AD=A5FTP=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/lm/file_export/controller.go | 196 ++++++++++++++++++++++++++ features/lm/file_export/route.go | 13 +- go.mod | 3 + go.sum | 7 + src/config/config.default.yaml | 2 + 5 files changed, 220 insertions(+), 1 deletion(-) diff --git a/features/lm/file_export/controller.go b/features/lm/file_export/controller.go index d5610fff..060efa65 100644 --- a/features/lm/file_export/controller.go +++ b/features/lm/file_export/controller.go @@ -2,16 +2,26 @@ package file_export import ( "encoding/json" + "fmt" "net/http" "os" + "path" "path/filepath" + "time" + + "github.com/jlaffaye/ftp" "be.ems/lib/file" "be.ems/lib/log" "be.ems/lib/services" + "be.ems/src/framework/config" "be.ems/src/framework/datasource" "be.ems/src/framework/i18n" + "be.ems/src/framework/utils/crypto" "be.ems/src/framework/utils/ctx" + "be.ems/src/framework/utils/ssh" + "be.ems/src/framework/vo/result" + systemService "be.ems/src/modules/system/service" "github.com/gin-gonic/gin" ) @@ -140,3 +150,189 @@ 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 := ctx.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"` + Protocol string `json:"protocol" binding:"required,oneof=ssh ftp"` + Dir string `json:"dir" binding:"required"` + } + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 获取配置 + cfg := systemService.NewSysConfigImpl.SelectConfigByKey("sys.exportTable") + if cfg.ConfigID != "" { + // 加密body + appKey := config.Get("aes.appKey").(string) + byteData, err := json.Marshal(body) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + bodyEn, err := crypto.AESEncryptBase64(string(byteData), appKey) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + // 更新 + cfg.ConfigValue = bodyEn + systemService.NewSysConfigImpl.UpdateConfig(cfg) + } + + c.JSON(200, result.Ok(nil)) +} + +// 设置FTP配置 +// GET /table/ftp +func (m *SysJob) GetFTPConfig(c *gin.Context) { + // 获取配置 + cfg := systemService.NewSysConfigImpl.SelectConfigByKey("sys.exportTable") + if cfg.ConfigID != "" { + // 解密body + appKey := config.Get("aes.appKey").(string) + bodyDe, err := crypto.AESDecryptBase64(cfg.ConfigValue, appKey) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + 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"` + Protocol string `json:"protocol" binding:"required,oneof=ssh ftp"` + Dir string `json:"dir" binding:"required"` + } + err = json.Unmarshal([]byte(bodyDe), &body) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.OkData(body)) + return + } + + c.JSON(200, result.Ok(nil)) +} + +// FTP发送 +// PUT /table/ftp +func (m *SysJob) PutFTP(c *gin.Context) { + language := ctx.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, result.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, result.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"` + Protocol string `json:"protocol" binding:"required,oneof=ssh ftp"` + Dir string `json:"dir" binding:"required"` + } + cfg := systemService.NewSysConfigImpl.SelectConfigByKey("sys.exportTable") + if cfg.ConfigID != "" { + // 解密body + appKey := config.Get("aes.appKey").(string) + bodyDe, err := crypto.AESDecryptBase64(cfg.ConfigValue, appKey) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + err = json.Unmarshal([]byte(bodyDe), &cfgData) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + } + + if cfgData.Protocol == "ssh" { + 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, result.ErrMsg(err.Error())) + return + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + defer sftpClient.Close() + // 远程文件 + remotePath := filepath.Join(cfgData.Dir, path.Base(body.FilePath), body.FileName) + // 复制到远程 + if err = sftpClient.CopyFileLocalToRemote(localFilePath, remotePath); err != nil { + c.JSON(200, result.ErrMsg("error uploading file")) + return + } + c.JSON(200, result.Ok(nil)) + return + } + + if cfgData.Protocol == "ftp" { + // 连接到 FTP 服务器 + addr := fmt.Sprintf("%s:%d", cfgData.ToIp, cfgData.ToPort) + ftpComm, err := ftp.Dial(addr, ftp.DialWithTimeout(15*time.Second)) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + // 登录到 FTP 服务器 + err = ftpComm.Login(cfgData.Username, cfgData.Password) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + defer ftpComm.Quit() + // 打开本地文件 + file, err := os.Open(localFilePath) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + defer file.Close() + // 远程文件 + remotePath := filepath.Join(cfgData.Dir, path.Base(body.FilePath), body.FileName) + // 上传文件到 FTP 服务器 + err = ftpComm.Stor(remotePath, file) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + } + + c.JSON(200, result.Err(nil)) +} diff --git a/features/lm/file_export/route.go b/features/lm/file_export/route.go index d6caba91..eb3567c6 100644 --- a/features/lm/file_export/route.go +++ b/features/lm/file_export/route.go @@ -15,7 +15,18 @@ func Register(r *gin.RouterGroup) { middleware.PreAuthorize(nil), m.GetFileExportTable, ) - + lmTable.POST("/ftp", + middleware.PreAuthorize(nil), + m.SetFTPConfig, + ) + lmTable.GET("/ftp", + middleware.PreAuthorize(nil), + m.GetFTPConfig, + ) + lmTable.PUT("/ftp", + middleware.PreAuthorize(nil), + m.PutFTP, + ) } lmFile := r.Group("/file") { diff --git a/go.mod b/go.mod index e3f7ea41..9f078861 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.3 github.com/gosnmp/gosnmp v1.38.0 + github.com/jlaffaye/ftp v0.2.0 github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f github.com/linxGnu/gosmpp v0.3.0 github.com/matoous/go-nanoid/v2 v2.1.0 @@ -45,6 +46,8 @@ require ( ) require ( + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index e013b378..364d3101 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,11 @@ github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gosnmp/gosnmp v1.36.2-0.20231009064202-d306ed5aa998/go.mod h1:O938QjIS4vpSag1UTcnnBq9MfNmimuOGtvQsT1NbErc= github.com/gosnmp/gosnmp v1.38.0 h1:I5ZOMR8kb0DXAFg/88ACurnuwGwYkXWq3eLpJPHMEYc= github.com/gosnmp/gosnmp v1.38.0/go.mod h1:FE+PEZvKrFz9afP9ii1W3cprXuVZ17ypCcyyfYuu5LY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -126,6 +131,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= +github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= diff --git a/src/config/config.default.yaml b/src/config/config.default.yaml index 1ee1c3c5..f099fdee 100644 --- a/src/config/config.default.yaml +++ b/src/config/config.default.yaml @@ -175,6 +175,8 @@ aes: apiKey: "T9ox2DCzpLfJIPzkH9pKhsOTMOEMJcFv" # 网元主机密钥 hostKey: "AGT66VfY4SMaiT97a7df0aef1704d5c5" + # 应用密钥 + appKey: "E83dbfeb35BA4839232e2761b0FE5f32" # 用户配置 user: