diff --git a/server/api/config.go b/server/api/config.go index ff2479d..0382716 100644 --- a/server/api/config.go +++ b/server/api/config.go @@ -61,10 +61,22 @@ func setServerConfig(c fiber.Ctx) error { }) } +func getStatistics(c fiber.Ctx) error { + s, err := service.GetStatistic() + if err != nil { + return model.NewInternalServerError("Failed to get statistics") + } + return c.JSON(model.Response[*service.Statistic]{ + Success: true, + Data: s, + }) +} + func AddConfigRoutes(r fiber.Router) { configGroup := r.Group("/config") { configGroup.Get("/", getServerConfig) configGroup.Post("/", setServerConfig) + configGroup.Get("/statistics", getStatistics) } } diff --git a/server/dao/file.go b/server/dao/file.go index 63808d9..c3f62ec 100644 --- a/server/dao/file.go +++ b/server/dao/file.go @@ -239,3 +239,11 @@ func ListUserFiles(userID uint, page, pageSize int) ([]*model.File, int64, error } return files, count, nil } + +func CountFiles() (int64, error) { + var count int64 + if err := db.Model(&model.File{}).Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} diff --git a/server/dao/resource.go b/server/dao/resource.go index 11919b4..4b045ae 100644 --- a/server/dao/resource.go +++ b/server/dao/resource.go @@ -461,3 +461,11 @@ func BatchGetResources(ids []uint) ([]model.Resource, error) { return resources, nil } + +func CountResources() (int64, error) { + var count int64 + if err := db.Model(&model.Resource{}).Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} diff --git a/server/service/statistic.go b/server/service/statistic.go new file mode 100644 index 0000000..b49b50d --- /dev/null +++ b/server/service/statistic.go @@ -0,0 +1,63 @@ +package service + +import ( + "fmt" + "nysoure/server/dao" + "nysoure/server/utils" + "os" + "time" +) + +type Statistic struct { + TotalResources int64 `json:"total_resources"` + TotalFiles int64 `json:"total_files"` + StartTime int64 `json:"start_time"` +} + +var ( + startTime int64 + cache = utils.NewMemValueCache[*Statistic](1 * time.Minute) +) + +func init() { + timeFile := utils.GetStoragePath() + "/.start_time" + if _, err := os.Stat(timeFile); os.IsNotExist(err) { + startTime = time.Now().Unix() + str := fmt.Sprintf("%d", startTime) + err := os.WriteFile(timeFile, []byte(str), 0644) + if err != nil { + panic("Failed to write start time file: " + err.Error()) + } + } else { + data, err := os.ReadFile(timeFile) + if err != nil { + panic("Failed to read start time file: " + err.Error()) + } + var t int64 + _, err = fmt.Sscanf(string(data), "%d", &t) + if err != nil { + panic("Failed to parse start time: " + err.Error()) + } + startTime = t + } +} + +func getStatistic() (*Statistic, error) { + totalResources, err := dao.CountResources() + if err != nil { + return nil, err + } + totalFiles, err := dao.CountFiles() + if err != nil { + return nil, err + } + return &Statistic{ + TotalResources: totalResources, + TotalFiles: totalFiles, + StartTime: startTime, + }, nil +} + +func GetStatistic() (*Statistic, error) { + return cache.Get(getStatistic) +} diff --git a/server/utils/cache.go b/server/utils/cache.go new file mode 100644 index 0000000..f251953 --- /dev/null +++ b/server/utils/cache.go @@ -0,0 +1,29 @@ +package utils + +import "time" + +type MemValueCache[T any] struct { + value T + duration time.Duration + expiry time.Time +} + +func NewMemValueCache[T any](duration time.Duration) *MemValueCache[T] { + return &MemValueCache[T]{ + duration: duration, + } +} + +func (c *MemValueCache[T]) Get(fetchFunc func() (T, error)) (T, error) { + var zero T + if time.Now().Before(c.expiry) { + return c.value, nil + } + v, err := fetchFunc() + if err != nil { + return zero, err + } + c.value = v + c.expiry = time.Now().Add(c.duration) + return v, nil +}