mirror of
https://github.com/veops/oneterm.git
synced 2025-09-27 03:36:02 +08:00
301 lines
8.1 KiB
Go
301 lines
8.1 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/samber/lo"
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
"github.com/veops/oneterm/pkg/server/auth/acl"
|
|
"github.com/veops/oneterm/pkg/server/model"
|
|
"github.com/veops/oneterm/pkg/server/storage/cache/redis"
|
|
"github.com/veops/oneterm/pkg/server/storage/db/mysql"
|
|
)
|
|
|
|
// StatAssetType godoc
|
|
//
|
|
// @Tags stat
|
|
// @Success 200 {object} HttpResponse{data=ListData{list=[]model.StatAssetType}}
|
|
// @Router /stat/assettype [get]
|
|
func (c *Controller) StatAssetType(ctx *gin.Context) {
|
|
stat := make([]*model.StatAssetType, 0)
|
|
key := "stat-assettype"
|
|
if redis.Get(ctx, key, stat) == nil {
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
return
|
|
}
|
|
|
|
t := mysql.DB.
|
|
Model(&model.Asset{}).
|
|
Raw(`
|
|
WITH RECURSIVE cte AS(
|
|
SELECT parent_id
|
|
FROM asset
|
|
WHERE deleted_at = 0
|
|
UNION ALL
|
|
SELECT t.parent_id
|
|
FROM cte
|
|
INNER JOIN node t on cte.parent_id = t.id
|
|
WHERE deleted_at = 0
|
|
)
|
|
SELECT
|
|
parent_id,
|
|
COUNT(*) AS count
|
|
FROM cte
|
|
GROUP BY parent_id
|
|
`)
|
|
|
|
err := mysql.DB.
|
|
Model(&model.Node{}).
|
|
Select("node.name, t.count").
|
|
Joins("LEFT JOIN (?) t ON node.id = t.parent_id", t).
|
|
Where("node.parent_id = 0").
|
|
Find(&stat).
|
|
Error
|
|
if err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
redis.SetEx(ctx, key, stat, time.Minute)
|
|
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
}
|
|
|
|
// StatCount godoc
|
|
//
|
|
// @Tags stat
|
|
// @Success 200 {object} HttpResponse{data=model.StatCount}
|
|
// @Router /stat/count [get]
|
|
func (c *Controller) StatCount(ctx *gin.Context) {
|
|
stat := &model.StatCount{}
|
|
key := "stat-count"
|
|
if redis.Get(ctx, key, stat) == nil {
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(stat))
|
|
return
|
|
}
|
|
|
|
eg := &errgroup.Group{}
|
|
eg.Go(func() error {
|
|
return mysql.DB.
|
|
Model(&model.Session{}).
|
|
Select("COUNT(DISTINCT asset_id, account_id) as connect, COUNT(DISTINCT uid) as user, COUNT(DISTINCT gateway_id) as gateway, COUNT(*) as session").
|
|
Where("status = 1").
|
|
First(&stat).
|
|
Error
|
|
})
|
|
eg.Go(func() error {
|
|
return mysql.DB.Model(&model.Asset{}).Count(&stat.TotalAsset).Error
|
|
})
|
|
eg.Go(func() error {
|
|
return mysql.DB.Model(&model.Asset{}).Where("connectable = 1").Count(&stat.Asset).Error
|
|
})
|
|
eg.Go(func() error {
|
|
return mysql.DB.Model(&model.Gateway{}).Count(&stat.TotalGateway).Error
|
|
})
|
|
|
|
if err := eg.Wait(); err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
stat.Gateway = lo.Ternary(stat.Gateway <= stat.TotalGateway, stat.Gateway, stat.TotalGateway)
|
|
|
|
redis.SetEx(ctx, key, stat, time.Minute)
|
|
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(stat))
|
|
}
|
|
|
|
// StatAccount godoc
|
|
//
|
|
// @Tags stat
|
|
// @Param type query string true "account name" Enums(day, week, month)
|
|
// @Success 200 {object} HttpResponse{data=ListData{list=[]model.StatAccount}}
|
|
// @Router /stat/account [get]
|
|
func (c *Controller) StatAccount(ctx *gin.Context) {
|
|
start, end := time.Now(), time.Now()
|
|
switch ctx.Query("type") {
|
|
case "day":
|
|
start = start.Add(-time.Hour * 24)
|
|
case "week":
|
|
start = start.Add(-time.Hour * 24 * 7)
|
|
case "month":
|
|
start = start.Add(-time.Hour * 24 * 30)
|
|
default:
|
|
ctx.AbortWithError(http.StatusBadRequest, fmt.Errorf("wrong time range %s", ctx.Query("type")))
|
|
return
|
|
}
|
|
|
|
stat := make([]*model.StatAccount, 0)
|
|
key := "stat-account-" + ctx.Query("type")
|
|
if redis.Get(ctx, key, stat) == nil {
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
return
|
|
}
|
|
|
|
err := mysql.DB.
|
|
Model(&model.Account{}).
|
|
Select("account.name, COUNT(*) AS count").
|
|
Joins("LEFT JOIN session ON account.id = session.account_id").
|
|
Group("account.id").
|
|
Order("count DESC").
|
|
Limit(10).
|
|
Where("session.created_at >= ? AND session.created_at <= ?", start, end).
|
|
Find(&stat).
|
|
Error
|
|
if err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
redis.SetEx(ctx, key, stat, time.Minute)
|
|
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
}
|
|
|
|
// StatAsset godoc
|
|
//
|
|
// @Tags stat
|
|
// @Param type query string true "account name" Enums(day, week, month)
|
|
// @Success 200 {object} HttpResponse{data=ListData{list=[]model.StatAsset}}
|
|
// @Router /stat/asset [get]
|
|
func (c *Controller) StatAsset(ctx *gin.Context) {
|
|
start, end := time.Now(), time.Now()
|
|
interval := time.Hour * 24
|
|
dateFmt := "%Y-%m-%d"
|
|
timeFmt := time.DateOnly
|
|
switch ctx.Query("type") {
|
|
case "day":
|
|
start = start.Add(-time.Hour * 24)
|
|
interval = time.Hour
|
|
dateFmt = "%Y-%m-%d %H:00:00"
|
|
timeFmt = time.DateTime
|
|
case "week":
|
|
start = start.Add(-time.Hour * 24 * 7)
|
|
case "month":
|
|
start = start.Add(-time.Hour * 24 * 30)
|
|
default:
|
|
ctx.AbortWithError(http.StatusBadRequest, fmt.Errorf("wrong time range %s", ctx.Query("type")))
|
|
return
|
|
}
|
|
|
|
stat := make([]*model.StatAsset, 0)
|
|
key := "stat-asset-" + ctx.Query("type")
|
|
if redis.Get(ctx, key, stat) == nil {
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
return
|
|
}
|
|
err := mysql.DB.
|
|
Model(&model.Session{}).
|
|
Select("COUNT(DISTINCT asset_id, uid) AS connect, COUNT(*) AS session, COUNT(DISTINCT asset_id) AS asset, COUNT(DISTINCT uid) AS user, DATE_FORMAT(created_at, ?) AS time", dateFmt).
|
|
Where("session.created_at >= ? AND session.created_at <= ?", start, end).
|
|
Group("time").
|
|
Find(&stat).
|
|
Error
|
|
if err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
for ; !start.After(end); start = start.Add(interval) {
|
|
t := start.Truncate(interval).Format(timeFmt)
|
|
if lo.ContainsBy(stat, func(s *model.StatAsset) bool { return t == s.Time }) {
|
|
continue
|
|
}
|
|
stat = append(stat, &model.StatAsset{Time: t})
|
|
}
|
|
|
|
sort.Slice(stat, func(i, j int) bool { return stat[i].Time < stat[j].Time })
|
|
|
|
redis.SetEx(ctx, key, stat, time.Minute)
|
|
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
}
|
|
|
|
// StatCountOfUser godoc
|
|
//
|
|
// @Tags stat
|
|
// @Success 200 {object} HttpResponse{data=model.StatCountOfUser}
|
|
// @Router /stat/count/ofuser [get]
|
|
func (c *Controller) StatCountOfUser(ctx *gin.Context) {
|
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
|
stat := &model.StatCountOfUser{}
|
|
key := fmt.Sprintf("stat-count-%d-", currentUser.Uid)
|
|
if redis.Get(ctx, key, stat) == nil {
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(stat))
|
|
return
|
|
}
|
|
|
|
eg := &errgroup.Group{}
|
|
eg.Go(func() error {
|
|
return mysql.DB.
|
|
Model(&model.Session{}).
|
|
Select("COUNT(DISTINCT asset_id, account_id) as connect, COUNT(DISTINCT asset_id) as asset, COUNT(*) as session").
|
|
Where("status = 1").
|
|
Where("uid = ?", currentUser.Uid).
|
|
First(&stat).
|
|
Error
|
|
})
|
|
eg.Go(func() error {
|
|
isAdmin := acl.IsAdmin(currentUser)
|
|
db := mysql.DB.Model(&model.Asset{})
|
|
if !isAdmin {
|
|
authorizationResourceIds, err := GetAutorizationResourceIds(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db = db.Where("id IN (?)", mysql.DB.Model(&model.Authorization{}).Select("asset_id").Where("resource_id IN ?", authorizationResourceIds))
|
|
}
|
|
return db.Count(&stat.TotalAsset).Error
|
|
})
|
|
|
|
if err := eg.Wait(); err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
redis.SetEx(ctx, key, stat, time.Minute)
|
|
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(stat))
|
|
}
|
|
|
|
// StatRankOfUser godoc
|
|
//
|
|
// @Tags stat
|
|
// @Success 200 {object} HttpResponse{data=ListData{list=[]model.StatAsset}}
|
|
// @Router /stat/rank/ofuser [get]
|
|
func (c *Controller) StatRankOfUser(ctx *gin.Context) {
|
|
stat := make([]*model.StatRankOfUser, 0)
|
|
key := "stat-rank-user"
|
|
if redis.Get(ctx, key, stat) == nil {
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
return
|
|
}
|
|
|
|
if err := mysql.DB.
|
|
Model(&model.Session{}).
|
|
Select("uid, COUNT(*) AS count, MAX(created_at) AS last_time").
|
|
Group("uid").
|
|
Order("count DESC").
|
|
Limit(3).
|
|
Find(&stat).
|
|
Error; err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
redis.SetEx(ctx, key, stat, time.Minute)
|
|
|
|
ctx.JSON(http.StatusOK, NewHttpResponseWithData(toListData(stat)))
|
|
}
|
|
|
|
func toListData[T any](data []T) *ListData {
|
|
return &ListData{
|
|
Count: int64(len(data)),
|
|
List: lo.Map(data, func(d T, _ int) any { return d }),
|
|
}
|
|
}
|