package cmd import ( "context" "fmt" "net/http" "net/url" "os" "reflect" "strings" "unsafe" _ "unsafe" "github.com/tiger1103/gfast/v3/api/v1/common/coryCommon" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/goai" "github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/gres" "github.com/gogf/gf/v2/os/gsession" "github.com/gogf/gf/v2/os/gspath" "github.com/gogf/gf/v2/os/gview" "github.com/gogf/gf/v2/text/gstr" "github.com/tiger1103/gfast/v3/api/saft_hat" "github.com/tiger1103/gfast/v3/api/video_hat" "github.com/tiger1103/gfast/v3/internal/consts" "github.com/tiger1103/gfast/v3/internal/router" "github.com/tiger1103/gfast/v3/library/libValidate" "github.com/tiger1103/gfast/v3/library/upload" "github.com/tiger1103/gfast/v3/task" ) var Main = gcmd.Command{ Name: "main", Usage: "main", Brief: "start http server", Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { g.Log().SetFlags(glog.F_ASYNC | glog.F_TIME_DATE | glog.F_TIME_TIME | glog.F_FILE_LONG) g.Log().Info(ctx, "GFast version:", consts.Version) s := g.Server() getwd, err := os.Getwd() getwd = strings.ReplaceAll(getwd, "\\", "/") //s.Group("/file", func(group *ghttp.RouterGroup) { // group.Middleware(commonService.Middleware().MiddlewareCORS) //}) s.AddStaticPath("/resource", getwd+"/resource") // 指向当前资源 s.AddStaticPath(coryCommon.GlobalFile, getwd+"/resource/public") // 指向pc s.AddStaticPath("/wxfile", getwd+"/resource/public") // 指向小程序 s.Group("/", func(group *ghttp.RouterGroup) { router.R.BindController(ctx, group) }) //// zm/api/wxApplet/wxApplet/ //s.BindHookHandler("/zm/api/wxApplet/wxApplet/*", ghttp.HookBeforeServe, bandWxApp) s.BindHookHandler("/file/*", ghttp.HookBeforeServe, reStaticFile) // 重新配置swaggerUI静态页面--start--,若要使用原版gf字段swaggerUI请删除或注释此段 s.BindHookHandler(g.Cfg().MustGet(ctx, "server.swaggerPath").String()+"/*", ghttp.HookBeforeServe, func(r *ghttp.Request) { content := gstr.ReplaceByMap(consts.SwaggerUITemplate, map[string]string{ `{SwaggerUIDocUrl}`: g.Cfg().MustGet(ctx, "server.openapiPath").String(), `{SwaggerUIDocNamePlaceHolder}`: gstr.TrimRight(fmt.Sprintf(`//%s`, r.Host)), }) r.Response.Write(content) r.ExitAll() }) // 重新配置swaggerUI静态页面--end-- enhanceOpenAPIDoc(s) // 注册相关组件 register() key := g.Cfg().MustGet(gctx.New(), "wx.key") crt := g.Cfg().MustGet(gctx.New(), "wx.crt") if len(key.String()) > 0 && len(crt.String()) > 0 { s.EnableHTTPS(crt.String(), key.String()) } s.Run() return nil }, } type staticFile struct { File *gres.File // Resource file object. Path string // File path. IsDir bool // Is directory. } type middleware struct { served bool // Is the request served, which is used for checking response status 404. request *Request // The request object pointer. handlerIndex int // Index number for executing sequence purpose for handler items. handlerMDIndex int // Index number for executing sequence purpose for bound middleware of handler item. } type Request struct { *http.Request Server *ghttp.Server // Server. Cookie *ghttp.Cookie // Cookie. Session *gsession.Session // Session. Response *ghttp.Response // Corresponding Response of this request. Router *ghttp.Router // Matched Router for this request. Note that it's not available in HOOK handler. EnterTime int64 // Request starting time in milliseconds. LeaveTime int64 // Request to end time in milliseconds. Middleware *middleware // Middleware manager. StaticFile *staticFile // Static file object for static file serving. // ================================================================================================================= // Private attributes for internal usage purpose. // ================================================================================================================= handlers []*ghttp.HandlerItemParsed // All matched handlers containing handler, hook and middleware for this request. serveHandler *ghttp.HandlerItemParsed // Real handler serving for this request, not hook or middleware. handlerResponse interface{} // Handler response object for Request/Response handler. hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose. hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose. parsedQuery bool // A bool marking whether the GET parameters parsed. parsedBody bool // A bool marking whether the request body parsed. parsedForm bool // A bool marking whether request Form parsed for HTTP method PUT, POST, PATCH. paramsMap map[string]interface{} // Custom parameters map. routerMap map[string]string // Router parameters map, which might be nil if there are no router parameters. queryMap map[string]interface{} // Query parameters map, which is nil if there's no query string. formMap map[string]interface{} // Form parameters map, which is nil if there's no form of data from the client. bodyMap map[string]interface{} // Body parameters map, which might be nil if their nobody content. error error // Current executing error of the request. exitAll bool // A bool marking whether current request is exited. parsedHost string // The parsed host name for current host used by GetHost function. clientIp string // The parsed client ip for current host used by GetClientIp function. bodyContent []byte // Request body content. isFileRequest bool // A bool marking whether current request is file serving. viewObject *gview.View // Custom template view engine object for this response. viewParams gview.Params // Custom template view variables for this response. originUrlPath string // Original URL path that passed from client. } type staticPathItem struct { Prefix string Path string } //// bandWxApp 禁止访问小程序 //func bandWxApp(r *ghttp.Request) { // // 如果不为 /zm/api/wxApplet/wxApplet/sysUserProjectRelevancy/list // if r.URL.Path != "/zm/api/wxApplet/wxApplet/sysUserProjectRelevancy/list" { // r.Response.WriteJsonExit(g.Map{"code": 403, "msg": "禁止访问"}) // } //} // staticFile 重赋值中间件 func reStaticFile(r *ghttp.Request) { isURLEncoded := func(s string) bool { // 如果 s 为空,直接返回 false if s == "" { return false } _, err := url.QueryUnescape(s) return err == nil } if !isURLEncoded(r.URL.RawPath) { return } // 解码 URL r.URL.Path, _ = url.QueryUnescape(r.URL.RawPath) fileRequestField := reflect.ValueOf(r).Elem().FieldByName("isFileRequest") reflect.NewAt(fileRequestField.Type(), unsafe.Pointer(fileRequestField.UnsafeAddr())).Elem().Set(reflect.ValueOf(true)) configField := reflect.ValueOf(r.Server) staticPathList := []staticPathItem{} staticPaths := configField.Elem().FieldByName("config").FieldByName("StaticPaths") for i := range make([]struct{}, staticPaths.Len()) { prefix := staticPaths.Index(i).FieldByName("Prefix").String() path := staticPaths.Index(i).FieldByName("Path").String() staticPathList = append(staticPathList, staticPathItem{ Prefix: prefix, Path: path, }) } indexFileList := []string{} IndexFiles := configField.Elem().FieldByName("config").FieldByName("IndexFiles") for i := range make([]struct{}, IndexFiles.Len()) { indexFileList = append(indexFileList, IndexFiles.Index(i).String()) } SearchPathsList := []string{} SearchPaths := configField.Elem().FieldByName("config").FieldByName("SearchPaths") for i := range make([]struct{}, SearchPaths.Len()) { SearchPathsList = append(SearchPathsList, SearchPaths.Index(i).String()) } (*Request)(unsafe.Pointer(r)).StaticFile = searchStaticFile(r.URL.Path, staticPathList, SearchPathsList, indexFileList) } func searchStaticFile(uri string, StaticPaths []staticPathItem, SearchPaths, indexFileList []string) *staticFile { var ( file *gres.File path string dir bool ) // Firstly search the StaticPaths mapping. if len(StaticPaths) > 0 { for _, item := range StaticPaths { if len(uri) >= len(item.Prefix) && strings.EqualFold(item.Prefix, uri[0:len(item.Prefix)]) { // To avoid case like: /static/style -> /static/style.css if len(uri) > len(item.Prefix) && uri[len(item.Prefix)] != '/' { continue } file = gres.GetWithIndex(item.Path+uri[len(item.Prefix):], indexFileList) if file != nil { return &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } } path, dir = gspath.Search(item.Path, uri[len(item.Prefix):], indexFileList...) if path != "" { return &staticFile{ Path: path, IsDir: dir, } } } } } // Secondly search the root and searching paths. if len(SearchPaths) > 0 { for _, p := range SearchPaths { file = gres.GetWithIndex(p+uri, indexFileList) if file != nil { return &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } } if path, dir = gspath.Search(p, uri, indexFileList...); path != "" { return &staticFile{ Path: path, IsDir: dir, } } } } // Lastly search the resource manager. if len(StaticPaths) == 0 && len(SearchPaths) == 0 { if file = gres.GetWithIndex(uri, indexFileList); file != nil { return &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } } } return nil } // 相关组件注册 func register() { // 注册上传组件 upload.Register() // 注册自定义验证规则 libValidate.Register() // 执行计划任务 task.Run() go video_hat.WsConnection() // 连接视频安全帽平台 // go saft_hat.StartWs() // 开启安全帽 WS 推送,端口8222 go saft_hat.HeartCheck() // 开启安全帽设备数据的心跳检测 } func enhanceOpenAPIDoc(s *ghttp.Server) { openapi := s.GetOpenApi() openapi.Config.CommonResponse = ghttp.DefaultHandlerResponse{} openapi.Config.CommonResponseDataField = `Data` // API description. openapi.Info = goai.Info{ Title: consts.OpenAPITitle, Description: consts.OpenAPIDescription, Contact: &goai.Contact{ Name: consts.OpenAPIContactName, URL: consts.OpenAPIContactUrl, }, } }