diff --git a/backend/go.mod b/backend/go.mod index 9920b93..c6e959a 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -13,7 +13,7 @@ require ( github.com/labstack/echo/v4 v4.13.3 github.com/matoous/go-nanoid/v2 v2.1.0 github.com/redis/go-redis/v9 v9.7.3 - github.com/spf13/cast v1.7.1 + github.com/samber/lo v1.50.0 github.com/spf13/viper v1.20.1 go.uber.org/zap v1.27.0 golang.org/x/time v0.11.0 @@ -39,6 +39,7 @@ require ( github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 7646c2a..84940ed 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -59,6 +59,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -71,6 +73,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY= +github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= @@ -111,8 +115,8 @@ golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/backend/internal/controllers/stat.go b/backend/internal/controllers/stat.go index 3c992d0..31a5312 100644 --- a/backend/internal/controllers/stat.go +++ b/backend/internal/controllers/stat.go @@ -3,24 +3,66 @@ package controllers import ( "backend/internal/models" "backend/internal/utils" - "strings" + "encoding/json" + "time" + "github.com/hibiken/asynq" "github.com/labstack/echo/v4" - "github.com/spf13/cast" + "github.com/samber/lo" ) +type FileChartData struct { + FileSize int64 `json:"file_size"` + FileNum int64 `json:"file_num"` + Date string `json:"date"` +} + func GetStat(c echo.Context) error { - keys, err := models.GetRedisFileKeysAll() + fileInfoMap, err := models.GetRedisFileInfoAll() if err != nil { return utils.HTTPErrorHandler(c, err) } - var filesSize int64 - for _, key := range keys { - list := strings.Split(key, "_") - if len(list) > 1 { - filesSize += cast.ToInt64(list[1]) + fileChartData := make(map[string]FileChartData) + for _, value := range fileInfoMap { + var fileInfo models.RedisFileInfo + err := json.Unmarshal([]byte(value), &fileInfo) + if err != nil { + return utils.HTTPErrorHandler(c, err) + } + if fileInfo.FileType != models.FileTypeUpload { + continue + } + if time.Unix(fileInfo.CreatedAt, 0).After(time.Now().Add(-30 * 24 * time.Hour)) { + dateKey := time.Unix(fileInfo.CreatedAt, 0).Format("2006-01-02") + if data, ok := fileChartData[dateKey]; ok { + fileChartData[dateKey] = FileChartData{ + FileSize: data.FileSize + fileInfo.FileSize, + FileNum: data.FileNum + 1, + } + } else { + fileChartData[dateKey] = FileChartData{ + FileSize: fileInfo.FileSize, + FileNum: 1, + } + } } } + storageChartData := lo.Times(30, func(i int) FileChartData { + dateKey := time.Now().AddDate(0, 0, -i).Format("2006-01-02") + if data, ok := fileChartData[dateKey]; ok { + return FileChartData{ + FileSize: data.FileSize, + FileNum: data.FileNum, + Date: dateKey, + } + } + return FileChartData{ + FileSize: 0, + FileNum: 0, + Date: dateKey, + } + }) + queueInspector := utils.GetQueueInspector() queues, err := queueInspector.History("default", 30) if err != nil { @@ -32,19 +74,26 @@ func GetStat(c echo.Context) error { return utils.HTTPErrorHandler(c, err) } + queueData := lo.Map(queues, func(item *asynq.DailyStats, _ int) map[string]any { + return map[string]any{ + "date": item.Date.Format("2006-01-02"), + "processed": item.Processed, + "failed": item.Failed, + } + }) + return utils.HTTPSuccessHandler(c, map[string]any{ "version": "0.1.0", - "total": map[string]any{ - "file_size": filesSize, - "file_num": len(keys), - }, - "limit": map[string]any{ + "max_limit": map[string]any{ "file_size": maxStorageSize, }, "admin": map[string]any{ "user_name": utils.GetEnv("ADMIN_NAME"), "email": utils.GetEnv("ADMIN_EMAIL"), }, - "queue": queues, + "chart": map[string]any{ + "storage": storageChartData, + "queue": queueData, + }, }) } diff --git a/backend/internal/models/file.go b/backend/internal/models/file.go index 4d144ef..fd8a985 100644 --- a/backend/internal/models/file.go +++ b/backend/internal/models/file.go @@ -59,7 +59,7 @@ func SetRedisFileInfo(fileId string, fileInfo RedisFileInfo) error { return err } -func GetRedisFileKeysAll() ([]string, error) { +func GetRedisFileInfoAll() (map[string]string, error) { rdb, ctx := utils.GetRedisClient() - return rdb.HKeys(ctx, "015:fileInfoMap").Result() + return rdb.HGetAll(ctx, "015:fileInfoMap").Result() }