在 fast-forward 到 origin/main 后解决 music logic/repo 冲突,SQL 与模型统一 t_* 表名;Makefile 修正 golangci-lint 变量并令 test/deploy 依赖 lint;补齐 admin/transport 等 lint 修复与 music logic 单测以满足覆盖率门禁。 Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
package httpx
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
|
|
adminlogic "spark-server/internal/module/admin/logic"
|
|
authlogic "spark-server/internal/module/auth/logic"
|
|
favlogic "spark-server/internal/module/favorite/logic"
|
|
friendlogic "spark-server/internal/module/friend/logic"
|
|
grouplogic "spark-server/internal/module/group/logic"
|
|
msglogic "spark-server/internal/module/message/logic"
|
|
momentlogic "spark-server/internal/module/moment/logic"
|
|
musiclogic "spark-server/internal/module/music/logic"
|
|
smslogic "spark-server/internal/module/sms/logic"
|
|
squarelogic "spark-server/internal/module/square/logic"
|
|
synclogic "spark-server/internal/module/sync/logic"
|
|
uploadlogic "spark-server/internal/module/upload/logic"
|
|
userlogic "spark-server/internal/module/user/logic"
|
|
"spark-server/internal/pkg/ratelimit"
|
|
)
|
|
|
|
type Server struct {
|
|
log *zap.Logger
|
|
http *http.Server
|
|
ready chan struct{}
|
|
readyOnce sync.Once
|
|
listening atomic.Bool
|
|
}
|
|
|
|
// Health is the readiness checker. Nil means tests: /readyz is always ready.
|
|
type Health interface {
|
|
Ready(ctx context.Context) (ok bool, data any)
|
|
}
|
|
|
|
// EngineDeps wires Application services into Gin. Auth nil disables Bearer checks (tests).
|
|
type EngineDeps struct {
|
|
Auth authlogic.Service
|
|
Login *authlogic.LoginService
|
|
SMS *smslogic.Service
|
|
Users *userlogic.Service
|
|
Friends *friendlogic.Service
|
|
Groups *grouplogic.Service
|
|
Files *uploadlogic.Service
|
|
Favorites *favlogic.Service
|
|
Moments *momentlogic.Service
|
|
Square *squarelogic.Service
|
|
Messages *msglogic.Service
|
|
Sync *synclogic.Service
|
|
Admin *adminlogic.Service
|
|
AdminTokens *adminlogic.TokenService
|
|
Music *musiclogic.Service
|
|
Health Health
|
|
AuthLimiter *ratelimit.Limiter
|
|
}
|
|
|
|
func New(log *zap.Logger, addr string, deps *EngineDeps) *Server {
|
|
if log == nil {
|
|
log = zap.NewNop()
|
|
}
|
|
if deps == nil {
|
|
deps = &EngineDeps{}
|
|
}
|
|
r := NewEngine(log, deps)
|
|
return &Server{
|
|
log: log,
|
|
ready: make(chan struct{}),
|
|
http: &http.Server{
|
|
Addr: addr,
|
|
Handler: r,
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewEngine(log *zap.Logger, deps *EngineDeps) *gin.Engine {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
r := gin.New()
|
|
_ = r.SetTrustedProxies([]string{"127.0.0.1", "::1"})
|
|
if deps == nil {
|
|
deps = &EngineDeps{}
|
|
}
|
|
r.Use(Trace(log), Recovery(log), AccessLog(), AuthRateLimit(deps.AuthLimiter), Auth(deps.Auth))
|
|
registerRoutes(r, deps)
|
|
return r
|
|
}
|
|
|
|
func (s *Server) Start() error {
|
|
ln, err := net.Listen("tcp", s.http.Addr)
|
|
if err != nil {
|
|
s.signalReady()
|
|
return err
|
|
}
|
|
s.listening.Store(true)
|
|
s.signalReady()
|
|
s.log.Info("http listening", zap.String("addr", ln.Addr().String()))
|
|
err = s.http.Serve(ln)
|
|
if err == http.ErrServerClosed {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (s *Server) WaitReady(ctx context.Context) error {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-s.ready:
|
|
if s.listening.Load() {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("http not listening")
|
|
}
|
|
}
|
|
|
|
func (s *Server) signalReady() {
|
|
s.readyOnce.Do(func() { close(s.ready) })
|
|
}
|
|
|
|
func (s *Server) Stop(ctx context.Context) error {
|
|
return s.http.Shutdown(ctx)
|
|
}
|