1
0

merge: 合并代码20240706

This commit is contained in:
TsMask
2024-07-06 18:27:00 +08:00
parent 3b50e2f3f8
commit 49860c2f28
145 changed files with 4366 additions and 4051 deletions

View File

@@ -1,7 +1,7 @@
# 项目信息
framework:
name: "CN EMS"
version: "2.2405.4"
version: "2.2407.1"
# 应用服务配置
server:

View File

@@ -1,12 +1,12 @@
package admin
// 管理员常量信息
// 系统管理员常量信息
// 管理员-系统指定角色ID
// 系统管理员-系统指定角色ID
const ROLE_ID = "1"
// 管理员-系统指定角色KEY
const ROLE_KEY = "admin"
// 系统管理员-系统指定角色KEY
const ROLE_KEY = "system"
// 管理员-系统指定权限
// 系统管理员-系统指定权限
const PERMISSION = "*:*:*"

View File

@@ -8,6 +8,7 @@ import (
"time"
"be.ems/src/framework/constants/common"
tokenConstants "be.ems/src/framework/constants/token"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/utils/parse"
@@ -178,6 +179,8 @@ var maskProperties []string = []string{
"oldPassword",
"newPassword",
"confirmPassword",
tokenConstants.RESPONSE_FIELD,
tokenConstants.ACCESS_TOKEN,
}
// processSensitiveFields 处理敏感属性字段

View File

@@ -20,6 +20,7 @@ var URL_WHITE_LIST = []string{
"/systemState",
"/omcNeConfig",
"/cdrEvent",
"/ueEvent",
"/upload-ue",
"/oauth/token",
}

View File

@@ -84,8 +84,8 @@ func Authorization(c *gin.Context) string {
return ""
}
// 拆分 Authorization 请求头,提取 JWT 令牌部分
arr := strings.Split(authHeader, token.HEADER_PREFIX)
if len(arr) == 2 && arr[1] == "" {
arr := strings.SplitN(authHeader, token.HEADER_PREFIX, 2)
if len(arr) < 2 {
return ""
}
return arr[1]
@@ -212,6 +212,11 @@ func LoginUserToDataScopeSQL(c *gin.Context, deptAlias string, userAlias string)
conditions = append(conditions, sql)
}
if roledatascope.DEPT == dataScope {
sql := fmt.Sprintf(`%s.dept_id = '%s'`, deptAlias, userInfo.DeptID)
conditions = append(conditions, sql)
}
if roledatascope.DEPT_AND_CHILD == dataScope {
sql := fmt.Sprintf(`%s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = '%s' or find_in_set('%s' , ancestors ) )`, deptAlias, userInfo.DeptID, userInfo.DeptID)
conditions = append(conditions, sql)
@@ -220,7 +225,7 @@ func LoginUserToDataScopeSQL(c *gin.Context, deptAlias string, userAlias string)
if roledatascope.SELF == dataScope {
// 数据权限为仅本人且没有userAlias别名不查询任何数据
if userAlias == "" {
sql := fmt.Sprintf(`%s.dept_id = '0'`, deptAlias)
sql := fmt.Sprintf(`%s.parent_id = '0'`, deptAlias)
conditions = append(conditions, sql)
} else {
sql := fmt.Sprintf(`%s.user_id = '%s'`, userAlias, userInfo.UserID)

View File

@@ -48,7 +48,14 @@ func ParseDateToStr(date any, formatStr string) string {
if v == 0 {
return ""
}
t = time.UnixMilli(v)
if v > 9999999999 {
t = time.UnixMilli(v)
} else if v > 999999999 {
t = time.Unix(v, 0)
} else {
logger.Infof("utils ParseDateToStr err %v", "Invalid timestamp")
return ""
}
case string:
parsedTime, err := time.Parse(formatStr, v)
if err != nil {

View File

@@ -172,10 +172,10 @@ func Color(colorStr string) *color.RGBA {
}
}
// ConvertIPMask 转换IP网络地址掩码 24 -> 255.255.255.0
// ConvertIPMask 转换IP网络地址掩码 24->"255.255.255.0" 20->"255.255.240.0"
func ConvertIPMask(bits int64) string {
if bits < 0 || bits > 32 {
return "Invalid Mask Bits"
return "255.255.255.255"
}
// 构建一个32位的uint32类型掩码指定前bits位为1其余为0

View File

@@ -66,12 +66,12 @@ func SetFieldValue(obj any, fieldName string, value any) {
}
fieldValue.SetFloat(floatValue)
case reflect.Struct:
fmt.Printf("%s 时间解析 %s %v \n", fieldName, fieldValue.Type(), value)
fmt.Printf("%s time resolution %s %v \n", fieldName, fieldValue.Type(), value)
if fieldValue.Type() == reflect.TypeOf(time.Time{}) && value != nil {
// 解析 value 并转换为 time.Time 类型
parsedTime, err := time.Parse("2006-01-02 15:04:05 +0800 CST", fmt.Sprintf("%v", value))
if err != nil {
fmt.Println("时间解析出错:", err)
fmt.Println("Time resolution error:", err)
} else {
// 设置字段的值
fieldValue.Set(reflect.ValueOf(parsedTime))

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"strings"
"be.ems/src/framework/config"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/cmd"
"be.ems/src/framework/utils/parse"
@@ -23,35 +22,31 @@ type FileListRow struct {
}
// 文件列表
// neIp 网元IP空字符串为本地
// search 文件名后模糊*
//
// return 目录大小,行记录,异常
func FileList(path, neIp, search string) (string, []FileListRow, error) {
func FileList(sshClient *ConnSSH, path, search string) (string, []FileListRow, error) {
totalSize := ""
var rows []FileListRow
rowStr := ""
// 发送命令
searchStr := ""
searchStr := "*"
if search != "" {
searchStr = search + "*"
searchStr = search + searchStr
}
pathStr := fmt.Sprintf("cd %s \n", path)
cmdStr := fmt.Sprintf("ls -lht --time-style=+%%s %s \n", searchStr)
cmdStr := fmt.Sprintf("cd %s && ls -lthd --time-style=+%%s %s", path, searchStr)
// 是否远程读取
if neIp != "" {
usernameNe := config.Get("ne.user").(string) // 网元统一用户
sshHost := fmt.Sprintf("%s@%s", usernameNe, neIp)
resultStr, err := cmd.ExecWithCheck("ssh", sshHost, pathStr, cmdStr)
// 是否远程客户端读取
if sshClient == nil {
resultStr, err := cmd.Execf(cmdStr)
if err != nil {
logger.Errorf("Ne FileList Path: %s, Search: %s, Error:%s", path, search, err.Error())
return totalSize, rows, err
}
rowStr = resultStr
} else {
resultStr, err := cmd.Execf(pathStr, cmdStr)
resultStr, err := sshClient.RunCMD(cmdStr)
if err != nil {
logger.Errorf("Ne FileList Path: %s, Search: %s, Error:%s", path, search, err.Error())
return totalSize, rows, err

View File

@@ -1,55 +0,0 @@
package ssh
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
"be.ems/src/framework/config"
"be.ems/src/framework/logger"
)
// 网元NE 文件复制到远程文件
func FileSCPLocalToNe(neIp, localPath, nePath string) error {
usernameNe := config.Get("ne.user").(string)
// scp /path/to/local/file.txt user@remote-server:/path/to/remote/directory/
neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath)
cmd := exec.Command("scp", "-r", localPath, neDir)
output, err := cmd.CombinedOutput()
if err != nil {
logger.Errorf("FileSCPLocalToNe %s => %s", output, err.Error())
return err
}
return nil
}
// 网元NE 远程文件复制到本地文件
func FileSCPNeToLocal(neIp, nePath, localPath string) error {
// 确保文件夹路径存在
if err := os.MkdirAll(filepath.Dir(localPath), 0775); err != nil {
logger.Errorf("FileSCPNeToLocal MkdirAll err %v", err)
return err
}
// 如果目标文件已经存在,先将目标文件重命名
if info, err := os.Stat(localPath); err == nil && !info.IsDir() {
ext := filepath.Ext(localPath)
name := localPath[0 : len(localPath)-len(ext)]
newName := fmt.Sprintf("%s-%s%s", name, time.Now().Format("20060102_150405"), ext)
err := os.Rename(localPath, newName)
if err != nil {
return err
}
}
usernameNe := config.Get("ne.user").(string)
// scp user@remote-server:/path/to/remote/directory/ /path/to/local/file.txt
neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath)
cmd := exec.Command("scp", "-r", neDir, localPath)
output, err := cmd.CombinedOutput()
if err != nil {
logger.Errorf("FileSCPNeToLocal %s => %s", output, err.Error())
return err
}
return nil
}

View File

@@ -0,0 +1,202 @@
package ssh
import (
"io"
"os"
"path/filepath"
"be.ems/src/framework/logger"
gosftp "github.com/pkg/sftp"
)
// SSHClientSFTP SSH客户端SFTP对象
type SSHClientSFTP struct {
Client *gosftp.Client
}
// Close 关闭会话
func (s *SSHClientSFTP) Close() {
if s.Client != nil {
s.Client.Close()
}
}
// CopyDirRemoteToLocal 复制目录-远程到本地
func (s *SSHClientSFTP) CopyDirRemoteToLocal(remoteDir, localDir string) error {
// 列出远程目录中的文件和子目录
remoteFiles, err := s.Client.ReadDir(remoteDir)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to reading remote directory %s: => %s", remoteDir, err.Error())
return err
}
// 创建本地目录
err = os.MkdirAll(localDir, 0775)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to creating local directory %s: => %s", localDir, err.Error())
return err
}
// 遍历远程文件和子目录并复制到本地
for _, remoteFile := range remoteFiles {
remotePath := filepath.Join(remoteDir, remoteFile.Name())
localPath := filepath.Join(localDir, remoteFile.Name())
if remoteFile.IsDir() {
// 如果是子目录,则递归复制子目录
err = s.CopyDirRemoteToLocal(remotePath, localPath)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to copying remote directory %s: => %s", remotePath, err.Error())
continue
}
} else {
// 如果是文件,则复制文件内容
remoteFile, err := s.Client.Open(remotePath)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to opening remote file %s: => %s", remotePath, err.Error())
continue
}
defer remoteFile.Close()
localFile, err := os.Create(localPath)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to creating local file %s: => %s", localPath, err.Error())
continue
}
defer localFile.Close()
_, err = io.Copy(localFile, remoteFile)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to copying file contents from %s to %s: => %s", remotePath, localPath, err.Error())
continue
}
}
}
return nil
}
// CopyDirRemoteToLocal 复制目录-本地到远程
func (s *SSHClientSFTP) CopyDirLocalToRemote(localDir, remoteDir string) error {
// 创建远程目录
err := s.Client.MkdirAll(remoteDir)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to creating remote directory %s: => %s", remoteDir, err.Error())
return err
}
// 遍历本地目录中的文件和子目录并复制到远程
err = filepath.Walk(localDir, func(localPath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 生成远程路径
remotePath := filepath.Join(remoteDir, localPath[len(localDir):])
if info.IsDir() {
// 如果是子目录,则创建远程目录
err := s.Client.MkdirAll(remotePath)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to creating remote directory %s: => %s", remotePath, err.Error())
return nil
}
} else {
// 如果是文件,则复制文件内容
localFile, err := os.Open(localPath)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to opening local file %s: => %s", localPath, err.Error())
return nil
}
defer localFile.Close()
remoteFile, err := s.Client.Create(remotePath)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to creating remote file %s: => %s", remotePath, err.Error())
return nil
}
defer remoteFile.Close()
_, err = io.Copy(remoteFile, localFile)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to copying file contents from %s to %s: => %s", localPath, remotePath, err.Error())
return nil
}
}
return nil
})
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to walking local directory: => %s", err.Error())
return err
}
return nil
}
// CopyDirRemoteToLocal 复制文件-远程到本地
func (s *SSHClientSFTP) CopyFileRemoteToLocal(remotePath, localPath string) error {
// 打开远程文件
remoteFile, err := s.Client.Open(remotePath)
if err != nil {
logger.Errorf("CopyFileRemoteToLocal failed to opening remote file: => %s", err.Error())
return err
}
defer remoteFile.Close()
if err := os.MkdirAll(filepath.Dir(localPath), 0775); err != nil {
return err
}
// 如果目标文件已经存在,先将目标文件重命名
// if info, err := os.Stat(localPath); err == nil && !info.IsDir() {
// ext := filepath.Ext(localPath)
// name := localPath[0 : len(localPath)-len(ext)]
// newName := fmt.Sprintf("%s-%s%s", name, time.Now().Format("20060102_150405"), ext)
// err := os.Rename(localPath, newName)
// if err != nil {
// return err
// }
// }
// 创建本地文件
localFile, err := os.Create(localPath)
if err != nil {
logger.Errorf("CopyFileRemoteToLocal failed to creating local file: => %s", err.Error())
return err
}
defer localFile.Close()
// 复制文件内容
_, err = io.Copy(localFile, remoteFile)
if err != nil {
logger.Errorf("CopyFileRemoteToLocal failed to copying contents: => %s", err.Error())
return err
}
return nil
}
// CopyDirRemoteToLocal 复制文件-本地到远程
func (s *SSHClientSFTP) CopyFileLocalToRemote(localPath, remotePath string) error {
// 打开本地文件
localFile, err := os.Open(localPath)
if err != nil {
logger.Errorf("CopyFileLocalToRemote failed to opening local file: => %s", err.Error())
return err
}
defer localFile.Close()
// 创建远程文件
remoteFile, err := s.Client.Create(remotePath)
if err != nil {
logger.Errorf("CopyFileLocalToRemote failed to creating remote file: => %s", err.Error())
return err
}
defer remoteFile.Close()
// 复制文件内容
_, err = io.Copy(remoteFile, localFile)
if err != nil {
logger.Errorf("CopyFileLocalToRemote failed to copying contents: => %s", err.Error())
return err
}
return nil
}

