402 lines
9.4 KiB
Go
402 lines
9.4 KiB
Go
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()
|
||
}
|