Ver Fonte

Merge pull request #24 from jxhczhl/dev

Dev
hliang há 1 ano atrás
pai
commit
fada16f6cf
15 ficheiros alterados com 615 adições e 391 exclusões
  1. 5 2
      README.md
  2. 2 0
      config.yaml
  3. 46 6
      config/config.go
  4. 322 0
      core/api.go
  5. 76 0
      core/engine.go
  6. 33 0
      core/middlewares.go
  7. 28 0
      core/routers.go
  8. 1 3
      go.mod
  9. 2 8
      go.sum
  10. 7 372
      main.go
  11. 8 0
      utils/code.go
  12. 23 0
      utils/file.go
  13. 9 0
      utils/hash.go
  14. 32 0
      utils/logger.go
  15. 21 0
      utils/terminal.go

+ 5 - 2
README.md

@@ -71,6 +71,8 @@
 - `/wst`  :ws测试使用-发啥回啥 (ws | wss)
 - `/go` :获取数据的接口  (get | post)
 - `/execjs` :传递jscode给浏览器执行 (get | post)
+- `/page/cookie` :直接获取当前页面的cookie (get)
+- `/page/html` :获取当前页面的html (get)
 
 说明:接口用?group分组 如 "ws://127.0.0.1:12080/ws?group={}"
 以及可选参数 clientId
