package server import ( "context" "fmt" "net/http" "path/filepath" "time" "github.com/gogf/gf/v2/os/glog" "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/gcfg" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gfile" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" ) type Json struct { Code int `json:"code" d:"1"` Data any `json:"data"` Msg string `json:"msg" d:"操作成功"` } type ApiRes struct { ctx context.Context json *Json } func Success(ctx context.Context) *ApiRes { json := Json{ Code: 1, } var a = ApiRes{ ctx: ctx, json: &json, } return &a } func Error(ctx context.Context) *ApiRes { json := Json{ Code: 0, } var a = ApiRes{ ctx: ctx, json: &json, } return &a } func (a *ApiRes) SetCode(code int) *ApiRes { a.json.Code = code return a } func (a *ApiRes) SetData(data interface{}) *ApiRes { a.json.Data = data return a } func (a *ApiRes) SetMsg(msg string) *ApiRes { a.json.Msg = msg return a } func (a *ApiRes) End() { from := g.RequestFromCtx(a.ctx) from.Header.Set("Access-Control-Expose-Headers", "Set-Cookie") from.Response.Status = 200 from.Response.WriteJson(a.json) return } func (a *ApiRes) FileDownload(path, name string) { from := g.RequestFromCtx(a.ctx) from.Response.ServeFileDownload(path) return } func (a *ApiRes) FileSelect(path string) { from := g.RequestFromCtx(a.ctx) from.Response.ServeFile(path) return } // LoginJson 返回登录json数据 /* * @param ctx 上下文 * @param msg 返回信息 * @param data 返回数据 */ func LoginJson(r *ghttp.Request, msg string, data ...interface{}) { var info interface{} if len(data) > 0 { info = data[0] } else { info = nil } r.Response.WriteJsonExit(Json{ Code: 1, Data: info, Msg: msg, }) } // ResponseJson 返回json数据 /* * @param ctx 上下文 * @param data 返回数据 */ func ResponseJson(ctx context.Context, data interface{}) { g.RequestFromCtx(ctx).Response.WriteJson(data) return } type PageSize struct { CurrentPage int `json:"currentPage"` Data interface{} `json:"data"` LastPage int `json:"lastPage"` PerPage int `json:"per_page"` Total int `json:"total"` } // SetPage 设置分页 /* * @param page 当前页 * @param limit 每页显示条数 * @param total 总条数 * @param data 返回数据 * @return PageSize */ func SetPage(page, limit, total int, data interface{}) *PageSize { var size = new(PageSize) if page == 1 { size.LastPage = 1 } else { size.LastPage = page - 1 } size.PerPage = limit size.Total = total size.CurrentPage = page size.Data = data return size } // MiddlewareError 异常处理中间件 func MiddlewareError(r *ghttp.Request) { r.Middleware.Next() var ( err = r.GetError() msg string res = r.GetHandlerResponse() status = r.Response.Status ) json := new(Json) json.Code = 1 json.Data = res json.Msg = "操作成功" if err != nil { bo := gstr.Contains(err.Error(), ": ") if bo { msg = gstr.SubStrFromEx(err.Error(), ": ") } else { msg = err.Error() } r.Response.ClearBuffer() json.Code = 0 json.Msg = msg r.Response.Status = http.StatusInternalServerError } if r.Response.BufferLength() > 0 { return } if status == 401 { json.Code = 0 json.Msg = "请登录后操作" } r.Response.WriteJson(json) } // AuthBase 鉴权中间件,只有前端或者后端登录成功之后才能通过 func AuthBase(r *ghttp.Request, name string) { info, err := r.Session.Get(name, nil) if err != nil { panic(err.Error()) } if !info.IsEmpty() { r.Middleware.Next() } else { NoLogin(r) } } // AuthAdmin 鉴权中间件,只有后端登录成功之后才能通过 func AuthAdmin(r *ghttp.Request) { AuthBase(r, "admin") } // AuthIndex 鉴权中间件,只有前端登录成功之后才能通过 func AuthIndex(r *ghttp.Request) { AuthBase(r, "user") } // NoLogin 未登录返回 func NoLogin(r *ghttp.Request) { r.Response.Status = 401 r.Response.WriteJsonExit(Json{ Code: 401, Data: nil, Msg: "请登录后操作", }) } // CreateFileDir 创建文件目录 func CreateFileDir() error { path := gfile.Pwd() + "/resource" if !gfile.IsDir(path) { if err := gfile.Mkdir(path); err != nil { return err } if err := gfile.Mkdir(path + "/public/upload"); err != nil { return err } } return nil } func AuthLoginSession(ctx context.Context, sessionKey string) { ti, err := g.RequestFromCtx(ctx).Session.Get(sessionKey+"LoginTime", "") if err != nil { panic(err.Error()) } if !ti.IsEmpty() { now := gtime.Now().Timestamp() if now-gconv.Int64(ti) <= 300 { number, err := g.RequestFromCtx(ctx).Session.Get(sessionKey+"LoginNum", 0) if err != nil { panic(err.Error()) } if !number.IsEmpty() { count := gconv.Int(number) if count == 3 { panic("请等待5分钟后再次尝试或修改后尝试登录") } } } } } func LoginCountSession(ctx context.Context, sessionKey string) { ti, err := g.RequestFromCtx(ctx).Session.Get(sessionKey+"LoginTime", "") if err != nil { panic(err.Error()) } if ti.IsEmpty() { _ = g.RequestFromCtx(ctx).Session.Set(sessionKey+"LoginTime", gtime.Now().Timestamp()) } now := gtime.Now().Timestamp() if now-gconv.Int64(ti) <= 300 { number, err := g.RequestFromCtx(ctx).Session.Get(sessionKey+"LoginNum", 0) if err != nil { panic(err.Error()) } if number.IsEmpty() { _ = g.RequestFromCtx(ctx).Session.Set(sessionKey+"LoginNum", 1) } else { count := gconv.Int(number) if count == 3 { panic("尝试登录已超过限制,请等待5分钟后再次尝试或修改后尝试登录") } _ = g.RequestFromCtx(ctx).Session.Set(sessionKey+"LoginNum", count+1) } } else { _ = g.RequestFromCtx(ctx).Session.Set(sessionKey+"LoginTime", gtime.Now().Timestamp()) _ = g.RequestFromCtx(ctx).Session.Set(sessionKey+"LoginNum", 1) } } func enhanceOpenAPIDoc(s *ghttp.Server) { openapi := s.GetOpenApi() openapi.Config.CommonResponse = ghttp.DefaultHandlerResponse{} openapi.Config.CommonResponseDataField = `Data` // API description. openapi.Info = goai.Info{ Title: "Api列表", Description: "Api列表 包含各端接口信息 字段注释 枚举说明", Contact: &goai.Contact{ Name: "Api列表", URL: "https://panel.magicany.cc:8888/btpanel", }, License: &goai.License{ Name: "马国栋", URL: "https://panel.magicany.cc:8888/btpanel", }, Version: "Api列表", } } var ConfigPath = filepath.Join(gfile.Pwd(), "manifest", "config", "config.yaml") var uploadPath = filepath.Join(gfile.Pwd(), "resource") // Start 启动服务 /* * @param agent string 浏览器标识 * @param maxSessionTime time.Duration session最大时间 * @param isApi bool 是否开启api * @param maxBody ...int64 最大上传文件大小 默认200M * @return *ghttp.Server 服务实例 */ func Start(agent string, maxSessionTime time.Duration, isApi bool, maxBody ...int64) *ghttp.Server { // var s *ghttp.Server s := g.Server() s.SetDumpRouterMap(false) s.AddStaticPath(fmt.Sprintf("%vstatic", gfile.Separator), uploadPath) err := s.SetLogPath(gfile.Join(gfile.Pwd(), "resource", "log")) if err != nil { fmt.Println(err) } s.SetLogLevel("all") s.SetLogStdout(false) if len(maxBody) > 0 { s.SetClientMaxBodySize(maxBody[0]) } else { s.SetClientMaxBodySize(200 * 1024 * 1024) } s.SetFormParsingMemory(50 * 1024 * 1024) if isApi { s.SetOpenApiPath("/api.json") s.SetSwaggerPath("/swagger") } s.SetMaxHeaderBytes(1024 * 20) s.SetErrorStack(true) s.SetSessionIdName("zrSession") s.SetAccessLogEnabled(true) s.SetSessionMaxAge(maxSessionTime) err = s.SetConfigWithMap(g.Map{ "sessionPath": gfile.Join(gfile.Pwd(), "resource", "session"), "serverAgent": agent, }) if err != nil { fmt.Println(err) } s.Use(MiddlewareError) enhanceOpenAPIDoc(s) return s } // SetConfigAndRun 设置配置并运行服务 // @param s *ghttp.Server 服务实例 // @param address string 监听地址 func SetConfigAndRun(s *ghttp.Server, address string) { g.Log().Info(gctx.New(), "正在设置日志配置") g.Log().File("{Y-m-d}.log") g.Log().Path(gfile.Join(gfile.Pwd(), "log")) g.Log().Level(glog.LEVEL_ALL) g.Log().SetWriterColorEnable(false) g.Log().SetTimeFormat("2006-01-02 15:04:05") g.Log().Stdout(false) cfg := g.Log().GetConfig() cfg.RotateBackupLimit = 10 cfg.RotateSize = 1024 * 1024 * 2 err := g.Log().SetConfig(cfg) if err != nil { panic(fmt.Sprintf("设置日志配置失败: %+v", err)) } s.SetAccessLogEnabled(false) s.SetErrorLogEnabled(true) sLog := s.Logger() sLog.Level(glog.LEVEL_ERRO) err = sLog.SetPath(gfile.Join(gfile.Pwd(), "resource", "log")) if err != nil { panic(fmt.Sprintf("添加服务日志路径失败: %+v", err)) } sLog.SetLevelPrefix(glog.LEVEL_ERRO, "error") s.SetLogger(sLog) g.Log().Info(gctx.New(), "设置日志配置完成") g.Log().Info(gctx.New(), "正在设置服务监听") s.SetAddr(address) s.SetFileServerEnabled(true) s.SetCookieDomain(fmt.Sprintf("http://%s", address)) g.Log().Info(gctx.New(), "设置服务监听完成,执行自动服务") s.Run() } func CORSMiddleware(r *ghttp.Request) { corsOptions := r.Response.DefaultCORSOptions() cfg, _ := gcfg.Instance().Get(r.Context(), "doMain", nil) if !cfg.IsNil() { corsOptions.AllowDomain = cfg.Strings() } r.Response.CORS(corsOptions) r.Middleware.Next() }