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,
|
||
},
|
||
}
|
||
}
|