diff --git a/cmd/kratos-admin/wire_gen.go b/cmd/kratos-admin/wire_gen.go index fa772f1..7f3ac94 100644 --- a/cmd/kratos-admin/wire_gen.go +++ b/cmd/kratos-admin/wire_gen.go @@ -30,18 +30,23 @@ func wireApp(confServer *conf.Server, runtime *conf.Runtime, logger *slog.Logger if err != nil { return nil, nil, err } - accessRepo := data.NewAccessRepo(dataData) - accessUsecase := biz.NewAccessUsecase(accessRepo) - accessControlService := service.NewAccessControlService(accessUsecase) - authorityService := service.NewAuthorityService(accessUsecase) + authorityAccessRepo := data.NewAuthorityAccessRepo(dataData) + apiRepo := data.NewAPIRepo(dataData) + accessControlUsecase := biz.NewAccessControlUsecase(authorityAccessRepo, apiRepo) + accessControlService := service.NewAccessControlService(accessControlUsecase) + authorityUsecase := biz.NewAuthorityUsecase(authorityAccessRepo) + authorityService := service.NewAuthorityService(authorityUsecase) authority := handler.NewAuthority(authorityService) menuRepo := data.NewMenuRepo(dataData) menuUsecase := biz.NewMenuUsecase(menuRepo) menuService := service.NewMenuService(menuUsecase) menu := handler.NewMenu(menuService) - apiService := service.NewAPIService(accessUsecase, runtime) + apiUsecase := biz.NewAPIUsecase(apiRepo) + apiService := service.NewAPIService(apiUsecase, runtime) api := handler.NewAPI(apiService) - permissionService := service.NewPermissionService(accessUsecase) + permissionRepo := data.NewPermissionRepo(dataData) + permissionUsecase := biz.NewPermissionUsecase(permissionRepo) + permissionService := service.NewPermissionService(permissionUsecase) permission := handler.NewPermission(permissionService) departmentRepo := data.NewDepartmentRepo(dataData) departmentUsecase := biz.NewDepartmentUsecase(departmentRepo) @@ -73,11 +78,15 @@ func wireApp(confServer *conf.Server, runtime *conf.Runtime, logger *slog.Logger task := handler.NewTask(taskService, taskScheduler) mediaService := service.NewMediaService(mediaUsecase, runtime) media := handler.NewMedia(mediaService) - auditRepo := data.NewAuditRepo(dataData) - auditUsecase := biz.NewAuditUsecase(auditRepo) + auditQueryRepo := data.NewAuditRepo(dataData) + auditUsecase := biz.NewAuditUsecase(auditQueryRepo) auditService := service.NewAuditService(auditUsecase) - auditRecorder := service.NewAuditRecorder(auditUsecase) - logViewerService := service.NewLogViewerService(auditUsecase) + auditRecordRepo := data.NewAuditRecorderRepo(dataData) + auditRecorderUsecase := biz.NewAuditRecorderUsecase(auditRecordRepo) + auditRecorder := service.NewAuditRecorder(auditRecorderUsecase) + logFileRepo := data.NewLogFileRepo(dataData) + logViewerUsecase := biz.NewLogViewerUsecase(logFileRepo) + logViewerService := service.NewLogViewerService(logViewerUsecase) audit := handler.NewAudit(auditService, auditRecorder, logViewerService) exportRepo := data.NewExportRepo(dataData) exportUsecase := biz.NewExportUsecase(exportRepo) @@ -105,7 +114,7 @@ func wireApp(confServer *conf.Server, runtime *conf.Runtime, logger *slog.Logger systemConfigService := service.NewSystemConfigService(systemConfigUsecase, runtime) securityRepo := data.NewSecurityRepo(dataData) securityUsecase := biz.NewSecurityUsecase(securityRepo) - securityService := service.NewSecurityService(securityUsecase, runtime, cache, tokenService) + securityService := service.NewSecurityService(securityUsecase, runtime, cache, tokenUsecase) systemConfig := handler.NewSystemConfig(systemConfigService, securityService, taskScheduler) userRepo := data.NewUserRepo(dataData) userUsecase := biz.NewUserUsecase(userRepo) diff --git a/internal/biz/access.go b/internal/biz/access.go index 87a9659..1633cb4 100644 --- a/internal/biz/access.go +++ b/internal/biz/access.go @@ -57,21 +57,40 @@ type PermissionRepo interface { CanRemoveButton(context.Context, uint) (bool, error) } -type AccessRepo interface { - AuthorityAccessRepo - APIRepo - PermissionRepo +type AuthorityUsecase struct{ AuthorityAccessRepo } + +func NewAuthorityUsecase(repo AuthorityAccessRepo) *AuthorityUsecase { + return &AuthorityUsecase{AuthorityAccessRepo: repo} } -type AccessUsecase struct{ AccessRepo } +type APIUsecase struct{ APIRepo } -func NewAccessUsecase(repo AccessRepo) *AccessUsecase { return &AccessUsecase{AccessRepo: repo} } +func NewAPIUsecase(repo APIRepo) *APIUsecase { return &APIUsecase{APIRepo: repo} } -func (uc *AccessUsecase) Authorize(ctx context.Context, authorityID uint, path, method string) (bool, error) { +type PermissionUsecase struct{ PermissionRepo } + +func NewPermissionUsecase(repo PermissionRepo) *PermissionUsecase { + return &PermissionUsecase{PermissionRepo: repo} +} + +type AccessControlUsecase struct { + authorities AuthorityAccessRepo + apis APIRepo +} + +func NewAccessControlUsecase(authorities AuthorityAccessRepo, apis APIRepo) *AccessControlUsecase { + return &AccessControlUsecase{authorities: authorities, apis: apis} +} + +func (uc *AccessControlUsecase) Authorize(ctx context.Context, authorityID uint, path, method string) (bool, error) { if authorityID == 888 { return true, nil } - return uc.AccessRepo.Authorize(ctx, authorityID, path, method) + return uc.apis.Authorize(ctx, authorityID, path, method) +} + +func (uc *AccessControlUsecase) ResolveDataScope(ctx context.Context, authorityID, userID uint) (DataScope, error) { + return uc.authorities.ResolveDataScope(ctx, authorityID, userID) } type APISyncDiff struct { @@ -80,7 +99,7 @@ type APISyncDiff struct { Ignored []*API } -func (uc *AccessUsecase) SyncAPIs(ctx context.Context, routes []*API) (*APISyncDiff, error) { +func (uc *APIUsecase) SyncAPIs(ctx context.Context, routes []*API) (*APISyncDiff, error) { stored, _, err := uc.ListAPIs(ctx, 0, 0, nil) if err != nil { return nil, err @@ -118,7 +137,7 @@ func (uc *AccessUsecase) SyncAPIs(ctx context.Context, routes []*API) (*APISyncD return diff, nil } -func (uc *AccessUsecase) AuthorityTree(ctx context.Context) ([]*Authority, error) { +func (uc *AuthorityUsecase) AuthorityTree(ctx context.Context) ([]*Authority, error) { items, err := uc.ListAuthorities(ctx) if err != nil { return nil, err diff --git a/internal/biz/audit.go b/internal/biz/audit.go index 2d032b5..4f3aebf 100644 --- a/internal/biz/audit.go +++ b/internal/biz/audit.go @@ -91,6 +91,12 @@ type LogFileRepo interface { LogContent(context.Context, string, string, *int64) (*LogContent, error) } +type LogViewerUsecase struct{ LogFileRepo } + +func NewLogViewerUsecase(repo LogFileRepo) *LogViewerUsecase { + return &LogViewerUsecase{LogFileRepo: repo} +} + type ErrorRecord struct { ID uint CreatedAt time.Time @@ -106,14 +112,36 @@ type ErrorRecordRepo interface { ListErrors(context.Context, int, int, *ErrorRecord) ([]*ErrorRecord, int64, error) } -type AuditRepo interface { - OperationLogRepo - LoginLogRepo - DataAccessLogRepo - LogFileRepo - ErrorRecordRepo +type AuditRecordRepo interface { + RecordOperation(context.Context, *OperationRecord) error + RecordLogin(context.Context, *LoginLog) error + RecordDataAccess(context.Context, *DataAccessLog) error + CreateError(context.Context, *ErrorRecord) error } -type AuditUsecase struct{ AuditRepo } +type AuditQueryRepo interface { + ListOperations(context.Context, int, int, *OperationRecord) ([]*OperationRecord, int64, error) + FindOperation(context.Context, uint) (*OperationRecord, error) + DeleteOperations(context.Context, []uint) error + ListLogins(context.Context, int, int, *LoginLog) ([]*LoginLog, int64, error) + FindLogin(context.Context, uint) (*LoginLog, error) + DeleteLogins(context.Context, []uint) error + ListDataAccess(context.Context, int, int, *DataAccessLog) ([]*DataAccessLog, int64, error) + DeleteDataAccess(context.Context, []uint) error + UpdateError(context.Context, *ErrorRecord) error + DeleteErrors(context.Context, []uint) error + FindError(context.Context, uint) (*ErrorRecord, error) + ListErrors(context.Context, int, int, *ErrorRecord) ([]*ErrorRecord, int64, error) +} -func NewAuditUsecase(repo AuditRepo) *AuditUsecase { return &AuditUsecase{AuditRepo: repo} } +type AuditUsecase struct{ AuditQueryRepo } + +func NewAuditUsecase(repo AuditQueryRepo) *AuditUsecase { + return &AuditUsecase{AuditQueryRepo: repo} +} + +type AuditRecorderUsecase struct{ AuditRecordRepo } + +func NewAuditRecorderUsecase(repo AuditRecordRepo) *AuditRecorderUsecase { + return &AuditRecorderUsecase{AuditRecordRepo: repo} +} diff --git a/internal/biz/biz.go b/internal/biz/biz.go index c5a48aa..2743352 100644 --- a/internal/biz/biz.go +++ b/internal/biz/biz.go @@ -3,4 +3,4 @@ package biz import "github.com/google/wire" // ProviderSet is biz providers. -var ProviderSet = wire.NewSet(NewUserUsecase, NewSystemConfigUsecase, NewAccessUsecase, NewMenuUsecase, NewDepartmentUsecase, NewPositionUsecase, NewDictionaryUsecase, NewParameterUsecase, NewTokenUsecase, NewSecurityUsecase, NewVersionUsecase, NewExportUsecase, NewAuditUsecase, NewTaskUsecase, NewMediaUsecase, NewAnnouncementUsecase, NewEmailUsecase) +var ProviderSet = wire.NewSet(NewUserUsecase, NewSystemConfigUsecase, NewAuthorityUsecase, NewAPIUsecase, NewPermissionUsecase, NewAccessControlUsecase, NewMenuUsecase, NewDepartmentUsecase, NewPositionUsecase, NewDictionaryUsecase, NewParameterUsecase, NewTokenUsecase, NewSecurityUsecase, NewVersionUsecase, NewExportUsecase, NewAuditUsecase, NewAuditRecorderUsecase, NewLogViewerUsecase, NewTaskUsecase, NewMediaUsecase, NewAnnouncementUsecase, NewEmailUsecase) diff --git a/internal/data/access.go b/internal/data/access.go index 262594c..cfd9b5e 100644 --- a/internal/data/access.go +++ b/internal/data/access.go @@ -4,4 +4,8 @@ import "kra/internal/biz" type accessRepo struct{ data *Data } -func NewAccessRepo(data *Data) biz.AccessRepo { return &accessRepo{data: data} } +func NewAuthorityAccessRepo(data *Data) biz.AuthorityAccessRepo { return &accessRepo{data: data} } + +func NewAPIRepo(data *Data) biz.APIRepo { return &accessRepo{data: data} } + +func NewPermissionRepo(data *Data) biz.PermissionRepo { return &accessRepo{data: data} } diff --git a/internal/data/audit.go b/internal/data/audit.go index 644cd98..4944244 100644 --- a/internal/data/audit.go +++ b/internal/data/audit.go @@ -4,4 +4,8 @@ import "kra/internal/biz" type auditRepo struct{ data *Data } -func NewAuditRepo(data *Data) biz.AuditRepo { return &auditRepo{data: data} } +func NewAuditRepo(data *Data) biz.AuditQueryRepo { return &auditRepo{data: data} } + +func NewAuditRecorderRepo(data *Data) biz.AuditRecordRepo { return &auditRepo{data: data} } + +func NewLogFileRepo(data *Data) biz.LogFileRepo { return &auditRepo{data: data} } diff --git a/internal/data/data.go b/internal/data/data.go index c0d6646..8ba6b16 100644 --- a/internal/data/data.go +++ b/internal/data/data.go @@ -13,7 +13,7 @@ import ( "kra/internal/conf" ) -var ProviderSet = wire.NewSet(NewData, NewUserRepo, NewInitializationRepo, NewAccessRepo, NewMenuRepo, NewDepartmentRepo, NewPositionRepo, NewDictionaryRepo, NewParameterRepo, NewAPITokenRepo, NewSecurityRepo, NewVersionRepo, NewExportRepo, NewAuditRepo, NewTaskRepo, NewMediaRepo, NewAnnouncementRepo, NewEmailRepo, NewCache, NewFileStorage) +var ProviderSet = wire.NewSet(NewData, NewUserRepo, NewInitializationRepo, NewAuthorityAccessRepo, NewAPIRepo, NewPermissionRepo, NewMenuRepo, NewDepartmentRepo, NewPositionRepo, NewDictionaryRepo, NewParameterRepo, NewAPITokenRepo, NewSecurityRepo, NewVersionRepo, NewExportRepo, NewAuditRepo, NewAuditRecorderRepo, NewLogFileRepo, NewTaskRepo, NewMediaRepo, NewAnnouncementRepo, NewEmailRepo, NewCache, NewFileStorage) type Data struct { initMu sync.Mutex diff --git a/internal/server/handler/api.go b/internal/server/handler/api.go index b7f5559..eb530d7 100644 --- a/internal/server/handler/api.go +++ b/internal/server/handler/api.go @@ -1,8 +1,6 @@ package handler import ( - "strings" - "kra/internal/server/httpx" "kra/internal/service" "kra/internal/service/dto" @@ -98,24 +96,11 @@ func (h *API) Find(c *gin.Context) { httpx.Write(c, httpx.CodeSuccess, gin.H{"api": item}, "获取成功") } func (h *API) Groups(c *gin.Context) { - items, err := h.service.AllAPI(c.Request.Context(), false) + groups, groupAPIMap, err := h.service.Groups(c.Request.Context()) if err != nil { httpx.Fail(c, "获取失败") return } - seen := map[string]bool{} - groups := []string{} - groupAPIMap := map[string]string{} - for _, item := range items { - if !seen[item.APIGroup] { - seen[item.APIGroup] = true - groups = append(groups, item.APIGroup) - } - parts := strings.Split(item.Path, "/") - if len(parts) > 1 { - groupAPIMap[parts[1]] = item.APIGroup - } - } httpx.OKWithData(c, gin.H{"groups": groups, "apiGroupMap": groupAPIMap}) } func (h *API) Roles(c *gin.Context) { diff --git a/internal/server/handler/system_config.go b/internal/server/handler/system_config.go index ddc8b2b..c511eed 100644 --- a/internal/server/handler/system_config.go +++ b/internal/server/handler/system_config.go @@ -1,18 +1,12 @@ package handler import ( - "runtime" - "time" - "kra/internal/server/httpx" "kra/internal/service" "kra/internal/service/dto" "kra/internal/worker" "github.com/gin-gonic/gin" - "github.com/shirou/gopsutil/v4/cpu" - "github.com/shirou/gopsutil/v4/disk" - "github.com/shirou/gopsutil/v4/mem" ) type SystemConfig struct { @@ -78,31 +72,10 @@ func (h *SystemConfig) Reload(c *gin.Context) { } func (h *SystemConfig) ServerInfo(c *gin.Context) { - physicalCores, err := cpu.Counts(false) + server, err := h.system.ServerInfo() if err != nil { httpx.Fail(c, "获取失败") return } - cpuPercent, err := cpu.Percent(200*time.Millisecond, true) - if err != nil { - httpx.Fail(c, "获取失败") - return - } - memory, err := mem.VirtualMemory() - if err != nil { - httpx.Fail(c, "获取失败") - return - } - diskInfo := make([]gin.H, 0) - for _, mountPoint := range h.system.DiskMountPoints() { - usage, usageErr := disk.Usage(mountPoint) - if usageErr != nil { - httpx.Fail(c, "获取失败") - return - } - diskInfo = append(diskInfo, gin.H{"mountPoint": mountPoint, "usedMb": usage.Used / 1024 / 1024, "usedGb": usage.Used / 1024 / 1024 / 1024, "totalMb": usage.Total / 1024 / 1024, "totalGb": usage.Total / 1024 / 1024 / 1024, "usedPercent": int(usage.UsedPercent)}) - } - usedMB, totalMB, memoryPercent := memory.Used/1024/1024, memory.Total/1024/1024, int(memory.UsedPercent) - server := gin.H{"os": gin.H{"goos": runtime.GOOS, "numCpu": runtime.NumCPU(), "compiler": runtime.Compiler, "goVersion": runtime.Version(), "numGoroutine": runtime.NumGoroutine()}, "cpu": gin.H{"cpus": cpuPercent, "cores": physicalCores}, "ram": gin.H{"usedMb": usedMB, "totalMb": totalMB, "usedPercent": memoryPercent}, "disk": diskInfo} httpx.Write(c, httpx.CodeSuccess, gin.H{"server": server}, "获取成功") } diff --git a/internal/service/access.go b/internal/service/access.go index 055bd53..7fd78b3 100644 --- a/internal/service/access.go +++ b/internal/service/access.go @@ -7,21 +7,21 @@ import ( "kra/internal/service/dto" ) -type AuthorityService struct{ uc *biz.AccessUsecase } +type AuthorityService struct{ uc *biz.AuthorityUsecase } -func NewAuthorityService(uc *biz.AccessUsecase) *AuthorityService { +func NewAuthorityService(uc *biz.AuthorityUsecase) *AuthorityService { return &AuthorityService{uc: uc} } -type PermissionService struct{ uc *biz.AccessUsecase } +type PermissionService struct{ uc *biz.PermissionUsecase } -func NewPermissionService(uc *biz.AccessUsecase) *PermissionService { +func NewPermissionService(uc *biz.PermissionUsecase) *PermissionService { return &PermissionService{uc: uc} } -type AccessControlService struct{ uc *biz.AccessUsecase } +type AccessControlService struct{ uc *biz.AccessControlUsecase } -func NewAccessControlService(uc *biz.AccessUsecase) *AccessControlService { +func NewAccessControlService(uc *biz.AccessControlUsecase) *AccessControlService { return &AccessControlService{uc: uc} } @@ -111,23 +111,6 @@ func (s *AccessControlService) ContextWithDataScope(ctx context.Context, authori return biz.NewDataScopeContext(ctx, scope), nil } -func (s *PermissionService) Buttons(ctx context.Context, id uint) ([]map[string]any, error) { - items, err := s.uc.Buttons(ctx, id) - if err != nil { - return nil, err - } - out := make([]map[string]any, 0, len(items)) - for _, v := range items { - out = append(out, map[string]any{"ID": v.ID, "name": v.Name, "desc": v.Description, "sysBaseMenuID": v.MenuID}) - } - return out, nil -} -func (s *PermissionService) SetAuthorityButtons(ctx context.Context, id uint, v map[uint][]uint) error { - return s.uc.SetAuthorityButtons(ctx, id, v) -} -func (s *PermissionService) AuthorityButtonIDs(ctx context.Context, id uint) ([]uint, error) { - return s.uc.AuthorityButtonIDs(ctx, id) -} func (s *PermissionService) SelectedButtons(ctx context.Context, aid, menuID uint) ([]uint, error) { return s.uc.SelectedButtons(ctx, aid, menuID) } diff --git a/internal/service/api.go b/internal/service/api.go index 19b5d95..010b88d 100644 --- a/internal/service/api.go +++ b/internal/service/api.go @@ -10,11 +10,11 @@ import ( ) type APIService struct { - uc *biz.AccessUsecase + uc *biz.APIUsecase runtime *conf.Runtime } -func NewAPIService(uc *biz.AccessUsecase, runtime *conf.Runtime) *APIService { +func NewAPIService(uc *biz.APIUsecase, runtime *conf.Runtime) *APIService { return &APIService{uc: uc, runtime: runtime} } @@ -63,6 +63,27 @@ func (s *APIService) AllAPI(ctx context.Context, strict bool) ([]*dto.APIRespons return apiResponses(items), nil } +func (s *APIService) Groups(ctx context.Context) ([]string, map[string]string, error) { + items, err := s.AllAPI(ctx, false) + if err != nil { + return nil, nil, err + } + seen := make(map[string]bool) + groups := make([]string, 0) + groupAPIMap := make(map[string]string) + for _, item := range items { + if !seen[item.APIGroup] { + seen[item.APIGroup] = true + groups = append(groups, item.APIGroup) + } + parts := strings.Split(item.Path, "/") + if len(parts) > 1 { + groupAPIMap[parts[1]] = item.APIGroup + } + } + return groups, groupAPIMap, nil +} + func (s *APIService) CreateAPIRequest(ctx context.Context, req *dto.APIRequest) (*dto.APIResponse, error) { value := apiDomain(req) if err := s.uc.CreateAPI(ctx, value); err != nil { diff --git a/internal/service/audit.go b/internal/service/audit.go index 0a30e1f..f19ed27 100644 --- a/internal/service/audit.go +++ b/internal/service/audit.go @@ -11,9 +11,9 @@ type AuditService struct{ uc *biz.AuditUsecase } func NewAuditService(uc *biz.AuditUsecase) *AuditService { return &AuditService{uc: uc} } -type AuditRecorder struct{ uc *biz.AuditUsecase } +type AuditRecorder struct{ uc *biz.AuditRecorderUsecase } -func NewAuditRecorder(uc *biz.AuditUsecase) *AuditRecorder { return &AuditRecorder{uc: uc} } +func NewAuditRecorder(uc *biz.AuditRecorderUsecase) *AuditRecorder { return &AuditRecorder{uc: uc} } func (s *AuditRecorder) RecordOperation(ctx context.Context, v *biz.OperationRecord) error { return s.uc.RecordOperation(ctx, v) diff --git a/internal/service/audit_log_file.go b/internal/service/audit_log_file.go index ef6d743..3b07ef2 100644 --- a/internal/service/audit_log_file.go +++ b/internal/service/audit_log_file.go @@ -6,9 +6,9 @@ import ( "kra/internal/biz" ) -type LogViewerService struct{ uc *biz.AuditUsecase } +type LogViewerService struct{ uc *biz.LogViewerUsecase } -func NewLogViewerService(uc *biz.AuditUsecase) *LogViewerService { +func NewLogViewerService(uc *biz.LogViewerUsecase) *LogViewerService { return &LogViewerService{uc: uc} } diff --git a/internal/service/security_session.go b/internal/service/security_session.go index 394067b..a752cee 100644 --- a/internal/service/security_session.go +++ b/internal/service/security_session.go @@ -16,10 +16,10 @@ type SecurityService struct { securityMu sync.RWMutex securityCache *biz.SecurityConfig cache biz.Cache - tokens *TokenService + tokens *biz.TokenUsecase } -func NewSecurityService(uc *biz.SecurityUsecase, runtime *conf.Runtime, cache biz.Cache, tokens *TokenService) *SecurityService { +func NewSecurityService(uc *biz.SecurityUsecase, runtime *conf.Runtime, cache biz.Cache, tokens *biz.TokenUsecase) *SecurityService { return &SecurityService{uc: uc, runtime: runtime, cache: cache, tokens: tokens} } diff --git a/internal/service/system_info.go b/internal/service/system_info.go new file mode 100644 index 0000000..f268b46 --- /dev/null +++ b/internal/service/system_info.go @@ -0,0 +1,40 @@ +package service + +import ( + "runtime" + "time" + + "github.com/shirou/gopsutil/v4/cpu" + "github.com/shirou/gopsutil/v4/disk" + "github.com/shirou/gopsutil/v4/mem" +) + +func (s *SystemConfigService) ServerInfo() (map[string]any, error) { + physicalCores, err := cpu.Counts(false) + if err != nil { + return nil, err + } + cpuPercent, err := cpu.Percent(200*time.Millisecond, true) + if err != nil { + return nil, err + } + memory, err := mem.VirtualMemory() + if err != nil { + return nil, err + } + diskInfo := make([]map[string]any, 0) + for _, mountPoint := range s.DiskMountPoints() { + usage, usageErr := disk.Usage(mountPoint) + if usageErr != nil { + return nil, usageErr + } + diskInfo = append(diskInfo, map[string]any{"mountPoint": mountPoint, "usedMb": usage.Used / 1024 / 1024, "usedGb": usage.Used / 1024 / 1024 / 1024, "totalMb": usage.Total / 1024 / 1024, "totalGb": usage.Total / 1024 / 1024 / 1024, "usedPercent": int(usage.UsedPercent)}) + } + usedMB, totalMB := memory.Used/1024/1024, memory.Total/1024/1024 + return map[string]any{ + "os": map[string]any{"goos": runtime.GOOS, "numCpu": runtime.NumCPU(), "compiler": runtime.Compiler, "goVersion": runtime.Version(), "numGoroutine": runtime.NumGoroutine()}, + "cpu": map[string]any{"cpus": cpuPercent, "cores": physicalCores}, + "ram": map[string]any{"usedMb": usedMB, "totalMb": totalMB, "usedPercent": int(memory.UsedPercent)}, + "disk": diskInfo, + }, nil +}