View File

@@ -1,14 +1,10 @@
package ssh
import (
"bytes"
"fmt"
"io"
"os"
"os/user"
"path/filepath"
"strings"
"sync"
"time"
"be.ems/src/framework/logger"
@@ -89,24 +85,6 @@ func (c *ConnSSH) Close() {
}
}
// NewClientByLocalPrivate 创建SSH客户端-本地私钥(~/.ssh/id_rsa)直连
//
// ssh.ConnSSH{
// User: "user",
// Addr: "192.168.x.x",
// Port: body.Port,
// }
func (c *ConnSSH) NewClientByLocalPrivate() (*ConnSSH, error) {
c.Port = 22
c.AuthMode = "1"
privateKey, err := c.CurrentUserRsaKey(false)
if err != nil {
return nil, err
}
c.PrivateKey = privateKey
return c.NewClient()
}
// RunCMD 执行单次命令
func (c *ConnSSH) RunCMD(cmd string) (string, error) {
if c.Client == nil {
@@ -128,57 +106,6 @@ func (c *ConnSSH) RunCMD(cmd string) (string, error) {
return c.LastResult, err
}
// SendToAuthorizedKeys 发送当前用户私钥到远程服务器进行授权密钥
func (c *ConnSSH) SendToAuthorizedKeys() error {
publicKey, err := c.CurrentUserRsaKey(true)
if err != nil {
return err
}
authorizedKeysEntry := fmt.Sprintln(strings.TrimSpace(publicKey))
cmdStrArr := []string{
fmt.Sprintf("sudo mkdir -p /home/%s/.ssh && sudo chown %s:%s /home/%s/.ssh && sudo chmod 700 /home/%s/.ssh", c.User, c.User, c.User, c.User, c.User),
fmt.Sprintf("sudo touch /home/%s/.ssh/authorized_keys && sudo chown %s:%s /home/%s/.ssh/authorized_keys && sudo chmod 600 /home/%s/.ssh/authorized_keys", c.User, c.User, c.User, c.User, c.User),
fmt.Sprintf("echo '%s' | sudo tee -a /home/%s/.ssh/authorized_keys", authorizedKeysEntry, c.User),
}
_, err = c.RunCMD(strings.Join(cmdStrArr, " && "))
if err != nil {
logger.Errorf("SendAuthorizedKeys echo err %s", err.Error())
return err
}
return nil
}
// CurrentUserRsaKey 当前用户OMC使用的RSA私钥
// 默认读取私钥
// ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
// ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
func (c *ConnSSH) CurrentUserRsaKey(publicKey bool) (string, error) {
usr, err := user.Current()
if err != nil {
logger.Errorf("CurrentUserRsaKey get => %s", err.Error())
return "", err
}
// 是否存在私钥并创建
keyPath := fmt.Sprintf("%s/.ssh/id_rsa", usr.HomeDir)
if _, err := os.Stat(keyPath); err != nil {
if _, err := cmd.ExecWithCheck("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath); err != nil {
logger.Errorf("CurrentUserPrivateKey ssh-keygen [%s] rsa => %s", usr.Username, err.Error())
}
}
// 读取用户默认的文件
if publicKey {
keyPath = keyPath + ".pub"
}
key, err := os.ReadFile(keyPath)
if err != nil {
logger.Errorf("CurrentUserRsaKey [%s] read => %s", usr.Username, err.Error())
return "", fmt.Errorf("read file %s fail", keyPath)
}
return string(key), nil
}
// NewClientSession 创建SSH客户端会话对象
func (c *ConnSSH) NewClientSession(cols, rows int) (*SSHClientSession, error) {
sshSession, err := c.Client.NewSession()
@@ -216,69 +143,6 @@ func (c *ConnSSH) NewClientSession(cols, rows int) (*SSHClientSession, error) {
}, nil
}
// SSHClientSession SSH客户端会话对象
type SSHClientSession struct {
Stdin io.WriteCloser
Stdout *singleWriter
Session *gossh.Session
}
// Close 关闭会话
func (s *SSHClientSession) Close() {
if s.Stdin != nil {
s.Stdin.Close()
}
if s.Stdout != nil {
s.Stdout = nil
}
if s.Session != nil {
s.Session.Close()
}
}
// Write 写入命令 回车(\n)才会执行
func (s *SSHClientSession) Write(cmd string) (int, error) {
if s.Stdin == nil {
return 0, fmt.Errorf("ssh client session is nil to content write failed")
}
return s.Stdin.Write([]byte(cmd))
}
// Read 读取结果
func (s *SSHClientSession) Read() []byte {
if s.Stdout == nil {
return []byte{}
}
bs := s.Stdout.Bytes()
if len(bs) > 0 {
s.Stdout.Reset()
return bs
}
return []byte{}
}
// singleWriter SSH客户端会话消息
type singleWriter struct {
b bytes.Buffer
mu sync.Mutex
}
func (w *singleWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.b.Write(p)
}
func (w *singleWriter) Bytes() []byte {
w.mu.Lock()
defer w.mu.Unlock()
return w.b.Bytes()
}
func (w *singleWriter) Reset() {
w.mu.Lock()
defer w.mu.Unlock()
w.b.Reset()
}
// NewClientSFTP 创建SSH客户端SFTP对象
func (c *ConnSSH) NewClientSFTP() (*SSHClientSFTP, error) {
sftpClient, err := gosftp.NewClient(c.Client)
@@ -292,193 +156,70 @@ func (c *ConnSSH) NewClientSFTP() (*SSHClientSFTP, error) {
}, nil
}
// SSHClientSFTP SSH客户端SFTP对象
type SSHClientSFTP struct {
Client *gosftp.Client
// NewClientByLocalPrivate 创建SSH客户端-本地私钥(~/.ssh/id_rsa)直连
//
// ssh.ConnSSH{
// User: "user",
// Addr: "192.168.x.x",
// Port: body.Port,
// }
func (c *ConnSSH) NewClientByLocalPrivate() (*ConnSSH, error) {
c.Port = 22
c.AuthMode = "1"
privateKey, err := c.CurrentUserRsaKey(false)
if err != nil {
return nil, err
}
c.PrivateKey = privateKey
return c.NewClient()
}
// Close 关闭会话
func (s *SSHClientSFTP) Close() {
if s.Client != nil {
s.Client.Close()
}
}
// CopyDirRemoteToLocal 复制目录-远程到本地
func (s *SSHClientSFTP) CopyDirRemoteToLocal(remoteDir, localDir string) error {
// 列出远程目录中的文件和子目录
remoteFiles, err := s.Client.ReadDir(remoteDir)
// CurrentUserRsaKey 当前用户OMC使用的RSA私钥
// 默认读取私钥
// ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
// ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
func (c *ConnSSH) CurrentUserRsaKey(publicKey bool) (string, error) {
usr, err := user.Current()
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to reading remote directory %s: => %s", remoteDir, err.Error())
return err
logger.Errorf("CurrentUserRsaKey get => %s", err.Error())
return "", err
}
// 创建本地目录
err = os.MkdirAll(localDir, 0775)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to creating local directory %s: => %s", localDir, err.Error())
return err
}
// 遍历远程文件和子目录并复制到本地
for _, remoteFile := range remoteFiles {
remotePath := filepath.Join(remoteDir, remoteFile.Name())
localPath := filepath.Join(localDir, remoteFile.Name())
if remoteFile.IsDir() {
// 如果是子目录,则递归复制子目录
err = s.CopyDirRemoteToLocal(remotePath, localPath)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to copying remote directory %s: => %s", remotePath, err.Error())
continue
}
} else {
// 如果是文件,则复制文件内容
remoteFile, err := s.Client.Open(remotePath)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to opening remote file %s: => %s", remotePath, err.Error())
continue
}
defer remoteFile.Close()
localFile, err := os.Create(localPath)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to creating local file %s: => %s", localPath, err.Error())
continue
}
defer localFile.Close()
_, err = io.Copy(localFile, remoteFile)
if err != nil {
logger.Errorf("CopyDirRemoteToLocal failed to copying file contents from %s to %s: => %s", remotePath, localPath, err.Error())
continue
}
// 是否存在私钥并创建
keyPath := fmt.Sprintf("%s/.ssh/id_rsa", usr.HomeDir)
if _, err := os.Stat(keyPath); err != nil {
if _, err := cmd.ExecWithCheck("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath); err != nil {
logger.Errorf("CurrentUserPrivateKey ssh-keygen [%s] rsa => %s", usr.Username, err.Error())
}
}
return nil
// 读取用户默认的文件
if publicKey {
keyPath = keyPath + ".pub"
}
key, err := os.ReadFile(keyPath)
if err != nil {
logger.Errorf("CurrentUserRsaKey [%s] read => %s", usr.Username, err.Error())
return "", fmt.Errorf("read file %s fail", keyPath)
}
return string(key), nil
}
// CopyDirRemoteToLocal 复制目录-本地到远程
func (s *SSHClientSFTP) CopyDirLocalToRemote(localDir, remoteDir string) error {
// 创建远程目录
err := s.Client.MkdirAll(remoteDir)
// SendToAuthorizedKeys 发送当前用户私钥到远程服务器进行授权密钥
func (c *ConnSSH) SendToAuthorizedKeys() error {
publicKey, err := c.CurrentUserRsaKey(true)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to creating remote directory %s: => %s", remoteDir, err.Error())
return err
}
// 遍历本地目录中的文件和子目录并复制到远程
err = filepath.Walk(localDir, func(localPath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 生成远程路径
remotePath := filepath.Join(remoteDir, localPath[len(localDir):])
if info.IsDir() {
// 如果是子目录,则创建远程目录
err := s.Client.MkdirAll(remotePath)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to creating remote directory %s: => %s", remotePath, err.Error())
return nil
}
} else {
// 如果是文件,则复制文件内容
localFile, err := os.Open(localPath)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to opening local file %s: => %s", localPath, err.Error())
return nil
}
defer localFile.Close()
remoteFile, err := s.Client.Create(remotePath)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to creating remote file %s: => %s", remotePath, err.Error())
return nil
}
defer remoteFile.Close()
_, err = io.Copy(remoteFile, localFile)
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to copying file contents from %s to %s: => %s", localPath, remotePath, err.Error())
return nil
}
}
return nil
})
authorizedKeysEntry := fmt.Sprintln(strings.TrimSpace(publicKey))
cmdStrArr := []string{
fmt.Sprintf("sudo mkdir -p /home/%s/.ssh && sudo chown %s:%s /home/%s/.ssh && sudo chmod 700 /home/%s/.ssh", c.User, c.User, c.User, c.User, c.User),
fmt.Sprintf("sudo touch /home/%s/.ssh/authorized_keys && sudo chown %s:%s /home/%s/.ssh/authorized_keys && sudo chmod 600 /home/%s/.ssh/authorized_keys", c.User, c.User, c.User, c.User, c.User),
fmt.Sprintf("echo '%s' | sudo tee -a /home/%s/.ssh/authorized_keys", authorizedKeysEntry, c.User),
}
_, err = c.RunCMD(strings.Join(cmdStrArr, " && "))
if err != nil {
logger.Errorf("CopyDirLocalToRemote failed to walking local directory: => %s", err.Error())
return err
}
return nil
}
// CopyDirRemoteToLocal 复制文件-远程到本地
func (s *SSHClientSFTP) CopyFileRemoteToLocal(remotePath, localPath string) error {
// 打开远程文件
remoteFile, err := s.Client.Open(remotePath)
if err != nil {
logger.Errorf("CopyFileRemoteToLocal failed to opening remote file: => %s", err.Error())
return err
}
defer remoteFile.Close()
if err := os.MkdirAll(filepath.Dir(localPath), 0775); err != nil {
return err
}
// 如果目标文件已经存在,先将目标文件重命名
// if info, err := os.Stat(localPath); err == nil && !info.IsDir() {
// ext := filepath.Ext(localPath)
// name := localPath[0 : len(localPath)-len(ext)]
// newName := fmt.Sprintf("%s-%s%s", name, time.Now().Format("20060102_150405"), ext)
// err := os.Rename(localPath, newName)
// if err != nil {
// return err
// }
// }
// 创建本地文件
localFile, err := os.Create(localPath)
if err != nil {
logger.Errorf("CopyFileRemoteToLocal failed to creating local file: => %s", err.Error())
return err
}
defer localFile.Close()
// 复制文件内容
_, err = io.Copy(localFile, remoteFile)
if err != nil {
logger.Errorf("CopyFileRemoteToLocal failed to copying contents: => %s", err.Error())
return err
}
return nil
}
// CopyDirRemoteToLocal 复制文件-本地到远程
func (s *SSHClientSFTP) CopyFileLocalToRemote(localPath, remotePath string) error {
// 打开本地文件
localFile, err := os.Open(localPath)
if err != nil {
logger.Errorf("CopyFileLocalToRemote failed to opening local file: => %s", err.Error())
return err
}
defer localFile.Close()
// 创建远程文件
remoteFile, err := s.Client.Create(remotePath)
if err != nil {
logger.Errorf("CopyFileLocalToRemote failed to creating remote file: => %s", err.Error())
return err
}
defer remoteFile.Close()
// 复制文件内容
_, err = io.Copy(remoteFile, localFile)
if err != nil {
logger.Errorf("CopyFileLocalToRemote failed to copying contents: => %s", err.Error())
logger.Errorf("SendAuthorizedKeys echo err %s", err.Error())
return err
}
return nil

View File

@@ -0,0 +1,73 @@
package ssh
import (
"bytes"
"fmt"
"io"
"sync"
gossh "golang.org/x/crypto/ssh"
)
// SSHClientSession SSH客户端会话对象
type SSHClientSession struct {
Stdin io.WriteCloser
Stdout *singleWriter
Session *gossh.Session
}
// Close 关闭会话
func (s *SSHClientSession) Close() {
if s.Stdin != nil {
s.Stdin.Close()
}
if s.Stdout != nil {
s.Stdout = nil
}
if s.Session != nil {
s.Session.Close()
}
}
// Write 写入命令 回车(\n)才会执行
func (s *SSHClientSession) Write(cmd string) (int, error) {
if s.Stdin == nil {
return 0, fmt.Errorf("ssh client session is nil to content write failed")
}
return s.Stdin.Write([]byte(cmd))
}
// Read 读取结果
func (s *SSHClientSession) Read() []byte {
if s.Stdout == nil {
return []byte{}
}
bs := s.Stdout.Bytes()
if len(bs) > 0 {
s.Stdout.Reset()
return bs
}
return []byte{}
}
// singleWriter SSH客户端会话消息
type singleWriter struct {
b bytes.Buffer
mu sync.Mutex
}
func (w *singleWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.b.Write(p)
}
func (w *singleWriter) Bytes() []byte {
w.mu.Lock()
defer w.mu.Unlock()
return w.b.Bytes()
}
func (w *singleWriter) Reset() {
w.mu.Lock()
defer w.mu.Unlock()
w.b.Reset()
}

View File

@@ -0,0 +1,77 @@
package telnet
import (
"fmt"
"strings"
)
// ConvertToStr 转换为string
func ConvertToStr(telnetClient *ConnTelnet, cmd string) (string, error) {
output, err := telnetClient.RunCMD(cmd)
if err != nil {
return "", err
}
str := strings.ToLower(output)
// 截断
index := strings.Index(str, "\n")
if index != -1 {
str = str[:index]
}
// 命令成功
if strings.Contains(str, "ok") || strings.Contains(str, "success") {
return str, nil
}
return "", fmt.Errorf(str)
}
// ConvertToMap 转换为map
func ConvertToMap(telnetClient *ConnTelnet, cmd string) (map[string]string, error) {
output, err := telnetClient.RunCMD(cmd)
if err != nil {
return nil, err
}
// 无数据
if strings.HasPrefix(output, "No ") {
// 截断
index := strings.Index(output, "\n")
if index != -1 {
output = output[:index]
}
return nil, fmt.Errorf(output)
}
// 初始化一个map用于存储拆分后的键值对
m := make(map[string]string)
var items []string
if strings.Contains(output, "\r\n") {
// 按照分隔符"\r\n"进行拆分
items = strings.Split(output, "\r\n")
} else if strings.Contains(output, "\n") {
// 按照分隔符"\n"进行拆分
items = strings.Split(output, "\n")
}
// 遍历拆分后的结果
for _, item := range items {
var pair []string
if strings.Contains(item, "=") {
// 按照分隔符"="进行拆分键值对
pair = strings.SplitN(item, "=", 2)
} else if strings.Contains(item, ":") {
// 按照分隔符":"进行拆分键值对
pair = strings.SplitN(item, ":", 2)
}
if len(pair) == 2 {
// 将键值对存入map中
m[pair[0]] = pair[1]
}
}
return m, err
}

View File

@@ -50,11 +50,11 @@ func (c *ConnTelnet) NewClient() (*ConnTelnet, error) {
// fmt.Fprintln(client, c.User)
// fmt.Fprintln(client, c.Password)
// 需要确保接收方理解并正确处理发送窗口大小设置命令
client.Write([]byte{255, 251, 31}) // 发送窗口大小选项
client.Write([]byte{255, 250, 31, 0, 128, 0, 120, 255, 240}) // 发送窗口行和列的大小
c.Client = &client
// 调整窗口大小 (120 列 x 128 行)
requestPty(c.Client, 120, 128)
// 排空连接登录的信息
c.RunCMD("")
return c, nil
@@ -73,8 +73,6 @@ func (c *ConnTelnet) RunCMD(cmd string) (string, error) {
return "", fmt.Errorf("telnet client not connected")
}
conn := *c.Client
var buf bytes.Buffer
tmp := make([]byte, 1024)
// 写入命令
if cmd != "" {
@@ -83,22 +81,24 @@ func (c *ConnTelnet) RunCMD(cmd string) (string, error) {
}
}
// 读取命令消息
var buf bytes.Buffer
tmp := make([]byte, 1024)
for {
// 设置读取超时时间为1000毫秒
conn.SetReadDeadline(time.Now().Add(1000 * time.Millisecond))
// 读取命令消息
n, err := conn.Read(tmp)
if err != nil {
// 判断是否是超时错误
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
break
}
if n == 0 || err != nil {
tmp = nil
break
}
if n == 0 {
tmpStr := string(tmp[:n])
buf.WriteString(tmpStr)
// 是否有终止符
if strings.HasSuffix(tmpStr, ">") || strings.HasSuffix(tmpStr, "> ") || strings.HasSuffix(tmpStr, "# ") {
tmp = nil
break
}
buf.Write(tmp[:n])
}
defer buf.Reset()
@@ -107,77 +107,24 @@ func (c *ConnTelnet) RunCMD(cmd string) (string, error) {
}
// NewClient 创建Telnet客户端会话对象
func (c *ConnTelnet) NewClientSession(cols, rows uint8) (*TelnetClientSession, error) {
func (c *ConnTelnet) NewClientSession(cols, rows int) (*TelnetClientSession, error) {
if c.Client == nil {
return nil, fmt.Errorf("telnet client not connected")
}
requestPty(c.Client, cols, rows)
return &TelnetClientSession{
Client: *c.Client,
}, nil
}
// TelnetClientSession Telnet客户端会话对象
type TelnetClientSession struct {
Client net.Conn
}
// Close 关闭会话
func (s *TelnetClientSession) Close() {
if s.Client != nil {
s.Client.Close()
}
}
// Write 写入命令 不带回车(\n)也会执行根据客户端情况
func (s *TelnetClientSession) Write(cmd string) (int, error) {
if s.Client == nil {
return 0, fmt.Errorf("client is nil to content write failed")
}
return s.Client.Write([]byte(cmd))
}
// Read 读取结果 等待一会才有结果
func (s *TelnetClientSession) Read() []byte {
if s.Client == nil {
return []byte{}
}
buf := make([]byte, 1024)
// 设置读取超时时间为100毫秒
s.Client.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
_, err := s.Client.Read(buf)
if err != nil {
return []byte{}
}
return buf
}
// CombinedOutput 发送命令带结果返回
func (s *TelnetClientSession) CombinedOutput(cmd string) (string, error) {
n, err := s.Write(cmd)
if n == 0 || err != nil {
return "", err
}
var buf bytes.Buffer
tmp := make([]byte, 1024)
for {
// 设置读取超时时间为1000毫秒
s.Client.SetReadDeadline(time.Now().Add(1000 * time.Millisecond))
n, err := s.Client.Read(tmp)
if err != nil {
// 判断是否是超时错误
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
break
}
break
}
if n == 0 {
break
}
buf.Write(tmp[:n])
}
defer buf.Reset()
return buf.String(), nil
// requestPty 调整终端窗口大小
func requestPty(client *net.Conn, cols, rows int) error {
if client == nil {
return fmt.Errorf("telnet client not connected")
}
conn := *client
// 需要确保接收方理解并正确处理发送窗口大小设置命令
conn.Write([]byte{255, 251, 31})
conn.Write([]byte{255, 250, 31, byte(cols >> 8), byte(cols & 0xFF), byte(rows >> 8), byte(rows & 0xFF), 255, 240})
return nil
}

View File

@@ -0,0 +1,74 @@
package telnet
import (
"bytes"
"fmt"
"net"
"time"
)
// TelnetClientSession Telnet客户端会话对象
type TelnetClientSession struct {
Client net.Conn
}
// Close 关闭会话
func (s *TelnetClientSession) Close() {
if s.Client != nil {
s.Client.Close()
}
}
// Write 写入命令 不带回车(\n)也会执行根据客户端情况
func (s *TelnetClientSession) Write(cmd string) (int, error) {
if s.Client == nil {
return 0, fmt.Errorf("client is nil to content write failed")
}
return s.Client.Write([]byte(cmd))
}
// Read 读取结果 等待一会才有结果
func (s *TelnetClientSession) Read() []byte {
if s.Client == nil {
return []byte{}
}
buf := make([]byte, 1024)
// 设置读取超时时间为100毫秒
s.Client.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
_, err := s.Client.Read(buf)
if err != nil {
return []byte{}
}
return buf
}
// CombinedOutput 发送命令带结果返回
func (s *TelnetClientSession) CombinedOutput(cmd string) (string, error) {
n, err := s.Write(cmd)
if n == 0 || err != nil {
return "", err
}
var buf bytes.Buffer
tmp := make([]byte, 1024)
for {
// 设置读取超时时间为1000毫秒
s.Client.SetReadDeadline(time.Now().Add(1000 * time.Millisecond))
n, err := s.Client.Read(tmp)
if err != nil {
// 判断是否是超时错误
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
break
}
break
}
if n == 0 {
break
}
buf.Write(tmp[:n])
}
defer buf.Reset()
return buf.String(), nil
}