1.地址:
https://github.com/Jrohy/webssh.git
2.添加中文注释地址:
https://github.com/tonyimax/webssh_cn.git
main.go分析
主包名:main
package main //主包名
依赖包加载
//导入依赖包
import (
"embed" //可执行文件资源嵌入
"flag" //标志变量
"fmt" //格式化
"github.com/gin-contrib/gzip" //压缩库
"github.com/gin-gonic/gin" //网页框架
"io/fs" //文件系统
"net/http" //http通信
"os" //系统信息
"strconv" //字符串转换
"strings" //字符串
"time" //时间
"webssh/controller" //websocket通信
)
在可执行文件中嵌入文件夹dist
//go:embed web/dist/*
访问集成文件夹文件系统
var f embed.FS //集成文件集合,取go:embed中文件夹中文件与文件夹列表
变量声明
var (
//整形标志声明,用于储存整形指针
port = flag.Int("p", //标志名
5032, //标志值
"服务运行端口") //标志描述
v = flag.Bool("v", false, "显示版本号")
authInfo = flag.String("a", "", "开启账号密码登录验证, '-a user:pass'的格式传参")
//普通变量声明
timeout int //连接超时
savePass bool //保存密码
version string //版本号
buildDate string //编译时间
goVersion string //go版本号
gitVersion string //git版本号
username string //用户名
password string //密码
)
初始化函数分析
func init() {}
初始化变量为标志
//初始化timeout变量为标志
flag.IntVar(&timeout, //标志指针
"t", //标志名
120, //标志值
"ssh连接超时时间(min)") //标志描述
//初始化savePass变量为标志
flag.BoolVar(&savePass, //标志指针
"s", //标志名
true, //标志值
"保存ssh密码") //标志描述
flag.StringVar(&version,
"ver",
"v1.0.0",
"程序版本号")
flag.StringVar(&goVersion,
"gover",
"v1.22",
"go版本号")
flag.StringVar(&gitVersion,
"gitver",
"2.45.2",
"git版本号")
flag.StringVar(&buildDate,
"d",
time.Now().String(),
"编译日期")
操作环境变量
//查找环境变量savePass
if envVal, ok := os.LookupEnv("savePass"); ok {
//转换环境变量值为Bool值
if b, err := strconv.ParseBool(envVal); err == nil {
savePass = b //如果环境变量有值保存到savePass
}
}
//读取环境变量用户验证信息
if envVal, ok := os.LookupEnv("authInfo"); ok {
*authInfo = envVal
}
//读取环境变量通信端口信息
if envVal, ok := os.LookupEnv("port"); ok {
//转换为整数
if b, err := strconv.Atoi(envVal); err == nil {
*port = b
}
}
//必须在标志定义之后及程序访问之前调用
flag.Parse()
参数检测
//如果有-v参数,显示版本号信息
if *v {
fmt.Printf("Version: %s\n\n", version)
fmt.Printf("BuildDate: %s\n\n", buildDate)
fmt.Printf("GoVersion: %s\n\n", goVersion)
fmt.Printf("GitVersion: %s\n\n", gitVersion)
os.Exit(0)
}
用户名与密码参数分割 (-a user:pass'的格式传参)
if *authInfo != "" {
//分割用户名与密码
accountInfo := strings.Split(*authInfo, ":")
//非空判断
if len(accountInfo) != 2 ||
accountInfo[0] == "" ||
accountInfo[1] == "" {
fmt.Println("请按'user:pass'的格式来传参或设置环境变量, 且账号密码都不能为空!")
os.Exit(0)
}
//保存用户名与密码
username, password = accountInfo[0], accountInfo[1]
}
静态路由函数分析
func staticRouter(router *gin.Engine) {}
创建用户键值对
//创建账户map
accountList := map[string]string{
username: password,
}
授权并写页面
//授权路由
//传入用户列表{用户:密码}
authorized := router.Group("/", gin.BasicAuth(accountList))
authorized.GET("", func(c *gin.Context) {
//读取主页面
indexHTML, _ := f.ReadFile("web/dist/" + "index.html")
//向上下文写入主页面
c.Writer.Write(indexHTML)
})
http操作静态资源
staticFs, _ := fs.Sub(f, "web/dist/static")
router.StaticFS("/static", http.FS(staticFs))
main主函数分析
func main() {}
设置http服务参数并启用路由
//取web引擎实例
server := gin.Default()
//设置可信代理
server.SetTrustedProxies(nil)
//使用压缩中间件,支持资源压缩功能
server.Use(gzip.Gzip(gzip.DefaultCompression))
//启动路由
staticRouter(server)
HTTP服务操作:
//HTTP服务操作
//GET操作,连接终端websocket
server.GET("/term", func(c *gin.Context) {
//调用终端websocket
controller.TermWs(c, time.Duration(timeout)*time.Minute)
})
//GET操作,SSH服务检测
server.GET("/check", func(c *gin.Context) {
//检测SSH服务
responseBody := controller.CheckSSH(c)
//保存连接密码
responseBody.Data = map[string]interface{}{
"savePass": savePass,
}
//渲染JSON数据及HTTP状态码给客户端
c.JSON(200, responseBody)
})
文件资源操作:
//文件资源操作
file := server.Group("/file")
{
//请求文件列表
file.GET("/list", func(c *gin.Context) {
c.JSON(200, controller.FileList(c))
})
//下载文件
file.GET("/download", func(c *gin.Context) {
controller.DownloadFile(c)
})
//上传文件
file.POST("/upload", func(c *gin.Context) {
c.JSON(200, controller.UploadFile(c))
})
//上传/下载进度处理
file.GET("/progress", func(c *gin.Context) {
controller.UploadProgressWs(c)
})
}
启动HTTP服务
//启动HTTP服务
server.Run(fmt.Sprintf(":%d", *port))
启动成功输出 如下日志:
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET / --> main.staticRouter.func2 (4 handlers)
[GIN-debug] GET /static/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (4 handlers)
[GIN-debug] HEAD /static/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (4 handlers)
[GIN-debug] GET /term --> main.main.func1 (4 handlers)
[GIN-debug] GET /check --> main.main.func2 (4 handlers)
[GIN-debug] GET /file/list --> main.main.func3 (4 handlers)
[GIN-debug] GET /file/download --> main.main.func4 (4 handlers)
[GIN-debug] POST /file/upload --> main.main.func5 (4 handlers)
[GIN-debug] GET /file/progress --> main.main.func6 (4 handlers)
[GIN-debug] Listening and serving HTTP on :5032
main.go
package main //主包名
//导入依赖包
import (
"embed" //可执行文件资源嵌入
"flag" //标志变量
"fmt" //格式化
"github.com/gin-contrib/gzip" //压缩库
"github.com/gin-gonic/gin" //网页框架
"io/fs" //文件系统
"net/http" //http通信
"os" //系统信息
"strconv" //字符串转换
"strings" //字符串
"time" //时间
"webssh/controller" //websocket通信
)
// 在可执行文件中嵌入文件夹dist
//
//go:embed web/dist/*
var f embed.FS //集成文件集合,取go:embed中文件夹中文件与文件夹列表
// 变量声明
var (
//整形标志声明,用于储存整形指针
port = flag.Int("p", //标志名
5032, //标志值
"服务运行端口") //标志描述
v = flag.Bool("v", false, "显示版本号")
authInfo = flag.String("a", "", "开启账号密码登录验证, '-a user:pass'的格式传参")
//普通变量声明
timeout int //连接超时
savePass bool //保存密码
version string //版本号
buildDate string //编译时间
goVersion string //go版本号
gitVersion string //git版本号
username string //用户名
password string //密码
)
// 初始化
func init() {
//初始化timeout变量为标志
flag.IntVar(&timeout, //标志指针
"t", //标志名
120, //标志值
"ssh连接超时时间(min)") //标志描述
//初始化savePass变量为标志
flag.BoolVar(&savePass, //标志指针
"s", //标志名
true, //标志值
"保存ssh密码") //标志描述
flag.StringVar(&version,
"ver",
"v1.0.0",
"程序版本号")
flag.StringVar(&goVersion,
"gover",
"v1.22",
"go版本号")
flag.StringVar(&gitVersion,
"gitver",
"2.45.2",
"git版本号")
flag.StringVar(&buildDate,
"d",
time.Now().String(),
"编译日期")
//查找环境变量savePass
if envVal, ok := os.LookupEnv("savePass"); ok {
//转换环境变量值为Bool值
if b, err := strconv.ParseBool(envVal); err == nil {
savePass = b //如果环境变量有值保存到savePass
}
}
//读取环境变量用户验证信息
if envVal, ok := os.LookupEnv("authInfo"); ok {
*authInfo = envVal
}
//读取环境变量通信端口信息
if envVal, ok := os.LookupEnv("port"); ok {
//转换为整数
if b, err := strconv.Atoi(envVal); err == nil {
*port = b
}
}
//必须在标志定义之后及程序访问之前调用
flag.Parse()
//如果有-v参数,显示版本号信息
if *v {
fmt.Printf("Version: %s\n\n", version)
fmt.Printf("BuildDate: %s\n\n", buildDate)
fmt.Printf("GoVersion: %s\n\n", goVersion)
fmt.Printf("GitVersion: %s\n\n", gitVersion)
os.Exit(0)
}
if *authInfo != "" {
//分割用户名与密码
accountInfo := strings.Split(*authInfo, ":")
//非空判断
if len(accountInfo) != 2 ||
accountInfo[0] == "" ||
accountInfo[1] == "" {
fmt.Println("请按'user:pass'的格式来传参或设置环境变量, 且账号密码都不能为空!")
os.Exit(0)
}
//保存用户名与密码
username, password = accountInfo[0], accountInfo[1]
}
}
// 启动静态路由
func staticRouter(router *gin.Engine) {
//如果密码不为空
if password != "" {
//创建账户map
accountList := map[string]string{
username: password,
}
//授权路由
//传入用户列表{用户:密码}
authorized := router.Group("/", gin.BasicAuth(accountList))
authorized.GET("", func(c *gin.Context) {
//读取主页面
indexHTML, _ := f.ReadFile("web/dist/" + "index.html")
//向上下文写入主页面
c.Writer.Write(indexHTML)
})
} else {
router.GET("/", func(c *gin.Context) {
indexHTML, _ := f.ReadFile("web/dist/" + "index.html")
c.Writer.Write(indexHTML)
})
}
//http操作静态资源
staticFs, _ := fs.Sub(f, "web/dist/static")
router.StaticFS("/static", http.FS(staticFs))
}
func main() {
//取web引擎实例
server := gin.Default()
//设置可信代理
server.SetTrustedProxies(nil)
//使用压缩中间件,支持资源压缩功能
server.Use(gzip.Gzip(gzip.DefaultCompression))
//启动路由
staticRouter(server)
//HTTP服务操作
//GET操作,连接终端websocket
server.GET("/term", func(c *gin.Context) {
//调用终端websocket
controller.TermWs(c, time.Duration(timeout)*time.Minute)
})
//GET操作,SSH服务检测
server.GET("/check", func(c *gin.Context) {
//检测SSH服务
responseBody := controller.CheckSSH(c)
//保存连接密码
responseBody.Data = map[string]interface{}{
"savePass": savePass,
}
//渲染JSON数据及HTTP状态码给客户端
c.JSON(200, responseBody)
})
//文件资源操作
file := server.Group("/file")
{
//请求文件列表
file.GET("/list", func(c *gin.Context) {
c.JSON(200, controller.FileList(c))
})
//下载文件
file.GET("/download", func(c *gin.Context) {
controller.DownloadFile(c)
})
//上传文件
file.POST("/upload", func(c *gin.Context) {
c.JSON(200, controller.UploadFile(c))
})
//上传/下载进度处理
file.GET("/progress", func(c *gin.Context) {
controller.UploadProgressWs(c)
})
}
//启动HTTP服务
server.Run(fmt.Sprintf(":%d", *port))
}