mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-04 07:43:28 +08:00
786 lines
22 KiB
Go
786 lines
22 KiB
Go
package v1
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"crypto/md5"
|
||
"fmt"
|
||
"log/slog"
|
||
"net/http"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/GoYoko/web"
|
||
"golang.org/x/time/rate"
|
||
|
||
"github.com/chaitin/MonkeyCode/backend/config"
|
||
"github.com/chaitin/MonkeyCode/backend/consts"
|
||
"github.com/chaitin/MonkeyCode/backend/domain"
|
||
"github.com/chaitin/MonkeyCode/backend/errcode"
|
||
"github.com/chaitin/MonkeyCode/backend/internal/middleware"
|
||
"github.com/chaitin/MonkeyCode/backend/pkg/session"
|
||
"github.com/chaitin/MonkeyCode/backend/pkg/version"
|
||
"github.com/chaitin/MonkeyCode/backend/pkg/vsix"
|
||
)
|
||
|
||
// CacheEntry 缓存条目
|
||
type CacheEntry struct {
|
||
data []byte
|
||
createdAt time.Time
|
||
}
|
||
|
||
type UserHandler struct {
|
||
usecase domain.UserUsecase
|
||
euse domain.ExtensionUsecase
|
||
duse domain.DashboardUsecase
|
||
buse domain.BillingUsecase
|
||
scuse domain.SecurityScanningUsecase
|
||
session *session.Session
|
||
logger *slog.Logger
|
||
cfg *config.Config
|
||
vsixCache map[string]*CacheEntry
|
||
zipCache map[string]*CacheEntry
|
||
cacheMu sync.RWMutex
|
||
limiter *rate.Limiter
|
||
}
|
||
|
||
func NewUserHandler(
|
||
w *web.Web,
|
||
usecase domain.UserUsecase,
|
||
euse domain.ExtensionUsecase,
|
||
scuse domain.SecurityScanningUsecase,
|
||
duse domain.DashboardUsecase,
|
||
buse domain.BillingUsecase,
|
||
auth *middleware.AuthMiddleware,
|
||
active *middleware.ActiveMiddleware,
|
||
readonly *middleware.ReadOnlyMiddleware,
|
||
session *session.Session,
|
||
logger *slog.Logger,
|
||
cfg *config.Config,
|
||
) *UserHandler {
|
||
u := &UserHandler{
|
||
usecase: usecase,
|
||
euse: euse,
|
||
duse: duse,
|
||
buse: buse,
|
||
scuse: scuse,
|
||
session: session,
|
||
logger: logger,
|
||
cfg: cfg,
|
||
vsixCache: make(map[string]*CacheEntry),
|
||
zipCache: make(map[string]*CacheEntry),
|
||
limiter: rate.NewLimiter(rate.Every(time.Duration(cfg.Extension.LimitSecond)*time.Second), cfg.Extension.Limit),
|
||
}
|
||
|
||
w.GET("/api/v1/static/vsix/:version", web.BaseHandler(u.VSIXDownload))
|
||
w.GET("/api/v1/static/vsix", web.BaseHandler(u.VSIXDownload))
|
||
w.GET("/api/v1/static/jetbrains/:version", web.BaseHandler(u.ZipDownload))
|
||
w.GET("/api/v1/static/jetbrains", web.BaseHandler(u.ZipDownload))
|
||
w.POST("/api/v1/vscode/init-auth", web.BindHandler(u.VSCodeAuthInit))
|
||
|
||
// admin
|
||
admin := w.Group("/api/v1/admin")
|
||
admin.POST("/login", web.BindHandler(u.AdminLogin))
|
||
admin.GET("/setting", web.BaseHandler(u.GetSetting))
|
||
admin.GET("/role", web.BaseHandler(u.ListRole))
|
||
|
||
admin.Use(auth.Auth(), active.Active("admin"), readonly.Guard())
|
||
admin.GET("/profile", web.BaseHandler(u.AdminProfile))
|
||
admin.GET("/list", web.BaseHandler(u.AdminList, web.WithPage()))
|
||
admin.GET("/login-history", web.BaseHandler(u.AdminLoginHistory, web.WithPage()))
|
||
admin.PUT("/setting", web.BindHandler(u.UpdateSetting))
|
||
admin.POST("/create", web.BindHandler(u.CreateAdmin))
|
||
admin.POST("/logout", web.BaseHandler(u.AdminLogout))
|
||
admin.DELETE("/delete", web.BaseHandler(u.DeleteAdmin))
|
||
admin.GET("/export-completion-data", web.BaseHandler(u.ExportCompletionData))
|
||
admin.POST("/role", web.BindHandler(u.GrantRole))
|
||
|
||
// user
|
||
g := w.Group("/api/v1/user")
|
||
g.GET("/oauth/signup-or-in", web.BindHandler(u.OAuthSignUpOrIn))
|
||
g.GET("/oauth/callback", web.BindHandler(u.OAuthCallback))
|
||
g.POST("/register", web.BindHandler(u.Register))
|
||
g.POST("/login", web.BindHandler(u.Login))
|
||
|
||
g.Use(readonly.Guard())
|
||
g.GET("/profile", web.BaseHandler(u.Profile), auth.UserAuth())
|
||
g.PUT("/profile", web.BindHandler(u.UpdateProfile), auth.UserAuth())
|
||
g.POST("/logout", web.BaseHandler(u.Logout), auth.UserAuth())
|
||
|
||
g.Use(auth.Auth(), active.Active("admin"))
|
||
|
||
g.PUT("/update", web.BindHandler(u.Update))
|
||
g.DELETE("/delete", web.BaseHandler(u.Delete))
|
||
g.GET("/invite", web.BaseHandler(u.Invite))
|
||
g.GET("/list", web.BindHandler(u.List, web.WithPage()))
|
||
g.GET("/login-history", web.BaseHandler(u.LoginHistory, web.WithPage()))
|
||
|
||
// user dashboard
|
||
d := w.Group("/api/v1/user/dashboard")
|
||
d.Use(auth.UserAuth(), active.Active("user"))
|
||
d.GET("/stat", web.BindHandler(u.UserStat))
|
||
d.GET("/events", web.BaseHandler(u.UserEvents))
|
||
d.GET("/heatmap", web.BaseHandler(u.UserHeatmap))
|
||
|
||
// user record
|
||
uc := w.Group("/api/v1/user/chat")
|
||
uc.Use(auth.UserAuth(), active.Active("user"))
|
||
uc.GET("/record", web.BindHandler(u.ListChatRecord, web.WithPage()))
|
||
uc.GET("/info", web.BaseHandler(u.ChatInfo))
|
||
|
||
cplt := w.Group("/api/v1/user/completion")
|
||
cplt.Use(auth.UserAuth(), active.Active("user"))
|
||
cplt.GET("/record", web.BindHandler(u.ListCompletionRecord, web.WithPage()))
|
||
cplt.GET("/info", web.BaseHandler(u.CompletionInfo))
|
||
|
||
// user security
|
||
sc := w.Group("/api/v1/user/security")
|
||
sc.Use(auth.UserAuth(), active.Active("user"))
|
||
sc.GET("/scanning", web.BindHandler(u.SecurityList, web.WithPage()))
|
||
sc.GET("/scanning/detail", web.BaseHandler(u.SecurityDetail))
|
||
|
||
return u
|
||
}
|
||
|
||
func (h *UserHandler) VSCodeAuthInit(c *web.Context, req domain.VSCodeAuthInitReq) error {
|
||
s, err := h.usecase.GetSetting(c.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
req.BaseURL = h.cfg.GetBaseURL(c.Request(), s)
|
||
resp, err := h.usecase.VSCodeAuthInit(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.JSON(http.StatusOK, resp)
|
||
}
|
||
|
||
// generateCacheKey 生成缓存键
|
||
func (h *UserHandler) generateCacheKey(version, baseUrl string) string {
|
||
hash := md5.Sum([]byte(version + ":" + baseUrl))
|
||
return fmt.Sprintf("%x", hash)
|
||
}
|
||
|
||
// cleanExpiredCache 清理过期缓存
|
||
func (h *UserHandler) cleanExpiredCache() {
|
||
h.cacheMu.Lock()
|
||
defer h.cacheMu.Unlock()
|
||
|
||
now := time.Now()
|
||
for key, entry := range h.vsixCache {
|
||
// 缓存1小时后过期
|
||
if now.Sub(entry.createdAt) > time.Hour {
|
||
delete(h.vsixCache, key)
|
||
}
|
||
}
|
||
if h.zipCache != nil {
|
||
for key, entry := range h.zipCache {
|
||
if now.Sub(entry.createdAt) > time.Hour {
|
||
delete(h.zipCache, key)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// VSIXDownload 下载VSCode插件
|
||
//
|
||
// @Tags User
|
||
// @Summary 下载VSCode插件
|
||
// @Description 下载VSCode插件
|
||
// @ID vsix-download
|
||
// @Accept json
|
||
// @Produce octet-stream
|
||
// @Router /api/v1/static/vsix [get]
|
||
func (h *UserHandler) VSIXDownload(c *web.Context) error {
|
||
if !h.limiter.Allow() {
|
||
return c.String(http.StatusTooManyRequests, "Too Many Requests")
|
||
}
|
||
|
||
s, err := h.usecase.GetSetting(c.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
host := c.Request().Host
|
||
h.logger.With("url", c.Request().URL).With("header", c.Request().Header).With("host", host).DebugContext(c.Request().Context(), "vsix download")
|
||
cacheKey := h.generateCacheKey(version.Version, h.cfg.GetBaseURL(c.Request(), s))
|
||
version := strings.Trim(version.Version, "v")
|
||
|
||
h.cacheMu.RLock()
|
||
if entry, exists := h.vsixCache[cacheKey]; exists {
|
||
if time.Since(entry.createdAt) < time.Hour {
|
||
h.cacheMu.RUnlock()
|
||
|
||
disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.vsix", version)
|
||
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
||
c.Response().Header().Set("Content-Disposition", disposition)
|
||
c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(entry.data)))
|
||
|
||
_, err := c.Response().Writer.Write(entry.data)
|
||
return err
|
||
}
|
||
}
|
||
h.cacheMu.RUnlock()
|
||
|
||
var buf bytes.Buffer
|
||
if err := vsix.ChangeVsixEndpoint(fmt.Sprintf("/app/assets/vsix/monkeycode-%s.vsix", version), "extension/package.json", h.cfg.GetBaseURL(c.Request(), s), &buf); err != nil {
|
||
return err
|
||
}
|
||
|
||
data := buf.Bytes()
|
||
h.cacheMu.Lock()
|
||
h.vsixCache[cacheKey] = &CacheEntry{
|
||
data: data,
|
||
createdAt: time.Now(),
|
||
}
|
||
h.cacheMu.Unlock()
|
||
|
||
go h.cleanExpiredCache()
|
||
|
||
disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.vsix", version)
|
||
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
||
c.Response().Header().Set("Content-Disposition", disposition)
|
||
c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
|
||
|
||
_, err = c.Response().Writer.Write(data)
|
||
return err
|
||
}
|
||
|
||
// ZipDownload 下载ZIP包(Jetbrains插件包)
|
||
//
|
||
// @Tags User
|
||
// @Summary 下载ZIP包
|
||
// @Description 下载ZIP安装包(缓存1小时)
|
||
// @ID zip-download
|
||
// @Accept json
|
||
// @Produce octet-stream
|
||
// @Router /api/v1/static/jetbrains [get]
|
||
func (h *UserHandler) ZipDownload(c *web.Context) error {
|
||
if !h.limiter.Allow() {
|
||
return c.String(http.StatusTooManyRequests, "Too Many Requests")
|
||
}
|
||
|
||
s, err := h.usecase.GetSetting(c.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
host := c.Request().Host
|
||
h.logger.With("url", c.Request().URL).With("header", c.Request().Header).With("host", host).DebugContext(c.Request().Context(), "zip download")
|
||
cacheKey := h.generateCacheKey(version.Version, h.cfg.GetBaseURL(c.Request(), s))
|
||
ver := strings.Trim(version.Version, "v")
|
||
|
||
// 缓存命中
|
||
h.cacheMu.RLock()
|
||
if entry, exists := h.zipCache[cacheKey]; exists {
|
||
if time.Since(entry.createdAt) < time.Hour {
|
||
h.cacheMu.RUnlock()
|
||
|
||
disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.zip", ver)
|
||
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
||
c.Response().Header().Set("Content-Disposition", disposition)
|
||
c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(entry.data)))
|
||
|
||
fmt.Println(c.Response().Header())
|
||
|
||
_, werr := c.Response().Writer.Write(entry.data)
|
||
return werr
|
||
}
|
||
}
|
||
h.cacheMu.RUnlock()
|
||
|
||
var buf bytes.Buffer
|
||
if err := vsix.ChangeVsixEndpoint(fmt.Sprintf("/app/assets/jetbrains/monkeycode-%s.zip", ver), "roo-code/package.json", h.cfg.GetBaseURL(c.Request(), s), &buf); err != nil {
|
||
return err
|
||
}
|
||
|
||
data := buf.Bytes()
|
||
h.cacheMu.Lock()
|
||
h.zipCache[cacheKey] = &CacheEntry{
|
||
data: data,
|
||
createdAt: time.Now(),
|
||
}
|
||
h.cacheMu.Unlock()
|
||
|
||
// 异步清理
|
||
go h.cleanExpiredCache()
|
||
|
||
// 响应输出
|
||
disposition := fmt.Sprintf("attachment; filename=monkeycode-%s.zip", ver)
|
||
c.Response().Header().Set("Content-Type", "application/octet-stream")
|
||
c.Response().Header().Set("Content-Disposition", disposition)
|
||
c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
|
||
|
||
_, err = c.Response().Writer.Write(data)
|
||
return err
|
||
}
|
||
|
||
// Login 用户登录
|
||
//
|
||
// @Tags User
|
||
// @Summary 用户登录
|
||
// @Description 用户登录
|
||
// @ID login
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.LoginReq true "登录参数"
|
||
// @Success 200 {object} web.Resp{data=domain.LoginResp}
|
||
// @Router /api/v1/user/login [post]
|
||
func (h *UserHandler) Login(c *web.Context, req domain.LoginReq) error {
|
||
req.IP = c.RealIP()
|
||
resp, err := h.usecase.Login(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if req.Source == consts.LoginSourceBrowser {
|
||
if _, err := h.session.Save(c, consts.UserSessionName, c.Request().Host, resp.User); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// Logout 用户登出
|
||
//
|
||
// @Tags User
|
||
// @Summary 用户登出
|
||
// @Description 用户登出
|
||
// @ID logout
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{}
|
||
// @Router /api/v1/user/logout [post]
|
||
func (h *UserHandler) Logout(c *web.Context) error {
|
||
if err := h.session.Del(c, consts.UserSessionName); err != nil {
|
||
return err
|
||
}
|
||
return c.Success(nil)
|
||
}
|
||
|
||
// Update 更新用户
|
||
//
|
||
// @Tags User
|
||
// @Summary 更新用户
|
||
// @Description 更新用户
|
||
// @ID update-user
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.UpdateUserReq true "更新用户参数"
|
||
// @Success 200 {object} web.Resp{data=domain.User}
|
||
// @Router /api/v1/user/update [put]
|
||
func (h *UserHandler) Update(c *web.Context, req domain.UpdateUserReq) error {
|
||
resp, err := h.usecase.Update(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// Delete 删除用户
|
||
//
|
||
// @Tags User
|
||
// @Summary 删除用户
|
||
// @Description 删除用户
|
||
// @ID delete-user
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param id query string true "用户ID"
|
||
// @Success 200 {object} web.Resp{data=nil}
|
||
// @Router /api/v1/user/delete [delete]
|
||
func (h *UserHandler) Delete(c *web.Context) error {
|
||
err := h.usecase.Delete(c.Request().Context(), c.QueryParam("id"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(nil)
|
||
}
|
||
|
||
// DeleteAdmin 删除管理员
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 删除管理员
|
||
// @Description 删除管理员
|
||
// @ID delete-admin
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param id query string true "管理员ID"
|
||
// @Success 200 {object} web.Resp{data=nil}
|
||
// @Router /api/v1/admin/delete [delete]
|
||
func (h *UserHandler) DeleteAdmin(c *web.Context) error {
|
||
err := h.usecase.DeleteAdmin(c.Request().Context(), c.QueryParam("id"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(nil)
|
||
}
|
||
|
||
// AdminLogin 管理员登录
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 管理员登录
|
||
// @Description 管理员登录
|
||
// @ID admin-login
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.LoginReq true "登录参数"
|
||
// @Success 200 {object} web.Resp{data=domain.AdminUser}
|
||
// @Router /api/v1/admin/login [post]
|
||
func (h *UserHandler) AdminLogin(c *web.Context, req domain.LoginReq) error {
|
||
req.IP = c.RealIP()
|
||
resp, err := h.usecase.AdminLogin(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
h.logger.With("header", c.Request().Header).With("host", c.Request().Host).Info("admin login", "username", resp.Username)
|
||
if _, err := h.session.Save(c, consts.SessionName, c.Request().Host, resp); err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// AdminLogout 管理员登出
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 管理员登出
|
||
// @Description 管理员登出
|
||
// @ID admin-logout
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{}
|
||
// @Router /api/v1/admin/logout [post]
|
||
func (h *UserHandler) AdminLogout(c *web.Context) error {
|
||
if err := h.session.Del(c, consts.SessionName); err != nil {
|
||
return err
|
||
}
|
||
return c.Success(nil)
|
||
}
|
||
|
||
// AdminProfile 管理员信息
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 管理员信息
|
||
// @Description 管理员信息
|
||
// @ID admin-profile
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{data=domain.AdminUser}
|
||
// @Router /api/v1/admin/profile [get]
|
||
func (h *UserHandler) AdminProfile(c *web.Context) error {
|
||
user := middleware.GetAdmin(c)
|
||
return c.Success(user)
|
||
}
|
||
|
||
// List 获取用户列表
|
||
//
|
||
// @Tags User
|
||
// @Summary 获取用户列表
|
||
// @Description 获取用户列表
|
||
// @ID list-user
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param page query web.Pagination true "分页"
|
||
// @Success 200 {object} web.Resp{data=domain.ListUserResp}
|
||
// @Router /api/v1/user/list [get]
|
||
func (h *UserHandler) List(c *web.Context, req domain.ListReq) error {
|
||
resp, err := h.usecase.List(c.Request().Context(), req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// LoginHistory 获取用户登录历史
|
||
//
|
||
// @Tags User
|
||
// @Summary 获取用户登录历史
|
||
// @Description 获取用户登录历史
|
||
// @ID login-history
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param page query web.Pagination true "分页"
|
||
// @Success 200 {object} web.Resp{data=domain.ListLoginHistoryResp}
|
||
// @Router /api/v1/user/login-history [get]
|
||
func (h *UserHandler) LoginHistory(c *web.Context) error {
|
||
resp, err := h.usecase.LoginHistory(c.Request().Context(), c.Page())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// Invite 获取用户邀请码
|
||
//
|
||
// @Tags User
|
||
// @Summary 获取用户邀请码
|
||
// @Description 获取用户邀请码
|
||
// @ID invite
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{data=domain.InviteResp}
|
||
// @Router /api/v1/user/invite [get]
|
||
func (h *UserHandler) Invite(c *web.Context) error {
|
||
admin := middleware.GetAdmin(c)
|
||
resp, err := h.usecase.Invite(c.Request().Context(), admin.ID.String())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// Register 注册用户
|
||
//
|
||
// @Tags User
|
||
// @Summary 注册用户
|
||
// @Description 注册用户
|
||
// @ID register
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.RegisterReq true "注册参数"
|
||
// @Success 200 {object} web.Resp{data=domain.User}
|
||
// @Router /api/v1/user/register [post]
|
||
func (h *UserHandler) Register(c *web.Context, req domain.RegisterReq) error {
|
||
resp, err := h.usecase.Register(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// CreateAdmin 创建管理员
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 创建管理员
|
||
// @Description 创建管理员
|
||
// @ID create-admin
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.CreateAdminReq true "创建管理员参数"
|
||
// @Success 200 {object} web.Resp{data=domain.AdminUser}
|
||
// @Router /api/v1/admin/create [post]
|
||
func (h *UserHandler) CreateAdmin(c *web.Context, req domain.CreateAdminReq) error {
|
||
user := middleware.GetAdmin(c)
|
||
if user.Username != "admin" {
|
||
return errcode.ErrPermission
|
||
}
|
||
resp, err := h.usecase.CreateAdmin(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// AdminList 获取管理员用户列表
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 获取管理员用户列表
|
||
// @Description 获取管理员用户列表
|
||
// @ID list-admin-user
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param page query web.Pagination true "分页"
|
||
// @Success 200 {object} web.Resp{data=domain.ListAdminUserResp}
|
||
// @Router /api/v1/admin/list [get]
|
||
func (h *UserHandler) AdminList(c *web.Context) error {
|
||
resp, err := h.usecase.AdminList(c.Request().Context(), c.Page())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// AdminLoginHistory 获取管理员登录历史
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 获取管理员登录历史
|
||
// @Description 获取管理员登录历史
|
||
// @ID admin-login-history
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param page query web.Pagination true "分页"
|
||
// @Success 200 {object} web.Resp{data=domain.ListAdminLoginHistoryResp}
|
||
// @Router /api/v1/admin/login-history [get]
|
||
func (h *UserHandler) AdminLoginHistory(c *web.Context) error {
|
||
resp, err := h.usecase.AdminLoginHistory(c.Request().Context(), c.Page())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// ListRole 获取系统角色列表
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 获取角色列表
|
||
// @Description 获取角色列表
|
||
// @ID list-role
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{data=[]domain.Role}
|
||
// @Router /api/v1/admin/role [get]
|
||
func (h *UserHandler) ListRole(c *web.Context) error {
|
||
roles, err := h.usecase.ListRole(c.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(roles)
|
||
}
|
||
|
||
// GrantRole 授权角色
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 授权角色
|
||
// @Description 授权角色
|
||
// @ID grant-role
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.GrantRoleReq true "授权角色参数"
|
||
// @Success 200 {object} web.Resp
|
||
// @Router /api/v1/admin/role [post]
|
||
func (h *UserHandler) GrantRole(c *web.Context, req domain.GrantRoleReq) error {
|
||
if err := h.usecase.GrantRole(c.Request().Context(), &req); err != nil {
|
||
return err
|
||
}
|
||
return c.Success(nil)
|
||
}
|
||
|
||
// GetSetting 获取系统设置
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 获取系统设置
|
||
// @Description 获取系统设置
|
||
// @ID get-setting
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{data=domain.Setting}
|
||
// @Router /api/v1/admin/setting [get]
|
||
func (h *UserHandler) GetSetting(c *web.Context) error {
|
||
resp, err := h.usecase.GetSetting(c.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// UpdateSetting 更新系统设置
|
||
//
|
||
// @Tags Admin
|
||
// @Summary 更新系统设置
|
||
// @Description 更新为增量更新,只传需要更新的字段
|
||
// @ID update-setting
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param param body domain.UpdateSettingReq true "更新系统设置参数"
|
||
// @Success 200 {object} web.Resp{data=domain.Setting}
|
||
// @Router /api/v1/admin/setting [put]
|
||
func (h *UserHandler) UpdateSetting(c *web.Context, req domain.UpdateSettingReq) error {
|
||
resp, err := h.usecase.UpdateSetting(c.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.Success(resp)
|
||
}
|
||
|
||
// OAuthSignUpOrIn 用户 OAuth 登录或注册
|
||
//
|
||
// @Tags User
|
||
// @Summary 用户 OAuth 登录或注册
|
||
// @Description 用户 OAuth 登录或注册
|
||
// @ID user-oauth-signup-or-in
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param req query domain.OAuthSignUpOrInReq true "param"
|
||
// @Success 200 {object} web.Resp{data=domain.OAuthURLResp}
|
||
// @Router /api/v1/user/oauth/signup-or-in [get]
|
||
func (h *UserHandler) OAuthSignUpOrIn(ctx *web.Context, req domain.OAuthSignUpOrInReq) error {
|
||
h.logger.With("req", req).DebugContext(ctx.Request().Context(), "OAuthSignUpOrIn")
|
||
s, err := h.usecase.GetSetting(ctx.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
req.BaseURL = h.cfg.GetBaseURL(ctx.Request(), s)
|
||
resp, err := h.usecase.OAuthSignUpOrIn(ctx.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ctx.Success(resp)
|
||
}
|
||
|
||
// OAuthCallback 用户 OAuth 回调
|
||
//
|
||
// @Tags User
|
||
// @Summary 用户 OAuth 回调
|
||
// @Description 用户 OAuth 回调
|
||
// @ID user-oauth-callback
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param req query domain.OAuthCallbackReq true "param"
|
||
// @Success 200 {object} web.Resp{data=string}
|
||
// @Router /api/v1/user/oauth/callback [get]
|
||
func (h *UserHandler) OAuthCallback(ctx *web.Context, req domain.OAuthCallbackReq) error {
|
||
return h.usecase.OAuthCallback(ctx, &req)
|
||
}
|
||
|
||
// Profile 获取用户信息
|
||
//
|
||
// @Tags User Manage
|
||
// @Summary 获取用户信息
|
||
// @Description 获取用户信息
|
||
// @ID user-profile
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} web.Resp{data=domain.User}
|
||
// @Failure 401 {object} web.Resp{}
|
||
// @Router /api/v1/user/profile [get]
|
||
func (h *UserHandler) Profile(ctx *web.Context) error {
|
||
return ctx.Success(middleware.GetUser(ctx))
|
||
}
|
||
|
||
// UpdateProfile 更新用户信息
|
||
//
|
||
// @Tags User Manage
|
||
// @Summary 更新用户信息
|
||
// @Description 更新用户信息
|
||
// @ID user-update-profile
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param req body domain.ProfileUpdateReq true "param"
|
||
// @Success 200 {object} web.Resp{data=domain.User}
|
||
// @Failure 401 {object} web.Resp{}
|
||
// @Router /api/v1/user/profile [put]
|
||
func (h *UserHandler) UpdateProfile(ctx *web.Context, req domain.ProfileUpdateReq) error {
|
||
req.UID = middleware.GetUser(ctx).ID
|
||
user, err := h.usecase.ProfileUpdate(ctx.Request().Context(), &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ctx.Success(user)
|
||
}
|
||
|
||
func (h *UserHandler) InitAdmin() error {
|
||
return h.usecase.InitAdmin(context.Background())
|
||
}
|
||
|
||
// ExportCompletionData godoc
|
||
//
|
||
// @Summary 导出补全数据
|
||
// @Description 管理员导出所有补全相关数据
|
||
// @Tags admin
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Security ApiKeyAuth
|
||
// @Success 200 {object} domain.ExportCompletionDataResp
|
||
// @Failure 401 {object} web.Resp{}
|
||
// @Failure 500 {object} web.Resp{}
|
||
// @Router /api/v1/admin/export-completion-data [get]
|
||
func (h *UserHandler) ExportCompletionData(c *web.Context) error {
|
||
data, err := h.usecase.ExportCompletionData(c.Request().Context())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return c.JSON(http.StatusOK, data)
|
||
}
|