@@ -142,7 +144,7 @@ demo.regAction("hello", function (resolve) {
 ```
 
 访问接口,获得js端的返回值  
-http://localhost:12080/go?group=zzz&name=hlg&action=hello
+http://127.0.0.1:12080/go?group=zzz&action=hello
 
 ![image](https://github.com/jxhczhl/JsRpc/assets/41224971/5f0da051-18f3-49ac-98f8-96f408440475)
 
@@ -159,6 +161,7 @@ demo.regAction("hello2", function (resolve,param) {
 ```
 
 访问接口,获得js端的返回值
+http://127.0.0.1:12080/go?group=zzz&action=hello2&param=123456  
 
 ![image](https://github.com/jxhczhl/JsRpc/assets/41224971/91b993ae-7831-4b65-8553-f90e19cc7ebe)
 
@@ -181,7 +184,7 @@ demo.regAction("hello3", function (resolve,param) {
 访问接口,获得js端的返回值
 
 ```python
-url = "http://localhost:12080/go"
+url = "http://127.0.0.1:12080/go"
 data = {
     "group": "zzz",
     "action": "hello3",

+ 2 - 0
config.yaml

@@ -8,3 +8,5 @@ HttpsServices:
 DefaultTimeOut: 30 # 当执行端没有返回值时,等待%d秒返回超时
 CloseLog: false # 关闭一些日志
 CloseWebLog: false # 关闭Web服务访问的日志
+Mode: release  # release:发布版本   debug:调试版   test:测试版本
+Cors: false    # 是否开启CorsMiddleWare中间件--默认不开启

+ 46 - 6
config/config.go

@@ -1,19 +1,57 @@
 package config
 
 import (
+	"JsRpc/utils"
+	"errors"
+	"flag"
+	log "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v3"
 	"os"
 )
 
-func InitConf(path string, conf *ConfStruct) (err error) {
-	fileContent, err := os.ReadFile(path)
+var DefaultTimeout = 30
+
+func ReadConf() ConfStruct {
+	var ConfigPath string
+	// 定义命令行参数-c,后面跟着的是默认值以及参数说明
+	flag.StringVar(&ConfigPath, "c", "config.yaml", "指定配置文件的路径")
+	// 解析命令行参数
+	flag.Parse()
+
+	conf, err := initConf(ConfigPath)
 	if err != nil {
-		return err
+		log.Warning("读取配置文件错误,将使用默认配置运行。 ", err.Error())
+	}
+	return conf
+}
+
+func initConf(path string) (ConfStruct, error) {
+	defaultConf := ConfStruct{
+		BasicListen: `:12080`,
+		HttpsServices: HttpsConfig{
+			IsEnable:    false,
+			HttpsListen: `:12443`,
+		},
+		DefaultTimeOut: DefaultTimeout,
+	}
+	if !utils.IsExists(path) {
+		return defaultConf, errors.New("config path not found")
 	}
-	if err = yaml.Unmarshal(fileContent, &conf); err != nil {
-		return err
+
+	file, _ := os.Open(path) // 因为上面已经判断了 文件是存在的 所以这里不用捕获错误
+	defer func(file *os.File) {
+		err := file.Close()
+		if err != nil {
+		}
+	}(file)
+	conf := ConfStruct{}
+	decoder := yaml.NewDecoder(file)
+	err := decoder.Decode(&conf)
+	if err != nil {
+		return defaultConf, err
 	}
-	return
+	DefaultTimeout = conf.DefaultTimeOut
+	return conf, nil
 }
 
 type ConfStruct struct {
@@ -22,6 +60,8 @@ type ConfStruct struct {
 	DefaultTimeOut int         `yaml:"DefaultTimeOut"`
 	CloseLog       bool        `yaml:"CloseLog"`
 	CloseWebLog    bool        `yaml:"CloseWebLog"`
+	Mode           string      `yaml:"Mode"`
+	Cors           bool        `yaml:"Cors"`
 }
 
 // HttpsConfig 代表HTTPS相关配置的结构体

+ 322 - 0
core/api.go

@@ -0,0 +1,322 @@
+package core
+
+import (
+	"JsRpc/config"
+	"JsRpc/utils"
+	"github.com/gin-gonic/gin"
+	"github.com/gorilla/websocket"
+	log "github.com/sirupsen/logrus"
+	"github.com/unrolled/secure"
+	"net/http"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+var (
+	upGrader = websocket.Upgrader{
+		CheckOrigin: func(r *http.Request) bool { return true },
+	}
+	gm        = &sync.Mutex{}
+	hlSyncMap sync.Map
+)
+
+// Message 请求和传递请求
+type Message struct {
+	Action string `json:"action"`
+	Param  string `json:"param"`
+}
+
+type ApiParam struct {
+	GroupName string `form:"group" json:"group"`
+	ClientId  string `form:"clientId" json:"clientId"`
+	Action    string `form:"action" json:"action"`
+	Param     string `form:"param" json:"param"`
+	Code      string `form:"code" json:"code"` // 直接eval的代码
+}
+
+// Clients 客户端信息
+type Clients struct {
+	clientGroup string
+	clientId    string
+	actionData  map[string]chan string
+	clientWs    *websocket.Conn
+}
+
+// NewClient  initializes a new Clients instance
+func NewClient(group string, uid string, ws *websocket.Conn) *Clients {
+	return &Clients{
+		clientGroup: group,
+		clientId:    uid,
+		actionData:  make(map[string]chan string, 1), // action有消息后就保存到chan里
+		clientWs:    ws,
+	}
+}
+
+func GinJsonMsg(c *gin.Context, code int, msg string) {
+	c.JSON(code, gin.H{"status": code, "data": msg})
+	return
+}
+
+// ws, provides inject function for a job
+func ws(c *gin.Context) {
+	group, clientId := c.Query("group"), c.Query("clientId")
+	//必须要group名字,不然不让它连接ws
+	if group == "" {
+		return
+	}
+	//没有给客户端id的话 就用时间戳给他生成一个
+	if clientId == "" {
+		clientId = utils.GetUUID()
+	}
+	wsClient, err := upGrader.Upgrade(c.Writer, c.Request, nil)
+	if err != nil {
+		log.Error("websocket err:", err)
+		return
+	}
+	client := NewClient(group, clientId, wsClient)
+	hlSyncMap.Store(group+"->"+clientId, client)
+	utils.LogPrint("新上线group:" + group + ",clientId:->" + clientId)
+	for {
+		//等待数据
+		_, message, err := wsClient.ReadMessage()
+		if err != nil {
+			break
+		}
+		msg := string(message)
+		check := []uint8{104, 108, 94, 95, 94}
+		strIndex := strings.Index(msg, string(check))
+		if strIndex >= 1 {
+			action := msg[:strIndex]
+			client.actionData[action] <- msg[strIndex+5:]
+			if len(msg) > 100 {
+				utils.LogPrint("get_message:", msg[strIndex+5:101]+"......")
+			} else {
+				utils.LogPrint("get_message:", msg[strIndex+5:])
+			}
+
+		} else {
+			log.Error(msg, "message error")
+		}
+
+	}
+	defer func(ws *websocket.Conn) {
+		_ = ws.Close()
+		utils.LogPrint(group+"->"+clientId, "下线了")
+		hlSyncMap.Range(func(key, value interface{}) bool {
+			//client, _ := value.(*Clients)
+			if key == group+"->"+clientId {
+				hlSyncMap.Delete(key)
+			}
+			return true
+		})
+	}(wsClient)
+}
+
+func wsTest(c *gin.Context) {
+	testClient, _ := upGrader.Upgrade(c.Writer, c.Request, nil)
+	for {
+		//等待数据
+		_, message, err := testClient.ReadMessage()
+		if err != nil {
+			break
+		}
+		msg := string(message)
+		utils.LogPrint("接收到测试消息", msg)
+		_ = testClient.WriteMessage(websocket.BinaryMessage, []byte(msg))
+	}
+	defer func(ws *websocket.Conn) {
+		_ = ws.Close()
+	}(testClient)
+}
+
+func GetCookie(c *gin.Context) {
+	var RequestParam ApiParam
+	if err := c.ShouldBind(&RequestParam); err != nil {
+		GinJsonMsg(c, http.StatusBadRequest, err.Error())
+		return
+	}
+	group := c.Query("group")
+	if group == "" {
+		GinJsonMsg(c, http.StatusBadRequest, "需要传入group")
+		return
+	}
+
+	clientId := RequestParam.ClientId
+	client := getRandomClient(group, clientId)
+	if client == nil {
+		GinJsonMsg(c, http.StatusBadRequest, "没有找到对应的group或clientId,请通过list接口查看现有的注入")
+		return
+	}
+
+	c3 := make(chan string, 1)
+	go client.GQueryFunc("_execjs", utils.ConcatCode("document.cookie"), c3)
+	c.JSON(http.StatusOK, gin.H{"status": 200, "group": client.clientGroup, "clientId": client.clientId, "data": <-c3})
+}
+
+func GetHtml(c *gin.Context) {
+	var RequestParam ApiParam
+	if err := c.ShouldBind(&RequestParam); err != nil {
+		GinJsonMsg(c, http.StatusBadRequest, err.Error())
+		return
+	}
+	group := c.Query("group")
+	if group == "" {
+		GinJsonMsg(c, http.StatusBadRequest, "需要传入group")
+		return
+	}
+
+	clientId := RequestParam.ClientId
+	client := getRandomClient(group, clientId)
+	if client == nil {
+		GinJsonMsg(c, http.StatusBadRequest, "没有找到对应的group或clientId,请通过list接口查看现有的注入")
+		return
+	}
+
+	c3 := make(chan string, 1)
+	go client.GQueryFunc("_execjs", utils.ConcatCode("document.documentElement.outerHTML"), c3)
+	c.JSON(http.StatusOK, gin.H{"status": 200, "group": client.clientGroup, "clientId": client.clientId, "data": <-c3})
+}
+
+// GetResult 接收web请求参数,并发给客户端获取结果
+func getResult(c *gin.Context) {
+	var RequestParam ApiParam
+	if err := c.ShouldBind(&RequestParam); err != nil {
+		GinJsonMsg(c, http.StatusBadRequest, err.Error())
+		return
+	}
+
+	group := RequestParam.GroupName
+	if group == "" {
+		GinJsonMsg(c, http.StatusBadRequest, "需要传入group")
+		return
+	}
+	action := RequestParam.Action
+	if action == "" {
+		GinJsonMsg(c, http.StatusOK, "请传入action来调用客户端方法")
+		return
+	}
+	clientId := RequestParam.ClientId
+	client := getRandomClient(group, clientId)
+	if client == nil {
+		GinJsonMsg(c, http.StatusBadRequest, "没有找到对应的group或clientId,请通过list接口查看现有的注入")
+		return
+	}
+	c2 := make(chan string, 1)
+	go client.GQueryFunc(action, RequestParam.Param, c2)
+	//把管道传过去,获得值就返回了
+	c.JSON(http.StatusOK, gin.H{"status": 200, "group": client.clientGroup, "clientId": client.clientId, "data": <-c2})
+
+}
+
+func execjs(c *gin.Context) {
+	var RequestParam ApiParam
+	if err := c.ShouldBind(&RequestParam); err != nil {
+		GinJsonMsg(c, http.StatusBadRequest, err.Error())
+		return
+	}
+	Action := "_execjs"
+	//获取参数
+	group := RequestParam.GroupName
+	if group == "" {
+		GinJsonMsg(c, http.StatusBadRequest, "需要传入group")
+		return
+	}
+	JsCode := RequestParam.Code
+	if JsCode == "" {
+		GinJsonMsg(c, http.StatusBadRequest, "请传入代码")
+		return
+	}
+	clientId := RequestParam.ClientId
+	client := getRandomClient(group, clientId)
+	if client == nil {
+		GinJsonMsg(c, http.StatusBadRequest, "没有找到对应的group或clientId,请通过list接口查看现有的注入")
+		return
+	}
+	c2 := make(chan string)
+	go client.GQueryFunc(Action, JsCode, c2)
+	c.JSON(200, gin.H{"status": "200", "group": client.clientGroup, "name": client.clientId, "data": <-c2})
+
+}
+
+func getList(c *gin.Context) {
+	var data = make(map[string][]string)
+	hlSyncMap.Range(func(_, value interface{}) bool {
+		client, ok := value.(*Clients)
+		if !ok {
+			return true // 继续遍历
+		}
+		group := client.clientGroup
+		data[group] = append(data[group], client.clientId)
+		return true
+	})
+	c.JSON(http.StatusOK, gin.H{"status": 200, "data": data})
+}
+
+func index(c *gin.Context) {
+	c.String(200, "你好,我是黑脸怪~")
+}
+
+func tlsHandler(HttpsHost string) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		secureMiddleware := secure.New(secure.Options{
+			SSLRedirect: true,
+			SSLHost:     HttpsHost,
+		})
+		err := secureMiddleware.Process(c.Writer, c.Request)
+		if err != nil {
+			c.Abort()
+			return
+		}
+		c.Next()
+	}
+}
+
+func getGinMode(mode string) string {
+	switch mode {
+	case "release":
+		return gin.ReleaseMode
+	case "debug":
+		return gin.DebugMode
+	case "test":
+		return gin.TestMode
+	}
+	return gin.ReleaseMode // 默认就是release模式
+}
+
+func setupRouters(conf config.ConfStruct) *gin.Engine {
+	router := gin.Default()
+	if conf.Cors { // 是否开启cors中间件
+		router.Use(CorsMiddleWare())
+	}
+	return router
+}
+
+func InitAPI(conf config.ConfStruct) {
+	if conf.CloseWebLog {
+		// 将默认的日志输出器设置为空
+		gin.DefaultWriter = utils.LogWriter{}
+	}
+	gin.SetMode(getGinMode(conf.Mode))
+	router := setupRouters(conf)
+
+	setJsRpcRouters(router) // 核心路由
+
+	var sb strings.Builder
+	sb.WriteString("当前监听地址:")
+	sb.WriteString(conf.BasicListen)
+
+	sb.WriteString(" ssl启用状态:")
+	sb.WriteString(strconv.FormatBool(conf.HttpsServices.IsEnable))
+
+	if conf.HttpsServices.IsEnable {
+		sb.WriteString(" https监听地址:")
+		sb.WriteString(conf.HttpsServices.HttpsListen)
+	}
+	log.Infoln(sb.String())
+
+	err := router.Run(conf.BasicListen)
+	if err != nil {
+		log.Errorln("服务启动失败..")
+	}
+}

+ 76 - 0
core/engine.go

@@ -0,0 +1,76 @@
+package core
+
+import (
+	"JsRpc/config"
+	"encoding/json"
+	"fmt"
+	"math/rand"
+	"time"
+)
+
+// GQueryFunc 发送请求到客户端
+func (c *Clients) GQueryFunc(funcName string, param string, resChan chan<- string) {
+	WriteData := Message{Param: param, Action: funcName}
+	data, _ := json.Marshal(WriteData)
+	clientWs := c.clientWs
+	if c.actionData[funcName] == nil {
+		c.actionData[funcName] = make(chan string, 1) //此次action初始化1个消息
+	}
+	gm.Lock()
+	err := clientWs.WriteMessage(1, data)
+	gm.Unlock()
+	if err != nil {
+		fmt.Println(err, "写入数据失败")
+	}
+	resultFlag := false
+	for i := 0; i < config.DefaultTimeout*10; i++ {
+		if len(c.actionData[funcName]) > 0 {
+			res := <-c.actionData[funcName]
+			resChan <- res
+			resultFlag = true
+			break
+		}
+		time.Sleep(time.Millisecond * 100)
+	}
+	// 循环完了还是没有数据,那就超时退出
+	if true != resultFlag {
+		resChan <- "黑脸怪:timeout"
+	}
+	defer func() {
+		close(resChan)
+	}()
+}
+
+func getRandomClient(group string, clientId string) *Clients {
+	var client *Clients
+	// 不传递clientId时候,从group分组随便拿一个
+	if clientId != "" {
+		clientName, ok := hlSyncMap.Load(group + "->" + clientId)
+		if ok == false {
+			return nil
+		}
+		client, _ = clientName.(*Clients)
+		return client
+	}
+	groupClients := make([]*Clients, 0)
+	//循环读取syncMap 获取group名字的
+	hlSyncMap.Range(func(_, value interface{}) bool {
+		tmpClients, ok := value.(*Clients)
+		if !ok {
+			return true
+		}
+		if tmpClients.clientGroup == group {
+			groupClients = append(groupClients, tmpClients)
+		}
+		return true
+	})
+	if len(groupClients) == 0 {
+		return nil
+	}
+	// 使用随机数发生器
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	randomIndex := r.Intn(len(groupClients))
+	client = groupClients[randomIndex]
+	return client
+
+}

+ 33 - 0
core/middlewares.go

@@ -0,0 +1,33 @@
+package core
+
+import "github.com/gin-gonic/gin"
+
+func CorsMiddleWare() gin.HandlerFunc {
+	return func(context *gin.Context) {
+		method := context.Request.Method
+		origin := context.Request.Header.Get("Origin") //请求头部
+		if origin != "" {
+			//接收客户端发送的origin (重要!)
+			context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
+
+			//服务器支持的所有跨域请求的方法
+			context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
+			//允许跨域设置可以返回其他子段,可以自定义字段
+			context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session")
+			// 允许浏览器(客户端)可以解析的头部 (重要)
+			context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
+			//设置缓存时间
+			//c.Header("Access-Control-Max-Age", "172800")
+			//允许客户端传递校验信息比如 cookie (重要)
+			context.Header("Access-Control-Allow-Credentials", "true")
+		}
+
+		//允许类型校验
+		if method == "OPTIONS" {
+			context.AbortWithStatus(200)
+		} else {
+			context.Next()
+		}
+	}
+
+}

+ 28 - 0
core/routers.go

@@ -0,0 +1,28 @@
+package core
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+func setJsRpcRouters(router *gin.Engine) {
+	// 核心部分的的路由组
+	router.GET("/", index)
+
+	page := router.Group("/page")
+	{
+		page.GET("/cookie", GetCookie)
+		page.GET("/html", GetHtml)
+	}
+
+	rpc := router.Group("/")
+	{
+		rpc.GET("go", getResult)
+		rpc.POST("go", getResult)
+		rpc.GET("ws", ws)
+		rpc.GET("wst", wsTest)
+		rpc.GET("execjs", execjs)
+		rpc.POST("execjs", execjs)
+		rpc.GET("list", getList)
+	}
+
+}

+ 1 - 3
go.mod

@@ -7,24 +7,22 @@ require (
 	github.com/gorilla/websocket v1.5.1
 	github.com/sirupsen/logrus v1.9.3
 	github.com/unrolled/secure v1.14.0
-	github.com/weberhong/figlet4go v0.0.0-20160909034824-bc879344e874
 	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
 	github.com/bytedance/sonic v1.9.1 // indirect
 	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
-	github.com/fatih/color v1.16.0 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/validator/v10 v10.14.0 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
 	github.com/leodido/go-urn v1.2.4 // indirect
-	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect

+ 2 - 8
go.sum

@@ -7,8 +7,6 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
 github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -29,6 +27,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -38,9 +38,6 @@ github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZX
 github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
 github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -71,8 +68,6 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
 github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/unrolled/secure v1.14.0 h1:u9vJTU/pR4Bny0ntLUMxdfLtmIRGvQf2sEFuA0TG9AE=
 github.com/unrolled/secure v1.14.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
-github.com/weberhong/figlet4go v0.0.0-20160909034824-bc879344e874 h1:pKoR9yFIrYsX/dvBF5FDjlmHrSAVJp9y5pDWauJn4uI=
-github.com/weberhong/figlet4go v0.0.0-20160909034824-bc879344e874/go.mod h1:so6/8uteu8X0r6SpBU9CyyjXQAXt2Zh6tkLE/l2QIKQ=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
 golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -82,7 +77,6 @@ golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
 golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

+ 7 - 372
main.go

@@ -2,381 +2,16 @@ package main
 
 import (
 	"JsRpc/config"
-	"encoding/json"
-	"flag"
-	"fmt"
-	"github.com/gin-gonic/gin"
-	"github.com/gorilla/websocket"
-	log "github.com/sirupsen/logrus"
-	"github.com/unrolled/secure"
-	"math/rand"
-	"net/http"
-	"strconv"
-	"strings"
-	"sync"
-	"time"
+	"JsRpc/core"
+	"JsRpc/utils"
 )
 
-var (
-	upGrader = websocket.Upgrader{
-		CheckOrigin: func(r *http.Request) bool { return true },
-	}
-	hlSyncMap      sync.Map
-	gm             = &sync.Mutex{}
-	defaultTimeout = 30
-	isPrint        = true
-)
-
-// Clients 客户端信息
-type Clients struct {
-	clientGroup string
-	clientId    string
-	actionData  map[string]chan string
-	clientWs    *websocket.Conn
-}
-
-// Message 请求和传递请求
-type Message struct {
-	Action string `json:"action"`
-	Param  string `json:"param"`
-}
-
-type ApiParam struct {
-	GroupName string `form:"group" json:"group"`
-	ClientId  string `form:"clientId" json:"clientId"`
-	Action    string `form:"action" json:"action"`
-	Param     string `form:"param" json:"param"`
-	Code      string `form:"code" json:"code"` // 直接eval的代码
-}
-
-type logWriter struct{}
-
-func (w logWriter) Write(p []byte) (n int, err error) {
-	return len(p), nil
-}
-
-// is print?
-func logPrint(p ...interface{}) {
-	if isPrint {
-		log.Infoln(p)
-	}
-}
-
-// NewClient  initializes a new Clients instance
-func NewClient(group string, uid string, ws *websocket.Conn) *Clients {
-	return &Clients{
-		clientGroup: group,
-		clientId:    uid,
-		actionData:  make(map[string]chan string, 1), // action有消息后就保存到chan里
-		clientWs:    ws,
-	}
-}
-func GinJsonMsg(c *gin.Context, code int, msg string) {
-	c.JSON(code, gin.H{"status": code, "data": msg})
-	return
-}
-
-// ws, provides inject function for a job
-func ws(c *gin.Context) {
-	group, clientId := c.Query("group"), c.Query("clientId")
-	//必须要group名字,不然不让它连接ws
-	if group == "" {
-		return
-	}
-	//没有给客户端id的话 就用时间戳给他生成一个
-	if clientId == "" {
-		millisId := time.Now().UnixNano() / int64(time.Millisecond)
-		clientId = fmt.Sprintf("%d", millisId)
-	}
-	wsClient, err := upGrader.Upgrade(c.Writer, c.Request, nil)
-	if err != nil {
-		log.Error("websocket err:", err)
-		return
-	}
-	client := NewClient(group, clientId, wsClient)
-	hlSyncMap.Store(group+"->"+clientId, client)
-	logPrint("新上线group:" + group + ",clientId:->" + clientId)
-	for {
-		//等待数据
-		_, message, err := wsClient.ReadMessage()
-		if err != nil {
-			break
-		}
-		msg := string(message)
-		check := []uint8{104, 108, 94, 95, 94}
-		strIndex := strings.Index(msg, string(check))
-		if strIndex >= 1 {
-			action := msg[:strIndex]
-			client.actionData[action] <- msg[strIndex+5:]
-			logPrint("get_message:", msg[strIndex+5:])
-		} else {
-			log.Error(msg, "message error")
-		}
-
-	}
-	defer func(ws *websocket.Conn) {
-		_ = ws.Close()
-		logPrint(group+"->"+clientId, "下线了")
-		hlSyncMap.Range(func(key, value interface{}) bool {
-			//client, _ := value.(*Clients)
-			if key == group+"->"+clientId {
-				hlSyncMap.Delete(key)
-			}
-			return true
-		})
-	}(wsClient)
-}
-
-func wsTest(c *gin.Context) {
-	testClient, _ := upGrader.Upgrade(c.Writer, c.Request, nil)
-	for {
-		//等待数据
-		_, message, err := testClient.ReadMessage()
-		if err != nil {
-			break
-		}
-		msg := string(message)
-		logPrint("接收到测试消息", msg)
-		_ = testClient.WriteMessage(websocket.BinaryMessage, []byte(msg))
-	}
-	defer func(ws *websocket.Conn) {
-		_ = ws.Close()
-	}(testClient)
-}
-
-// GQueryFunc 发送请求到客户端
-func GQueryFunc(client *Clients, funcName string, param string, resChan chan<- string) {
-	WriteDate := Message{Param: param, Action: funcName}
-	data, _ := json.Marshal(WriteDate)
-	clientWs := client.clientWs
-	if client.actionData[funcName] == nil {
-		client.actionData[funcName] = make(chan string, 1) //此次action初始化1个消息
-	}
-	gm.Lock()
-	err := clientWs.WriteMessage(1, data)
-	gm.Unlock()
-	if err != nil {
-		fmt.Println(err, "写入数据失败")
-	}
-	resultFlag := false
-	for i := 0; i < defaultTimeout*10; i++ {
-		if len(client.actionData[funcName]) > 0 {
-			res := <-client.actionData[funcName]
-			resChan <- res
-			resultFlag = true
-			break
-		}
-		time.Sleep(time.Millisecond * 100)
-	}
-	// 循环完了还是没有数据,那就超时退出
-	if true != resultFlag {
-		resChan <- "黑脸怪:timeout"
-	}
-	defer func() {
-		close(resChan)
-	}()
-}
-
-// GetResult 接收web请求参数,并发给客户端获取结果
-func GetResult(c *gin.Context) {
-	var RequestParam ApiParam
-	if err := c.ShouldBind(&RequestParam); err != nil {
-		GinJsonMsg(c, http.StatusBadRequest, err.Error())
-		return
-	}
-
-	group := RequestParam.GroupName
-	if group == "" {
-		GinJsonMsg(c, http.StatusBadRequest, "需要传入group")
-		return
-	}
-	action := RequestParam.Action
-	if action == "" {
-		GinJsonMsg(c, http.StatusOK, "请传入action来调用客户端方法")
-		return
-	}
-	clientId := RequestParam.ClientId
-	client := GetRandomClient(group, clientId)
-	if client == nil {
-		GinJsonMsg(c, http.StatusBadRequest, "没有找到对应的group或clientId,请通过list接口查看现有的注入")
-		return
-	}
-	c2 := make(chan string, 1)
-	go GQueryFunc(client, action, RequestParam.Param, c2)
-	//把管道传过去,获得值就返回了
-	c.JSON(http.StatusOK, gin.H{"status": 200, "group": client.clientGroup, "clientId": client.clientId, "data": <-c2})
-
-}
-
-func GetRandomClient(group string, clientId string) *Clients {
-	var client *Clients
-	// 不传递clientId时候,从group分组随便拿一个
-	if clientId != "" {
-		clientName, ok := hlSyncMap.Load(group + "->" + clientId)
-		if ok == false {
-			return nil
-		}
-		client, _ = clientName.(*Clients)
-		return client
-	}
-	groupClients := make([]*Clients, 0)
-	//循环读取syncMap 获取group名字的
-	hlSyncMap.Range(func(_, value interface{}) bool {
-		tmpClients, ok := value.(*Clients)
-		if !ok {
-			return true
-		}
-		if tmpClients.clientGroup == group {
-			groupClients = append(groupClients, tmpClients)
-		}
-		return true
-	})
-	if len(groupClients) == 0 {
-		return nil
-	}
-	// 使用随机数发生器
-	r := rand.New(rand.NewSource(time.Now().UnixNano()))
-	randomIndex := r.Intn(len(groupClients))
-	client = groupClients[randomIndex]
-	return client
-
-}
-
-func Execjs(c *gin.Context) {
-	var RequestParam ApiParam
-	if err := c.ShouldBind(&RequestParam); err != nil {
-		GinJsonMsg(c, http.StatusBadRequest, err.Error())
-		return
-	}
-	Action := "_execjs"
-	//获取参数
-	group := RequestParam.GroupName
-	if group == "" {
-		GinJsonMsg(c, http.StatusBadRequest, "需要传入group")
-		return
-	}
-	JsCode := RequestParam.Code
-	if JsCode == "" {
-		GinJsonMsg(c, http.StatusBadRequest, "请传入代码")
-		return
-	}
-	clientId := RequestParam.ClientId
-	client := GetRandomClient(group, clientId)
-	if client == nil {
-		GinJsonMsg(c, http.StatusBadRequest, "没有找到对应的group或clientId,请通过list接口查看现有的注入")
-		return
-	}
-	c2 := make(chan string)
-	go GQueryFunc(client, Action, JsCode, c2)
-	c.JSON(200, gin.H{"status": "200", "group": client.clientGroup, "name": client.clientId, "data": <-c2})
-
-}
-
-func GetList(c *gin.Context) {
-	var data = make(map[string][]string)
-	hlSyncMap.Range(func(_, value interface{}) bool {
-		client, ok := value.(*Clients)
-		if !ok {
-			return true // 继续遍历
-		}
-		group := client.clientGroup
-		data[group] = append(data[group], client.clientId)
-		return true
-	})
-	c.JSON(http.StatusOK, gin.H{"status": 200, "data": data})
-}
-
-func Index(c *gin.Context) {
-	c.String(200, "你好,我是黑脸怪~")
-}
-
-func TlsHandler(HttpsHost string) gin.HandlerFunc {
-	return func(c *gin.Context) {
-		secureMiddleware := secure.New(secure.Options{
-			SSLRedirect: true,
-			SSLHost:     HttpsHost,
-		})
-		err := secureMiddleware.Process(c.Writer, c.Request)
-		if err != nil {
-			c.Abort()
-			return
-		}
-		c.Next()
-	}
-}
 func main() {
-	JsRpc := "       __       _______..______      .______     ______ \n      |  |     /       ||   _  \\     |   _  \\   /      |\n      |  |    |   (----`|  |_)  |    |  |_)  | |  ,----'\n.--.  |  |     \\   \\    |      /     |   ___/  |  |     \n|  `--'  | .----)   |   |  |\\  \\----.|  |      |  `----.\n \\______/  |_______/    | _| `._____|| _|       \\______|\n                                                        \n"
-	fmt.Print(JsRpc)
-	log.SetFormatter(&log.TextFormatter{
-		ForceColors:     true, // 强制终端输出带颜色日志
-		FullTimestamp:   true, // 显示完整时间戳
-		TimestampFormat: "2006-01-02 15:04:05",
-	})
-	var ConfigPath string
-	// 定义命令行参数-c,后面跟着的是默认值以及参数说明
-	flag.StringVar(&ConfigPath, "c", "config.yaml", "指定配置文件的路径")
-	// 解析命令行参数
-	flag.Parse()
-	MainConf := config.ConfStruct{
-		BasicListen: `:12080`,
-		HttpsServices: config.HttpsConfig{
-			IsEnable:    false,
-			HttpsListen: `:12443`,
-		},
-		DefaultTimeOut: defaultTimeout,
-	}
-	err := config.InitConf(ConfigPath, &MainConf)
-	if err != nil {
-		log.Error("读取配置文件错误,将使用默认配置运行。 ", err.Error())
-	}
-	if MainConf.CloseWebLog {
-		// 将默认的日志输出器设置为空
-		gin.DefaultWriter = logWriter{}
-	}
-	if MainConf.CloseLog {
-		isPrint = false
-	}
-	defaultTimeout = MainConf.DefaultTimeOut
-
-	gin.SetMode(gin.ReleaseMode)
-	r := gin.Default()
-	r.GET("/", Index)
-	r.GET("/go", GetResult)
-	r.POST("/go", GetResult)
-	r.GET("/ws", ws)
-	r.GET("/wst", wsTest)
-	r.GET("/execjs", Execjs)
-	r.POST("/execjs", Execjs)
-	r.GET("/list", GetList)
-	if MainConf.HttpsServices.IsEnable {
-		r.Use(TlsHandler(MainConf.HttpsServices.HttpsListen))
-		go func() {
-			err := r.RunTLS(
-				MainConf.HttpsServices.HttpsListen,
-				MainConf.HttpsServices.PemPath,
-				MainConf.HttpsServices.KeyPath,
-			)
-			if err != nil {
-				log.Error(err)
-			}
-		}()
-
-	}
-	var sb strings.Builder
-	sb.WriteString("当前监听地址:")
-	sb.WriteString(MainConf.BasicListen)
-
-	sb.WriteString(" ssl启用状态:")
-	sb.WriteString(strconv.FormatBool(MainConf.HttpsServices.IsEnable))
+	utils.PrintJsRpc() // 开屏打印
 
-	if MainConf.HttpsServices.IsEnable {
-		sb.WriteString(" https监听地址:")
-		sb.WriteString(MainConf.HttpsServices.HttpsListen)
-	}
-	log.Infoln(sb.String())
+	baseConf := config.ReadConf()       // 读取日志信息
+	utils.InitLogger(baseConf.CloseLog) // 初始化日志
+	core.InitAPI(baseConf)              // 初始化api部分
 
-	err = r.Run(MainConf.BasicListen)
-	if err != nil {
-		log.Error(err)
-	}
+	utils.CloseTerminal() // 安全退出
 }

+ 8 - 0
utils/code.go

@@ -0,0 +1,8 @@
+package utils
+
+import "fmt"
+
+func ConcatCode(code string) string {
+	// 拼接页面元素的js
+	return fmt.Sprintf("(function(){return %s;})()", code)
+}

+ 23 - 0
utils/file.go

@@ -0,0 +1,23 @@
+package utils
+
+import (
+	"fmt"
+	"os"
+)
+
+func IsExists(root string) bool {
+	// 判断文件或者文件夹是否存在
+	_, err := os.Stat(root)
+	if err == nil {
+		return true
+	}
+	if os.IsNotExist(err) {
+		return false
+	}
+	return false
+}
+
+func PrintJsRpc() {
+	JsRpc := "       __       _______..______      .______     ______ \n      |  |     /       ||   _  \\     |   _  \\   /      |\n      |  |    |   (----`|  |_)  |    |  |_)  | |  ,----'\n.--.  |  |     \\   \\    |      /     |   ___/  |  |     \n|  `--'  | .----)   |   |  |\\  \\----.|  |      |  `----.\n \\______/  |_______/    | _| `._____|| _|       \\______|\n                                                        \n"
+	fmt.Print(JsRpc)
+}

+ 9 - 0
utils/hash.go

@@ -0,0 +1,9 @@
+package utils
+
+import "github.com/google/uuid"
+
+func GetUUID() string {
+	u := uuid.New()
+	key := u.String()
+	return key
+}

+ 32 - 0
utils/logger.go

@@ -0,0 +1,32 @@
+package utils
+
+import (
+	log "github.com/sirupsen/logrus"
+)
+
+var isPrint = true
+
+func InitLogger(closeLog bool) {
+
+	if closeLog {
+		isPrint = false
+	}
+	log.SetFormatter(&log.TextFormatter{
+		ForceColors:     true, // 强制终端输出带颜色日志
+		FullTimestamp:   true, // 显示完整时间戳
+		TimestampFormat: "2006-01-02 15:04:05",
+		DisableQuote:    true,
+	})
+}
+
+type LogWriter struct{}
+
+func (w LogWriter) Write(p []byte) (n int, err error) {
+	return len(p), nil
+}
+
+func LogPrint(p ...interface{}) {
+	if isPrint {
+		log.Infoln(p)
+	}
+}

+ 21 - 0
utils/terminal.go

@@ -0,0 +1,21 @@
+package utils
+
+import (
+	"context"
+	log "github.com/sirupsen/logrus"
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+)
+
+func CloseTerminal() {
+	// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
+	quit := make(chan os.Signal)
+	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+	<-quit
+	_, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	log.Println("- EXIT - [Ctrl+C] The project will automatically close after 3 seconds")
+	time.Sleep(time.Second * 3)
+}