Files
zmkgC/internal/cmd/cmd.go
2025-07-07 20:11:59 +08:00

293 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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