293 lines
11 KiB
Go
293 lines
11 KiB
Go
|
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,
|
|||
|
},
|
|||
|
}
|
|||
|
}
|