功能更新
This commit is contained in:
244
internal/controller/admin.go
Normal file
244
internal/controller/admin.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"nl-pms-api/internal/commonservice"
|
||||
"nl-pms-api/internal/middleware"
|
||||
"nl-pms-api/internal/service"
|
||||
)
|
||||
|
||||
// AdminController 运营后台。
|
||||
type AdminController struct {
|
||||
Admin *service.AdminService
|
||||
Sec *service.AdminSecurityService
|
||||
Rel *service.ReleaseService
|
||||
Act *service.ActivityService
|
||||
}
|
||||
|
||||
func (h *AdminController) Overview(c *gin.Context) {
|
||||
days, _ := strconv.Atoi(c.DefaultQuery("days", "14"))
|
||||
out, err := h.Admin.Overview(days)
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (h *AdminController) ListUsers(c *gin.Context) {
|
||||
items, err := h.Admin.ListUsers()
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminController) PatchUser(c *gin.Context) {
|
||||
id := commonservice.ParseID(c.Param("id"))
|
||||
var req struct {
|
||||
AIBanned *int `json:"aiBanned"`
|
||||
Disabled *int `json:"disabled"`
|
||||
}
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := h.Admin.PatchUser(commonservice.UserID(c), id, req.AIBanned, req.Disabled); err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ok": true})
|
||||
}
|
||||
|
||||
func (h *AdminController) ListTeams(c *gin.Context) {
|
||||
items, err := h.Admin.ListTeams()
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminController) PatchTeam(c *gin.Context) {
|
||||
id := commonservice.ParseID(c.Param("id"))
|
||||
var req struct {
|
||||
AIBanned *int `json:"aiBanned"`
|
||||
}
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := h.Admin.PatchTeam(commonservice.UserID(c), id, req.AIBanned); err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ok": true})
|
||||
}
|
||||
|
||||
func (h *AdminController) TOTPStatus(c *gin.Context) {
|
||||
out, err := h.Sec.Status(commonservice.UserID(c))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (h *AdminController) TOTPSetupBegin(c *gin.Context) {
|
||||
out, err := h.Sec.SetupBegin(commonservice.UserID(c))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (h *AdminController) TOTPSetupConfirm(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := h.Sec.SetupConfirm(commonservice.UserID(c), req.Code); err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ok": true})
|
||||
}
|
||||
|
||||
func (h *AdminController) StepUp(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
token, exp, err := h.Sec.StepUp(commonservice.UserID(c), req.Code, middleware.ClientIP(c))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"stepupToken": token,
|
||||
"expiresAt": exp.UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AdminController) ListReleases(c *gin.Context) {
|
||||
items, err := h.Rel.List(c.Query("channel"))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"items": items})
|
||||
}
|
||||
|
||||
func (h *AdminController) UploadRelease(c *gin.Context) {
|
||||
version := c.PostForm("version")
|
||||
channel := c.PostForm("channel")
|
||||
changelog := c.PostForm("changelog")
|
||||
fh, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "FILE_REQUIRED"})
|
||||
return
|
||||
}
|
||||
f, err := fh.Open()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "FILE_REQUIRED"})
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
row, err := h.Rel.Upload(version, channel, changelog, f, fh.Size)
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, row)
|
||||
}
|
||||
|
||||
func (h *AdminController) PublishRelease(c *gin.Context) {
|
||||
id := commonservice.ParseID(c.Param("id"))
|
||||
row, err := h.Rel.Publish(id)
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, row)
|
||||
}
|
||||
|
||||
// ActivityController 日活心跳。
|
||||
type ActivityController struct {
|
||||
Svc *service.ActivityService
|
||||
}
|
||||
|
||||
func (h *ActivityController) Ping(c *gin.Context) {
|
||||
if err := h.Svc.Ping(commonservice.UserID(c), middleware.ClientIP(c)); err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ok": true})
|
||||
}
|
||||
|
||||
// AIController AI 策略与用量上报。
|
||||
type AIController struct {
|
||||
Admin *service.AdminService
|
||||
}
|
||||
|
||||
func (h *AIController) Policy(c *gin.Context) {
|
||||
out, err := h.Admin.GetAIPolicy(commonservice.UserID(c))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (h *AIController) Usage(c *gin.Context) {
|
||||
var req struct {
|
||||
Provider string `json:"provider"`
|
||||
TeamID int64 `json:"teamId"`
|
||||
PromptTokens int64 `json:"promptTokens"`
|
||||
CompletionTokens int64 `json:"completionTokens"`
|
||||
Estimated bool `json:"estimated"`
|
||||
}
|
||||
if !bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
if err := h.Admin.ReportAIUsage(commonservice.UserID(c), req.TeamID, req.Provider, req.PromptTokens, req.CompletionTokens, req.Estimated); err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ok": true})
|
||||
}
|
||||
|
||||
// AppController 客户端更新检查/下载。
|
||||
type AppController struct {
|
||||
Rel *service.ReleaseService
|
||||
}
|
||||
|
||||
func (h *AppController) Latest(c *gin.Context) {
|
||||
out, err := h.Rel.Latest(c.DefaultQuery("channel", "stable"))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func (h *AppController) Download(c *gin.Context) {
|
||||
row, f, err := h.Rel.OpenFile(c.Param("version"), c.DefaultQuery("channel", "stable"))
|
||||
if err != nil {
|
||||
writeErr(c, err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
c.Header("Content-Disposition", `attachment; filename="`+row.Version+`-installer.exe"`)
|
||||
c.Header("X-Content-SHA256", row.SHA256)
|
||||
http.ServeContent(c.Writer, c.Request, row.Version+"-installer.exe", time.Time{}, f)
|
||||
}
|
||||
Reference in New Issue
Block a user