mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-04 15:53:36 +08:00
Merge pull request #51 from yokowu/feat-user-active
feat(user): 记录用户活跃时间
This commit is contained in:
@@ -57,13 +57,14 @@ func newServer(dir string) (*Server, error) {
|
||||
openAIRepo := repo2.NewOpenAIRepo(client)
|
||||
openAIUsecase := openai.NewOpenAIUsecase(configConfig, openAIRepo, slogLogger)
|
||||
proxyMiddleware := middleware.NewProxyMiddleware(proxyUsecase)
|
||||
v1Handler := v1.NewV1Handler(slogLogger, web, domainProxy, openAIUsecase, proxyMiddleware)
|
||||
redisClient := store.NewRedisCli(configConfig)
|
||||
activeMiddleware := middleware.NewActiveMiddleware(redisClient, slogLogger)
|
||||
v1Handler := v1.NewV1Handler(slogLogger, web, domainProxy, openAIUsecase, proxyMiddleware, activeMiddleware)
|
||||
modelRepo := repo3.NewModelRepo(client)
|
||||
modelUsecase := usecase2.NewModelUsecase(slogLogger, modelRepo, configConfig)
|
||||
sessionSession := session.NewSession(configConfig)
|
||||
authMiddleware := middleware.NewAuthMiddleware(sessionSession, slogLogger)
|
||||
modelHandler := v1_2.NewModelHandler(web, modelUsecase, authMiddleware, slogLogger)
|
||||
redisClient := store.NewRedisCli(configConfig)
|
||||
userRepo := repo4.NewUserRepo(client)
|
||||
userUsecase := usecase3.NewUserUsecase(configConfig, redisClient, userRepo, slogLogger)
|
||||
userHandler := v1_3.NewUserHandler(web, userUsecase, authMiddleware, sessionSession, slogLogger, configConfig)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package consts
|
||||
|
||||
const (
|
||||
UserActiveKeyFmt = "user:active:%s"
|
||||
)
|
||||
|
||||
type UserStatus string
|
||||
|
||||
const (
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.23.7
|
||||
|
||||
require (
|
||||
entgo.io/ent v0.14.4
|
||||
github.com/GoYoko/web v1.1.0
|
||||
github.com/GoYoko/web v1.0.0
|
||||
github.com/golang-migrate/migrate/v4 v4.18.3
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/google/wire v0.6.0
|
||||
|
||||
@@ -8,8 +8,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/GoYoko/web v1.1.0 h1:nIbtol5z0Y03d0nHsvGjv+W0fgmFRGUL8fzPN3kmrOY=
|
||||
github.com/GoYoko/web v1.1.0/go.mod h1:DL9/gvuUG2jcBE1XUIY+9QBrrhdshzPEdxMCzR9jUHo=
|
||||
github.com/GoYoko/web v1.0.0 h1:kcNxz8BvpKavE0/iqatOmUeCXVghaoD5xYDiHDulVaE=
|
||||
github.com/GoYoko/web v1.0.0/go.mod h1:DL9/gvuUG2jcBE1XUIY+9QBrrhdshzPEdxMCzR9jUHo=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
|
||||
37
backend/internal/middleware/active.go
Normal file
37
backend/internal/middleware/active.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/chaitin/MonkeyCode/backend/consts"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type ActiveMiddleware struct {
|
||||
redis *redis.Client
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewActiveMiddleware(redis *redis.Client, logger *slog.Logger) *ActiveMiddleware {
|
||||
return &ActiveMiddleware{
|
||||
redis: redis,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ActiveMiddleware) Active() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if apikey := GetApiKey(c); apikey != nil {
|
||||
if err := a.redis.Set(context.Background(), fmt.Sprintf(consts.UserActiveKeyFmt, apikey.UserID), time.Now().Unix(), 0).Err(); err != nil {
|
||||
a.logger.With("error", err).ErrorContext(c.Request().Context(), "failed to set user active status in Redis")
|
||||
}
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ func NewV1Handler(
|
||||
proxy domain.Proxy,
|
||||
usecase domain.OpenAIUsecase,
|
||||
middleware *middleware.ProxyMiddleware,
|
||||
active *middleware.ActiveMiddleware,
|
||||
) *V1Handler {
|
||||
h := &V1Handler{
|
||||
logger: logger.With(slog.String("handler", "openai")),
|
||||
@@ -35,10 +36,10 @@ func NewV1Handler(
|
||||
|
||||
g := w.Group("/v1", middleware.Auth())
|
||||
g.GET("/models", web.BaseHandler(h.ModelList))
|
||||
g.POST("/completion/accept", web.BindHandler(h.AcceptCompletion))
|
||||
g.POST("/chat/completions", web.BindHandler(h.ChatCompletion))
|
||||
g.POST("/completions", web.BindHandler(h.Completions))
|
||||
g.POST("/embeddings", web.BindHandler(h.Embeddings))
|
||||
g.POST("/completion/accept", web.BindHandler(h.AcceptCompletion), active.Active())
|
||||
g.POST("/chat/completions", web.BindHandler(h.ChatCompletion), active.Active())
|
||||
g.POST("/completions", web.BindHandler(h.Completions), active.Active())
|
||||
g.POST("/embeddings", web.BindHandler(h.Embeddings), active.Active())
|
||||
return h
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ var Provider = wire.NewSet(
|
||||
dashrepo.NewDashboardRepo,
|
||||
middleware.NewProxyMiddleware,
|
||||
middleware.NewAuthMiddleware,
|
||||
middleware.NewActiveMiddleware,
|
||||
userV1.NewUserHandler,
|
||||
userrepo.NewUserRepo,
|
||||
userusecase.NewUserUsecase,
|
||||
|
||||
@@ -629,7 +629,7 @@ func (p *LLMProxy) handleChatCompletionStream(ctx context.Context, w http.Respon
|
||||
"apiBase", m.APIBase,
|
||||
"work_mode", mode,
|
||||
"requestHeader", newReq.Header,
|
||||
"requestBody", newReq,
|
||||
"requestBody", req,
|
||||
"taskID", taskID,
|
||||
"messages", cvt.Filter(req.Messages, func(i int, v openai.ChatCompletionMessage) (openai.ChatCompletionMessage, bool) {
|
||||
return v, v.Role != "system"
|
||||
|
||||
@@ -60,14 +60,36 @@ func (u *UserUsecase) List(ctx context.Context, req domain.ListReq) (*domain.Lis
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids := cvt.Iter(users, func(_ int, u *db.User) string { return u.ID.String() })
|
||||
m, err := u.getUserActive(ctx, ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &domain.ListUserResp{
|
||||
PageInfo: p,
|
||||
Users: cvt.Iter(users, func(_ int, e *db.User) *domain.User {
|
||||
return cvt.From(e, &domain.User{}).From(e)
|
||||
return cvt.From(e, &domain.User{
|
||||
LastActiveAt: m[e.ID.String()],
|
||||
})
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *UserUsecase) getUserActive(ctx context.Context, ids []string) (map[string]int64, error) {
|
||||
m := make(map[string]int64)
|
||||
for _, id := range ids {
|
||||
key := fmt.Sprintf(consts.UserActiveKeyFmt, id)
|
||||
if t, err := u.redis.Get(ctx, key).Int64(); err != nil {
|
||||
u.logger.With("key", key).With("error", err).Warn("get user active time failed")
|
||||
} else {
|
||||
m[id] = t
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// AdminList implements domain.UserUsecase.
|
||||
func (u *UserUsecase) AdminList(ctx context.Context, page *web.Pagination) (*domain.ListAdminUserResp, error) {
|
||||
admins, p, err := u.repo.AdminList(ctx, page)
|
||||
|
||||
Reference in New Issue
Block a user