mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	feat: 注册短信验证码验证功能已经开启
This commit is contained in:
		@@ -20,7 +20,7 @@ import (
 | 
			
		||||
 | 
			
		||||
type AppServer struct {
 | 
			
		||||
	Debug        bool
 | 
			
		||||
	AppConfig    *types.AppConfig
 | 
			
		||||
	Config       *types.AppConfig
 | 
			
		||||
	Engine       *gin.Engine
 | 
			
		||||
	ChatContexts *types.LMap[string, []types.Message] // 聊天上下文 Map [chatId] => []Message
 | 
			
		||||
	ChatConfig   *types.ChatConfig                    // 聊天配置
 | 
			
		||||
@@ -37,7 +37,7 @@ func NewServer(appConfig *types.AppConfig) *AppServer {
 | 
			
		||||
	gin.DefaultWriter = io.Discard
 | 
			
		||||
	return &AppServer{
 | 
			
		||||
		Debug:         false,
 | 
			
		||||
		AppConfig:     appConfig,
 | 
			
		||||
		Config:        appConfig,
 | 
			
		||||
		Engine:        gin.Default(),
 | 
			
		||||
		ChatContexts:  types.NewLMap[string, []types.Message](),
 | 
			
		||||
		ChatSession:   types.NewLMap[string, types.ChatSession](),
 | 
			
		||||
@@ -53,11 +53,11 @@ func (s *AppServer) Init(debug bool) {
 | 
			
		||||
		s.Engine.Use(corsMiddleware())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.Engine.Use(sessionMiddleware(s.AppConfig))
 | 
			
		||||
	s.Engine.Use(sessionMiddleware(s.Config))
 | 
			
		||||
	s.Engine.Use(authorizeMiddleware(s))
 | 
			
		||||
	s.Engine.Use(errorHandler)
 | 
			
		||||
	// 添加静态资源访问
 | 
			
		||||
	s.Engine.Static("/static", s.AppConfig.StaticDir)
 | 
			
		||||
	s.Engine.Static("/static", s.Config.StaticDir)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *AppServer) Run(db *gorm.DB) error {
 | 
			
		||||
@@ -71,15 +71,15 @@ func (s *AppServer) Run(db *gorm.DB) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("http://%s", s.AppConfig.Listen)
 | 
			
		||||
	return s.Engine.Run(s.AppConfig.Listen)
 | 
			
		||||
	logger.Infof("http://%s", s.Config.Listen)
 | 
			
		||||
	return s.Engine.Run(s.Config.Listen)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 全局异常处理
 | 
			
		||||
func errorHandler(c *gin.Context) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			logger.Error("Handler Panic: %v\n", r)
 | 
			
		||||
			logger.Errorf("Handler Panic: %v", r)
 | 
			
		||||
			debug.PrintStack()
 | 
			
		||||
			c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: types.ErrorMsg})
 | 
			
		||||
			c.Abort()
 | 
			
		||||
@@ -164,6 +164,7 @@ func authorizeMiddleware(s *AppServer) gin.HandlerFunc {
 | 
			
		||||
		if c.Request.URL.Path == "/api/user/login" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/admin/login" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/user/register" ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/api/verify/") ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/static/") ||
 | 
			
		||||
			c.Request.URL.Path == "/api/admin/config/get" {
 | 
			
		||||
			c.Next()
 | 
			
		||||
 
 | 
			
		||||
@@ -15,13 +15,13 @@ var logger = logger2.GetLogger()
 | 
			
		||||
 | 
			
		||||
func NewDefaultConfig() *types.AppConfig {
 | 
			
		||||
	return &types.AppConfig{
 | 
			
		||||
		Listen:    "0.0.0.0:5678",
 | 
			
		||||
		ProxyURL:  "",
 | 
			
		||||
		Manager:   types.Manager{Username: "admin", Password: "admin123"},
 | 
			
		||||
		StaticDir: "./static",
 | 
			
		||||
		StaticUrl: "http://localhost/5678/static",
 | 
			
		||||
		Redis:     types.RedisConfig{Host: "localhost", Port: 6379, Password: ""},
 | 
			
		||||
 | 
			
		||||
		Listen:        "0.0.0.0:5678",
 | 
			
		||||
		ProxyURL:      "",
 | 
			
		||||
		Manager:       types.Manager{Username: "admin", Password: "admin123"},
 | 
			
		||||
		StaticDir:     "./static",
 | 
			
		||||
		StaticUrl:     "http://localhost/5678/static",
 | 
			
		||||
		Redis:         types.RedisConfig{Host: "localhost", Port: 6379, Password: ""},
 | 
			
		||||
		AesEncryptKey: utils.RandString(24),
 | 
			
		||||
		Session: types.Session{
 | 
			
		||||
			Driver:    types.SessionDriverCookie,
 | 
			
		||||
			SecretKey: utils.RandString(64),
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,16 @@ type AppConfig struct {
 | 
			
		||||
	StaticDir string      // 静态资源目录
 | 
			
		||||
	StaticUrl string      // 静态资源 URL
 | 
			
		||||
	Redis     RedisConfig // redis 连接信息
 | 
			
		||||
 | 
			
		||||
	AesEncryptKey string
 | 
			
		||||
	SmsConfig     AliYunSmsConfig // 短信发送配置
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AliYunSmsConfig struct {
 | 
			
		||||
	AccessKey    string
 | 
			
		||||
	AccessSecret string
 | 
			
		||||
	Product      string
 | 
			
		||||
	Domain       string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RedisConfig struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ go 1.19
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/BurntSushi/toml v1.1.0
 | 
			
		||||
	github.com/aliyun/alibaba-cloud-sdk-go v1.62.405
 | 
			
		||||
	github.com/gin-contrib/sessions v0.0.5
 | 
			
		||||
	github.com/gin-gonic/gin v1.9.0
 | 
			
		||||
	github.com/gorilla/websocket v1.5.0
 | 
			
		||||
@@ -24,7 +25,9 @@ require (
 | 
			
		||||
	github.com/gomodule/redigo v2.0.0+incompatible // indirect
 | 
			
		||||
	github.com/jinzhu/inflection v1.0.0 // indirect
 | 
			
		||||
	github.com/jinzhu/now v1.1.5 // indirect
 | 
			
		||||
	github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
 | 
			
		||||
	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
 | 
			
		||||
	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 | 
			
		||||
	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
 | 
			
		||||
	github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
 | 
			
		||||
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 | 
			
		||||
@@ -33,6 +36,7 @@ require (
 | 
			
		||||
	golang.org/x/net v0.7.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.7.0 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.28.1 // indirect
 | 
			
		||||
	gopkg.in/ini.v1 v1.66.2 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -48,10 +52,10 @@ require (
 | 
			
		||||
	github.com/json-iterator/go v1.1.12 // indirect
 | 
			
		||||
	github.com/leodido/go-urn v1.2.1 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.17 // indirect
 | 
			
		||||
	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
 | 
			
		||||
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
			
		||||
	github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
			
		||||
	github.com/ugorji/go/codec v1.2.9 // indirect
 | 
			
		||||
	go.uber.org/atomic v1.7.0 // indirect
 | 
			
		||||
	go.uber.org/atomic v1.9.0 // indirect
 | 
			
		||||
	go.uber.org/fx v1.19.3
 | 
			
		||||
	go.uber.org/multierr v1.6.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.6.0
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
 | 
			
		||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
			
		||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.405 h1:cKNFQmeCQFN0WNfjScKoVrGi7vXxTVbkCvCqSrOf+P4=
 | 
			
		||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.405/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
 | 
			
		||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
 | 
			
		||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
 | 
			
		||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
 | 
			
		||||
@@ -32,6 +34,7 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ
 | 
			
		||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 | 
			
		||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
 | 
			
		||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 | 
			
		||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
@@ -58,11 +61,17 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
 | 
			
		||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
			
		||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
			
		||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 | 
			
		||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
			
		||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
			
		||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
 | 
			
		||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
 | 
			
		||||
@@ -70,8 +79,10 @@ github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259
 | 
			
		||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259ae0/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
@@ -79,9 +90,12 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
 | 
			
		||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
 | 
			
		||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
 | 
			
		||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
 | 
			
		||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
 | 
			
		||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
			
		||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkoukk/tiktoken-go v0.1.1-0.20230418101013-cae809389480 h1:IFhPCcB0/HtnEN+ZoUGDT55YgFCymbFJ15kXqs3nv5w=
 | 
			
		||||
github.com/pkoukk/tiktoken-go v0.1.1-0.20230418101013-cae809389480/go.mod h1:BijIqAP84FMYC4XbdJgjyMpiSjusU8x0Y0W9K2t0QtU=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
@@ -103,10 +117,15 @@ github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFd
 | 
			
		||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 | 
			
		||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 | 
			
		||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 | 
			
		||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
 | 
			
		||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
 | 
			
		||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
 | 
			
		||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 | 
			
		||||
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
 | 
			
		||||
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 | 
			
		||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
 | 
			
		||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 | 
			
		||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 | 
			
		||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 | 
			
		||||
go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8=
 | 
			
		||||
go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk=
 | 
			
		||||
go.uber.org/fx v1.19.3 h1:YqMRE4+2IepTYCMOvXqQpRa+QAVdiSTnsHU4XNWBceA=
 | 
			
		||||
@@ -137,14 +156,18 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
 | 
			
		||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
 | 
			
		||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
 | 
			
		||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y=
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ func (h *ManagerHandler) Login(c *gin.Context) {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	manager := h.App.AppConfig.Manager
 | 
			
		||||
	manager := h.App.Config.Manager
 | 
			
		||||
	if data.Username == manager.Username && data.Password == manager.Password {
 | 
			
		||||
		err := utils.SetLoginAdmin(c, manager)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -394,7 +394,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, user vo.User, apiKey *strin
 | 
			
		||||
	request = request.WithContext(ctx)
 | 
			
		||||
	request.Header.Add("Content-Type", "application/json")
 | 
			
		||||
 | 
			
		||||
	proxyURL := h.App.AppConfig.ProxyURL
 | 
			
		||||
	proxyURL := h.App.Config.ProxyURL
 | 
			
		||||
	if proxyURL == "" {
 | 
			
		||||
		client = &http.Client{}
 | 
			
		||||
	} else { // 使用代理
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ func (h *UploadHandler) Upload(c *gin.Context) {
 | 
			
		||||
// 生成上传文件路径
 | 
			
		||||
func (h *UploadHandler) genFilePath(filename string) (string, error) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	dir := fmt.Sprintf("%s/upload/%d/%d", h.App.AppConfig.StaticDir, now.Year(), now.Month())
 | 
			
		||||
	dir := fmt.Sprintf("%s/upload/%d/%d", h.App.Config.StaticDir, now.Year(), now.Month())
 | 
			
		||||
	_, err := os.Stat(dir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = os.MkdirAll(dir, 0755)
 | 
			
		||||
@@ -63,5 +63,5 @@ func (h *UploadHandler) genFilePath(filename string) (string, error) {
 | 
			
		||||
func (h *UploadHandler) genFileUrl(filePath string) string {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	filename := filepath.Base(filePath)
 | 
			
		||||
	return fmt.Sprintf("%s/upload/%d/%d/%s", h.App.AppConfig.StaticUrl, now.Year(), now.Month(), filename)
 | 
			
		||||
	return fmt.Sprintf("%s/upload/%d/%d/%s", h.App.Config.StaticUrl, now.Year(), now.Month(), filename)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package handler
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core"
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/store"
 | 
			
		||||
	"chatplus/store/model"
 | 
			
		||||
	"chatplus/store/vo"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
@@ -21,10 +22,11 @@ type UserHandler struct {
 | 
			
		||||
	BaseHandler
 | 
			
		||||
	db       *gorm.DB
 | 
			
		||||
	searcher *xdb.Searcher
 | 
			
		||||
	levelDB  *store.LevelDB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserHandler(app *core.AppServer, db *gorm.DB, searcher *xdb.Searcher) *UserHandler {
 | 
			
		||||
	handler := &UserHandler{db: db, searcher: searcher}
 | 
			
		||||
func NewUserHandler(app *core.AppServer, db *gorm.DB, searcher *xdb.Searcher, levelDB *store.LevelDB) *UserHandler {
 | 
			
		||||
	handler := &UserHandler{db: db, searcher: searcher, levelDB: levelDB}
 | 
			
		||||
	handler.App = app
 | 
			
		||||
	return handler
 | 
			
		||||
}
 | 
			
		||||
@@ -35,6 +37,8 @@ func (h *UserHandler) Register(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Username string `json:"username"`
 | 
			
		||||
		Password string `json:"password"`
 | 
			
		||||
		Mobile   string `json:"mobile"`
 | 
			
		||||
		Code     int    `json:"code"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
@@ -52,6 +56,16 @@ func (h *UserHandler) Register(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查验证码
 | 
			
		||||
	key := CodeStorePrefix + data.Mobile
 | 
			
		||||
	code, err := h.levelDB.Get(key)
 | 
			
		||||
	if err != nil || int(code.(float64)) != data.Code {
 | 
			
		||||
		resp.ERROR(c, "短信验证码错误")
 | 
			
		||||
		return
 | 
			
		||||
	} else {
 | 
			
		||||
		_ = h.levelDB.Delete(key) // 删除短信验证码
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check if the username is exists
 | 
			
		||||
	var item model.User
 | 
			
		||||
	tx := h.db.Where("username = ?", data.Username).First(&item)
 | 
			
		||||
@@ -89,7 +103,7 @@ func (h *UserHandler) Register(c *gin.Context) {
 | 
			
		||||
	var cfg model.Config
 | 
			
		||||
	h.db.Where("marker = ?", "system").First(&cfg)
 | 
			
		||||
	var config types.SystemConfig
 | 
			
		||||
	err := utils.JsonDecode(cfg.Config, &config)
 | 
			
		||||
	err = utils.JsonDecode(cfg.Config, &config)
 | 
			
		||||
	if err != nil || config.UserInitCalls <= 0 {
 | 
			
		||||
		user.Calls = types.UserInitCalls
 | 
			
		||||
	} else {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										116
									
								
								api/go/handler/verify_handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								api/go/handler/verify_handler.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
package handler
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core"
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/service"
 | 
			
		||||
	"chatplus/store"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"chatplus/utils/resp"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 生成验证的控制器
 | 
			
		||||
 | 
			
		||||
type VerifyHandler struct {
 | 
			
		||||
	BaseHandler
 | 
			
		||||
	sms *service.AliYunSmsService
 | 
			
		||||
	db  *store.LevelDB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TokenStorePrefix = "/tokens/"
 | 
			
		||||
const CodeStorePrefix = "/codes/"
 | 
			
		||||
 | 
			
		||||
func NewVerifyHandler(app *core.AppServer, sms *service.AliYunSmsService, db *store.LevelDB) *VerifyHandler {
 | 
			
		||||
	handler := &VerifyHandler{sms: sms, db: db}
 | 
			
		||||
	handler.App = app
 | 
			
		||||
	return handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type VerifyToken struct {
 | 
			
		||||
	Token     string
 | 
			
		||||
	Timestamp int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Token 生成自验证 token
 | 
			
		||||
func (h *VerifyHandler) Token(c *gin.Context) {
 | 
			
		||||
	// 确保是通过浏览器访问
 | 
			
		||||
	if c.GetHeader("Sec-Fetch-Mode") != "cors" {
 | 
			
		||||
		resp.HACKER(c)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token := VerifyToken{
 | 
			
		||||
		Token:     utils.RandString(32),
 | 
			
		||||
		Timestamp: time.Now().Unix(),
 | 
			
		||||
	}
 | 
			
		||||
	json := utils.JsonEncode(token)
 | 
			
		||||
	encrypt, err := utils.AesEncrypt(h.App.Config.AesEncryptKey, []byte(json))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "Token 加密出错")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = h.db.Put(TokenStorePrefix+token.Token, token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "Token 存储失败")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c, encrypt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendMsg 发送验证码短信
 | 
			
		||||
func (h *VerifyHandler) SendMsg(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Mobile string `json:"mobile"`
 | 
			
		||||
		Token  string `json:"token"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decrypt, err := utils.AesDecrypt(h.App.Config.AesEncryptKey, data.Token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "Token 解密失败")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var token VerifyToken
 | 
			
		||||
	err = utils.JsonDecode(string(decrypt), &token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "Token 解码失败")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = h.db.Get(TokenStorePrefix + token.Token)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.HACKER(c)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if time.Now().Unix()-token.Timestamp > 30 {
 | 
			
		||||
		resp.ERROR(c, "Token 已过期,请刷新页面重试")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	code := utils.RandomNumber(6)
 | 
			
		||||
	err = h.sms.SendVerifyCode(data.Mobile, code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 每个 token 用完一次立即失效
 | 
			
		||||
	_ = h.db.Delete(TokenStorePrefix + token.Token)
 | 
			
		||||
	// 存储验证码,等待后面注册验证
 | 
			
		||||
	err = h.db.Put(CodeStorePrefix+data.Mobile, code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "验证码保存失败")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c)
 | 
			
		||||
}
 | 
			
		||||
@@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"chatplus/handler"
 | 
			
		||||
	"chatplus/handler/admin"
 | 
			
		||||
	logger2 "chatplus/logger"
 | 
			
		||||
	"chatplus/service"
 | 
			
		||||
	"chatplus/store"
 | 
			
		||||
	"context"
 | 
			
		||||
	"embed"
 | 
			
		||||
@@ -103,6 +104,7 @@ func main() {
 | 
			
		||||
		fx.Provide(handler.NewUserHandler),
 | 
			
		||||
		fx.Provide(handler.NewChatHandler),
 | 
			
		||||
		fx.Provide(handler.NewUploadHandler),
 | 
			
		||||
		fx.Provide(handler.NewVerifyHandler),
 | 
			
		||||
 | 
			
		||||
		fx.Provide(admin.NewConfigHandler),
 | 
			
		||||
		fx.Provide(admin.NewAdminHandler),
 | 
			
		||||
@@ -110,6 +112,9 @@ func main() {
 | 
			
		||||
		fx.Provide(admin.NewUserHandler),
 | 
			
		||||
		fx.Provide(admin.NewChatRoleHandler),
 | 
			
		||||
 | 
			
		||||
		// 创建服务
 | 
			
		||||
		fx.Provide(service.NewAliYunSmsService),
 | 
			
		||||
 | 
			
		||||
		// 注册路由
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.ChatRoleHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/role/")
 | 
			
		||||
@@ -139,6 +144,11 @@ func main() {
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.UploadHandler) {
 | 
			
		||||
			s.Engine.POST("/api/upload", h.Upload)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.VerifyHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/verify/")
 | 
			
		||||
			group.GET("token", h.Token)
 | 
			
		||||
			group.POST("sms", h.SendMsg)
 | 
			
		||||
		}),
 | 
			
		||||
 | 
			
		||||
		// 管理后台控制器
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *admin.ConfigHandler) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								api/go/service/aliyun_sms_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								api/go/service/aliyun_sms_service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/store"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AliYunSmsService struct {
 | 
			
		||||
	config *types.AppConfig
 | 
			
		||||
	db     *store.LevelDB
 | 
			
		||||
	client *dysmsapi.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAliYunSmsService(config *types.AppConfig, db *store.LevelDB) (*AliYunSmsService, error) {
 | 
			
		||||
	// 创建阿里云短信客户端
 | 
			
		||||
	client, err := dysmsapi.NewClientWithAccessKey(
 | 
			
		||||
		"cn-hangzhou",
 | 
			
		||||
		config.SmsConfig.AccessKey,
 | 
			
		||||
		config.SmsConfig.AccessSecret)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to create client: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &AliYunSmsService{
 | 
			
		||||
		config: config,
 | 
			
		||||
		db:     db,
 | 
			
		||||
		client: client,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *AliYunSmsService) SendVerifyCode(mobile string, code int) error {
 | 
			
		||||
	// 创建短信请求并设置参数
 | 
			
		||||
	request := dysmsapi.CreateSendSmsRequest()
 | 
			
		||||
	request.Scheme = "https"
 | 
			
		||||
	request.Domain = s.config.SmsConfig.Domain
 | 
			
		||||
	request.PhoneNumbers = mobile
 | 
			
		||||
	request.SignName = "飞行的蜗牛"
 | 
			
		||||
	request.TemplateCode = "SMS_281460317"
 | 
			
		||||
	request.TemplateParam = fmt.Sprintf("{\"code\":\"%d\"}", code) // 短信模板中的参数
 | 
			
		||||
 | 
			
		||||
	// 发送短信
 | 
			
		||||
	response, err := s.client.SendSms(request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to send SMS:%v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if response.Code != "OK" {
 | 
			
		||||
		return fmt.Errorf("failed to send SMS:%v", response.Message)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								api/go/service/sms_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								api/go/service/sms_service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
package service
 | 
			
		||||
 | 
			
		||||
type SmsService interface {
 | 
			
		||||
	SendVerifyCode(mobile string, code int) error
 | 
			
		||||
}
 | 
			
		||||
@@ -30,13 +30,19 @@ func (db *LevelDB) Put(key string, value interface{}) error {
 | 
			
		||||
	return db.driver.Put([]byte(key), bytes, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *LevelDB) Get(key string) ([]byte, error) {
 | 
			
		||||
func (db *LevelDB) Get(key string) (interface{}, error) {
 | 
			
		||||
	bytes, err := db.driver.Get([]byte(key), nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bytes, nil
 | 
			
		||||
	var value interface{}
 | 
			
		||||
	err = json.Unmarshal(bytes, &value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return value, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *LevelDB) Search(prefix string) []string {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package model
 | 
			
		||||
type User struct {
 | 
			
		||||
	BaseModel
 | 
			
		||||
	Username    string `gorm:"index:username,unique"`
 | 
			
		||||
	Mobile      string
 | 
			
		||||
	Password    string
 | 
			
		||||
	Nickname    string
 | 
			
		||||
	Avatar      string
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import "chatplus/core/types"
 | 
			
		||||
type User struct {
 | 
			
		||||
	BaseVo
 | 
			
		||||
	Username    string           `json:"username"`
 | 
			
		||||
	Mobile      string           `json:"mobile"`
 | 
			
		||||
	Nickname    string           `json:"nickname"`
 | 
			
		||||
	Avatar      string           `json:"avatar"`
 | 
			
		||||
	Salt        string           `json:"salt"`          // 密码盐
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,24 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/store/model"
 | 
			
		||||
	"chatplus/store/vo"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/lionsoul2014/ip2region/binding/golang/xdb"
 | 
			
		||||
	"github.com/pkoukk/tiktoken-go"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/lionsoul2014/ip2region/binding/golang/xdb"
 | 
			
		||||
	"github.com/pkoukk/tiktoken-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	lMap := types.NewLMap[string, types.ChatSession]()
 | 
			
		||||
	lMap.Put("name", types.ChatSession{SessionId: utils.RandString(32)})
 | 
			
		||||
 | 
			
		||||
	item := lMap.Get("abc")
 | 
			
		||||
	fmt.Println(item)
 | 
			
		||||
	//testAesEncrypt()
 | 
			
		||||
	fmt.Println(utils.RandomNumber(6))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Http client 取消操作
 | 
			
		||||
@@ -143,3 +140,20 @@ func calTokens() {
 | 
			
		||||
	fmt.Println(len(token))
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testAesEncrypt() {
 | 
			
		||||
	// 加密
 | 
			
		||||
	text := []byte("this is a secret text")
 | 
			
		||||
	key := utils.RandString(24)
 | 
			
		||||
	encrypt, err := utils.AesEncrypt(key, text)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println("加密密文:", encrypt)
 | 
			
		||||
	// 解密
 | 
			
		||||
	decrypt, err := utils.AesDecrypt(key, encrypt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println("解密明文:", string(decrypt))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								api/go/utils/crypto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								api/go/utils/crypto.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AesEncrypt 加密
 | 
			
		||||
func AesEncrypt(keyStr string, data []byte) (string, error) {
 | 
			
		||||
	//创建加密实例
 | 
			
		||||
	key := []byte(keyStr)
 | 
			
		||||
	block, err := aes.NewCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	blockSize := block.BlockSize()
 | 
			
		||||
	encryptBytes := pkcs7Padding(data, blockSize)
 | 
			
		||||
	result := make([]byte, len(encryptBytes))
 | 
			
		||||
	//使用cbc加密模式
 | 
			
		||||
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
 | 
			
		||||
	//执行加密
 | 
			
		||||
	blockMode.CryptBlocks(result, encryptBytes)
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(result), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AesDecrypt 解密
 | 
			
		||||
func AesDecrypt(keyStr string, dataStr string) ([]byte, error) {
 | 
			
		||||
	//创建实例
 | 
			
		||||
	key := []byte(keyStr)
 | 
			
		||||
	block, err := aes.NewCipher(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, err := base64.StdEncoding.DecodeString(dataStr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	blockSize := block.BlockSize()
 | 
			
		||||
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
 | 
			
		||||
	result := make([]byte, len(data))
 | 
			
		||||
	//执行解密
 | 
			
		||||
	blockMode.CryptBlocks(result, data)
 | 
			
		||||
	//去除填充
 | 
			
		||||
	result, err = pkcs7UnPadding(result)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pkcs7Padding(data []byte, blockSize int) []byte {
 | 
			
		||||
	padding := blockSize - len(data)%blockSize
 | 
			
		||||
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
 | 
			
		||||
	return append(data, padText...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pkcs7UnPadding(data []byte) ([]byte, error) {
 | 
			
		||||
	length := len(data)
 | 
			
		||||
	if length == 0 {
 | 
			
		||||
		return nil, errors.New("empty encrypt data")
 | 
			
		||||
	}
 | 
			
		||||
	unPadding := int(data[length-1])
 | 
			
		||||
	return data[:(length - unPadding)], nil
 | 
			
		||||
}
 | 
			
		||||
@@ -22,17 +22,20 @@ func RandString(length int) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RandomNumber(bit int) int {
 | 
			
		||||
	rand.Seed(time.Now().UnixNano())
 | 
			
		||||
	min := 1 // min value
 | 
			
		||||
	max := 1 //max value
 | 
			
		||||
	for i := 0; i < bit; i++ {
 | 
			
		||||
		min = min * 10
 | 
			
		||||
		max = max * 10
 | 
			
		||||
	}
 | 
			
		||||
	max = max * 10
 | 
			
		||||
	min := intPow(10, bit-1)
 | 
			
		||||
	max := intPow(10, bit) - 1
 | 
			
		||||
 | 
			
		||||
	return rand.Intn(max-min+1) + min
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func intPow(x, y int) int {
 | 
			
		||||
	result := 1
 | 
			
		||||
	for i := 0; i < y; i++ {
 | 
			
		||||
		result *= x
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ContainsStr(slice []string, item string) bool {
 | 
			
		||||
	for _, e := range slice {
 | 
			
		||||
		if e == item {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								database/sms.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								database/sms.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
ALTER TABLE `chatgpt_users` ADD `mobile` CHAR(11) NOT NULL COMMENT '手机号码' AFTER `username`;
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
        <div class="content">
 | 
			
		||||
          <el-form :model="formData" label-width="120px" ref="formRef" :rules="rules">
 | 
			
		||||
            <div class="block">
 | 
			
		||||
              <el-input placeholder="手机号/邮箱(4-30位)"
 | 
			
		||||
              <el-input placeholder="请输入用户名(4-30位)"
 | 
			
		||||
                        size="large" maxlength="30"
 | 
			
		||||
                        v-model="formData.username"
 | 
			
		||||
                        autocomplete="off">
 | 
			
		||||
@@ -48,6 +48,42 @@
 | 
			
		||||
              </el-input>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="block">
 | 
			
		||||
              <el-input placeholder="手机号码"
 | 
			
		||||
                        size="large" maxlength="11"
 | 
			
		||||
                        v-model="formData.mobile"
 | 
			
		||||
                        autocomplete="off">
 | 
			
		||||
                <template #prefix>
 | 
			
		||||
                  <el-icon>
 | 
			
		||||
                    <Iphone/>
 | 
			
		||||
                  </el-icon>
 | 
			
		||||
                </template>
 | 
			
		||||
              </el-input>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="block">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="12">
 | 
			
		||||
                  <el-input placeholder="手机验证码"
 | 
			
		||||
                            size="large" maxlength="30"
 | 
			
		||||
                            v-model.number="formData.code"
 | 
			
		||||
                            autocomplete="off">
 | 
			
		||||
                    <template #prefix>
 | 
			
		||||
                      <el-icon>
 | 
			
		||||
                        <Checked/>
 | 
			
		||||
                      </el-icon>
 | 
			
		||||
                    </template>
 | 
			
		||||
                  </el-input>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="12">
 | 
			
		||||
                  <el-button type="primary" class="sms-btn" :disabled="!canSend" size="large" @click="sendMsg" plain>{{
 | 
			
		||||
                      btnText
 | 
			
		||||
                    }}
 | 
			
		||||
                  </el-button>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <el-row class="btn-row">
 | 
			
		||||
              <el-button class="login-btn" size="large" type="primary" @click="register">注册</el-button>
 | 
			
		||||
            </el-row>
 | 
			
		||||
@@ -70,17 +106,20 @@
 | 
			
		||||
<script setup>
 | 
			
		||||
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
import {Lock, UserFilled} from "@element-plus/icons-vue";
 | 
			
		||||
import {httpPost} from "@/utils/http";
 | 
			
		||||
import {Checked, Iphone, Lock, UserFilled} from "@element-plus/icons-vue";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import FooterBar from "@/components/FooterBar.vue";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const title = ref('ChatGPT-PLUS 用户注册');
 | 
			
		||||
const btnText = ref('发送验证码')
 | 
			
		||||
const formData = ref({
 | 
			
		||||
  username: '',
 | 
			
		||||
  password: '',
 | 
			
		||||
  mobile: '18575670125',
 | 
			
		||||
  code: '',
 | 
			
		||||
  repass: '',
 | 
			
		||||
})
 | 
			
		||||
const formRef = ref(null)
 | 
			
		||||
@@ -89,9 +128,6 @@ const register = function () {
 | 
			
		||||
  if (formData.value.username.length < 4) {
 | 
			
		||||
    return ElMessage.error('用户名的长度为4-30个字符');
 | 
			
		||||
  }
 | 
			
		||||
  if (!validateEmail(formData.value.username) && !validateMobile(formData.value.username)) {
 | 
			
		||||
    return ElMessage.error('用户名不合法,请输入手机号码或者邮箱地址');
 | 
			
		||||
  }
 | 
			
		||||
  if (formData.value.password.length < 8) {
 | 
			
		||||
    return ElMessage.error('密码的长度为8-16个字符');
 | 
			
		||||
  }
 | 
			
		||||
@@ -106,15 +142,50 @@ const register = function () {
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const validateEmail = function (email) {
 | 
			
		||||
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 | 
			
		||||
  return regex.test(email);
 | 
			
		||||
}
 | 
			
		||||
// const validateEmail = function (email) {
 | 
			
		||||
//   const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 | 
			
		||||
//   return regex.test(email);
 | 
			
		||||
// }
 | 
			
		||||
const validateMobile = function (mobile) {
 | 
			
		||||
  const regex = /^1[345789]\d{9}$/;
 | 
			
		||||
  return regex.test(mobile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const canSend = ref(true)
 | 
			
		||||
const sendMsg = () => {
 | 
			
		||||
  if (!canSend.value) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!validateMobile(formData.value.mobile)) {
 | 
			
		||||
    return ElMessage.error("请输入合法的手机号")
 | 
			
		||||
  }
 | 
			
		||||
  canSend.value = false
 | 
			
		||||
  httpGet('/api/verify/token').then(res => {
 | 
			
		||||
    httpPost('/api/verify/sms', {token: res.data, mobile: formData.value.mobile}).then(() => {
 | 
			
		||||
      ElMessage.success('短信发送成功')
 | 
			
		||||
      let time = 120
 | 
			
		||||
      btnText.value = time
 | 
			
		||||
      const handler = setInterval(() => {
 | 
			
		||||
        time = time - 1
 | 
			
		||||
        if (time <= 0) {
 | 
			
		||||
          clearInterval(handler)
 | 
			
		||||
          btnText.value = '重新发送'
 | 
			
		||||
          canSend.value = true
 | 
			
		||||
        } else {
 | 
			
		||||
          btnText.value = time
 | 
			
		||||
        }
 | 
			
		||||
      }, 1000)
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      canSend.value = true
 | 
			
		||||
      ElMessage.error('短信发送失败:' + e.message)
 | 
			
		||||
    })
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    console.log('failed to fetch token: ' + e.message)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user