5 Commits

Author SHA1 Message Date
fad0b852cb feat: 修改 api 为 option 模式设置; 添加 healthz handler 2025-07-11 11:52:53 +08:00
7f49105b23 feat: 添加 MustStop 方法
feat: 添加 gin 统一执行 api 函数
2025-07-10 16:29:43 +08:00
6cd0fa3d6c chore: 修改 401 错误信息 2025-07-08 16:47:37 +08:00
9c8460fc44 feat: 添加 MustWithData 2025-07-04 13:57:26 +08:00
5df55a364d feat: tool 添加 gin local 2025-07-04 10:27:44 +08:00
7 changed files with 201 additions and 1 deletions

115
api/api.go Normal file
View File

@ -0,0 +1,115 @@
package api
import (
"context"
"crypto/tls"
"errors"
"gitea.loveuer.com/yizhisec/packages/handler"
"gitea.loveuer.com/yizhisec/packages/logger"
"github.com/gin-gonic/gin"
"net"
"net/http"
)
type Option func(*option)
type option struct {
name string
version string
address string
app *gin.Engine
tlsConfig *tls.Config
}
func WithName(name string) Option {
return func(o *option) {
if name == "" {
o.name = name
}
}
}
func WithVersion(version string) Option {
return func(o *option) {
if version == "" {
o.version = version
}
}
}
func WithAddress(address string) Option {
return func(o *option) {
if address == "" {
o.address = address
}
}
}
func WithApp(app *gin.Engine) Option {
return func(o *option) {
if app != nil {
o.app = app
}
}
}
func WithTLSConfig(tlsConfig *tls.Config) Option {
return func(o *option) {
if tlsConfig != nil {
o.tlsConfig = tlsConfig
}
}
}
func New(ctx context.Context, optFns ...Option) (func(context.Context) error, error) {
var (
err error
fn func(context.Context) error
ln net.Listener
opt = &option{
name: "unknown",
version: "v0.0.1",
address: "127.0.0.1:9119",
tlsConfig: nil,
}
)
for _, ofn := range optFns {
ofn(opt)
}
if opt.app == nil {
opt.app = gin.Default()
opt.app.GET("/healthz", handler.Healthz(opt.name, opt.version))
}
if opt.tlsConfig != nil {
ln, err = tls.Listen("tcp", opt.address, opt.tlsConfig)
} else {
ln, err = net.Listen("tcp", opt.address)
}
if err != nil {
return fn, err
}
svc := &http.Server{
Handler: opt.app,
}
go func() {
logger.InfoCtx(ctx, "[%s] api svc running at: %s", opt.name, opt.address)
if err = svc.Serve(ln); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
logger.ErrorCtx(ctx, "api svc run failed, err = %s", err.Error())
}
}
}()
fn = func(timeout context.Context) error {
logger.WarnCtx(ctx, "[%s] api svc shutdown...", opt.name)
return svc.Shutdown(timeout)
}
return fn, nil
}

19
handler/healthz.go Normal file
View File

@ -0,0 +1,19 @@
package handler
import (
"gitea.loveuer.com/yizhisec/packages/resp"
"github.com/gin-gonic/gin"
"time"
)
func Healthz(name, version string) gin.HandlerFunc {
start := time.Now()
return func(c *gin.Context) {
resp.R200(c, gin.H{
"name": name,
"version": version,
"start_at": start,
"uptime": time.Since(start).String(),
})
}
}

4
opt/opt.go Normal file
View File

@ -0,0 +1,4 @@
package opt
type Config struct {
}

View File

@ -3,7 +3,7 @@ package resp
const (
Msg200 = "操作成功"
Msg400 = "参数错误"
Msg401 = "登录信息不存在或已过期, 请重新登录"
Msg401 = "该账号登录已失效, 请重新登录"
Msg401NoMulti = "用户已在其他地方登录"
Msg403 = "权限不足"
Msg404 = "资源不存在"

View File

@ -3,6 +3,7 @@ package resp
import (
"errors"
"github.com/gin-gonic/gin"
"github.com/spf13/cast"
)
type res struct {
@ -53,6 +54,10 @@ func _r(c *gin.Context, r *res, args ...any) {
r.Data = args[1]
case 3:
r.Err = args[2]
case 4:
if code, err := cast.ToIntE(args[3]); err == nil {
r.Code = code
}
}
if r.Msg == "" {
@ -65,6 +70,7 @@ func _r(c *gin.Context, r *res, args ...any) {
func R400(c *gin.Context, args ...any) {
r := &res{
Status: 400,
Code: -1,
}
_r(c, r, args...)
@ -73,6 +79,7 @@ func R400(c *gin.Context, args ...any) {
func R401(c *gin.Context, args ...any) {
r := &res{
Status: 401,
Code: -1,
}
_r(c, r, args...)
@ -81,6 +88,7 @@ func R401(c *gin.Context, args ...any) {
func R403(c *gin.Context, args ...any) {
r := &res{
Status: 403,
Code: -1,
}
_r(c, r, args...)
@ -89,6 +97,7 @@ func R403(c *gin.Context, args ...any) {
func R500(c *gin.Context, args ...any) {
r := &res{
Status: 500,
Code: -1,
}
_r(c, r, args...)
@ -97,6 +106,7 @@ func R500(c *gin.Context, args ...any) {
func R501(c *gin.Context, args ...any) {
r := &res{
Status: 501,
Code: -1,
}
_r(c, r, args...)

12
tool/gin.go Normal file
View File

@ -0,0 +1,12 @@
package tool
import "github.com/gin-gonic/gin"
func Local(c *gin.Context, key string) any {
data, ok := c.Get(key)
if !ok {
return nil
}
return data
}

View File

@ -1,7 +1,9 @@
package tool
import (
"context"
"gitea.loveuer.com/yizhisec/packages/logger"
"sync"
)
func Must(errs ...error) {
@ -11,3 +13,41 @@ func Must(errs ...error) {
}
}
}
func MustWithData[T any](data T, err error) T {
Must(err)
return data
}
func MustStop(ctx context.Context, stopFns ...func(ctx context.Context) error) {
if len(stopFns) == 0 {
return
}
ok := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(len(stopFns))
for _, fn := range stopFns {
go func() {
defer wg.Done()
if err := fn(ctx); err != nil {
logger.ErrorCtx(ctx, "stop function failed, err = %s", err.Error())
}
}()
}
go func() {
select {
case <-ctx.Done():
logger.FatalCtx(ctx, "stop function timeout, force down")
case _, _ = <-ok:
return
}
}()
wg.Wait()
close(ok)
}