code.gitea.io/gitea@v1.21.7/routers/web/admin/admin.go (about) 1 // Copyright 2014 The Gogs Authors. All rights reserved. 2 // Copyright 2019 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package admin 6 7 import ( 8 "fmt" 9 "net/http" 10 "runtime" 11 "sort" 12 "time" 13 14 activities_model "code.gitea.io/gitea/models/activities" 15 "code.gitea.io/gitea/modules/base" 16 "code.gitea.io/gitea/modules/context" 17 "code.gitea.io/gitea/modules/graceful" 18 "code.gitea.io/gitea/modules/json" 19 "code.gitea.io/gitea/modules/log" 20 "code.gitea.io/gitea/modules/setting" 21 "code.gitea.io/gitea/modules/updatechecker" 22 "code.gitea.io/gitea/modules/web" 23 "code.gitea.io/gitea/services/cron" 24 "code.gitea.io/gitea/services/forms" 25 repo_service "code.gitea.io/gitea/services/repository" 26 ) 27 28 const ( 29 tplDashboard base.TplName = "admin/dashboard" 30 tplCron base.TplName = "admin/cron" 31 tplQueue base.TplName = "admin/queue" 32 tplStacktrace base.TplName = "admin/stacktrace" 33 tplQueueManage base.TplName = "admin/queue_manage" 34 tplStats base.TplName = "admin/stats" 35 ) 36 37 var sysStatus struct { 38 StartTime string 39 NumGoroutine int 40 41 // General statistics. 42 MemAllocated string // bytes allocated and still in use 43 MemTotal string // bytes allocated (even if freed) 44 MemSys string // bytes obtained from system (sum of XxxSys below) 45 Lookups uint64 // number of pointer lookups 46 MemMallocs uint64 // number of mallocs 47 MemFrees uint64 // number of frees 48 49 // Main allocation heap statistics. 50 HeapAlloc string // bytes allocated and still in use 51 HeapSys string // bytes obtained from system 52 HeapIdle string // bytes in idle spans 53 HeapInuse string // bytes in non-idle span 54 HeapReleased string // bytes released to the OS 55 HeapObjects uint64 // total number of allocated objects 56 57 // Low-level fixed-size structure allocator statistics. 58 // Inuse is bytes used now. 59 // Sys is bytes obtained from system. 60 StackInuse string // bootstrap stacks 61 StackSys string 62 MSpanInuse string // mspan structures 63 MSpanSys string 64 MCacheInuse string // mcache structures 65 MCacheSys string 66 BuckHashSys string // profiling bucket hash table 67 GCSys string // GC metadata 68 OtherSys string // other system allocations 69 70 // Garbage collector statistics. 71 NextGC string // next run in HeapAlloc time (bytes) 72 LastGC string // last run in absolute time (ns) 73 PauseTotalNs string 74 PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] 75 NumGC uint32 76 } 77 78 func updateSystemStatus() { 79 sysStatus.StartTime = setting.AppStartTime.Format(time.RFC3339) 80 81 m := new(runtime.MemStats) 82 runtime.ReadMemStats(m) 83 sysStatus.NumGoroutine = runtime.NumGoroutine() 84 85 sysStatus.MemAllocated = base.FileSize(int64(m.Alloc)) 86 sysStatus.MemTotal = base.FileSize(int64(m.TotalAlloc)) 87 sysStatus.MemSys = base.FileSize(int64(m.Sys)) 88 sysStatus.Lookups = m.Lookups 89 sysStatus.MemMallocs = m.Mallocs 90 sysStatus.MemFrees = m.Frees 91 92 sysStatus.HeapAlloc = base.FileSize(int64(m.HeapAlloc)) 93 sysStatus.HeapSys = base.FileSize(int64(m.HeapSys)) 94 sysStatus.HeapIdle = base.FileSize(int64(m.HeapIdle)) 95 sysStatus.HeapInuse = base.FileSize(int64(m.HeapInuse)) 96 sysStatus.HeapReleased = base.FileSize(int64(m.HeapReleased)) 97 sysStatus.HeapObjects = m.HeapObjects 98 99 sysStatus.StackInuse = base.FileSize(int64(m.StackInuse)) 100 sysStatus.StackSys = base.FileSize(int64(m.StackSys)) 101 sysStatus.MSpanInuse = base.FileSize(int64(m.MSpanInuse)) 102 sysStatus.MSpanSys = base.FileSize(int64(m.MSpanSys)) 103 sysStatus.MCacheInuse = base.FileSize(int64(m.MCacheInuse)) 104 sysStatus.MCacheSys = base.FileSize(int64(m.MCacheSys)) 105 sysStatus.BuckHashSys = base.FileSize(int64(m.BuckHashSys)) 106 sysStatus.GCSys = base.FileSize(int64(m.GCSys)) 107 sysStatus.OtherSys = base.FileSize(int64(m.OtherSys)) 108 109 sysStatus.NextGC = base.FileSize(int64(m.NextGC)) 110 sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) 111 sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000) 112 sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000) 113 sysStatus.NumGC = m.NumGC 114 } 115 116 func prepareDeprecatedWarningsAlert(ctx *context.Context) { 117 if len(setting.DeprecatedWarnings) > 0 { 118 content := setting.DeprecatedWarnings[0] 119 if len(setting.DeprecatedWarnings) > 1 { 120 content += fmt.Sprintf(" (and %d more)", len(setting.DeprecatedWarnings)-1) 121 } 122 ctx.Flash.Error(content, true) 123 } 124 } 125 126 // Dashboard show admin panel dashboard 127 func Dashboard(ctx *context.Context) { 128 ctx.Data["Title"] = ctx.Tr("admin.dashboard") 129 ctx.Data["PageIsAdminDashboard"] = true 130 ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate() 131 ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion() 132 // FIXME: update periodically 133 updateSystemStatus() 134 ctx.Data["SysStatus"] = sysStatus 135 ctx.Data["SSH"] = setting.SSH 136 prepareDeprecatedWarningsAlert(ctx) 137 ctx.HTML(http.StatusOK, tplDashboard) 138 } 139 140 // DashboardPost run an admin operation 141 func DashboardPost(ctx *context.Context) { 142 form := web.GetForm(ctx).(*forms.AdminDashboardForm) 143 ctx.Data["Title"] = ctx.Tr("admin.dashboard") 144 ctx.Data["PageIsAdminDashboard"] = true 145 updateSystemStatus() 146 ctx.Data["SysStatus"] = sysStatus 147 148 // Run operation. 149 if form.Op != "" { 150 switch form.Op { 151 case "sync_repo_branches": 152 go func() { 153 if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), ctx.Doer.ID); err != nil { 154 log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err) 155 } 156 }() 157 ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_branch.started")) 158 default: 159 task := cron.GetTask(form.Op) 160 if task != nil { 161 go task.RunWithUser(ctx.Doer, nil) 162 ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op))) 163 } else { 164 ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op)) 165 } 166 } 167 } 168 if form.From == "monitor" { 169 ctx.Redirect(setting.AppSubURL + "/admin/monitor/cron") 170 } else { 171 ctx.Redirect(setting.AppSubURL + "/admin") 172 } 173 } 174 175 func CronTasks(ctx *context.Context) { 176 ctx.Data["Title"] = ctx.Tr("admin.monitor.cron") 177 ctx.Data["PageIsAdminMonitorCron"] = true 178 ctx.Data["Entries"] = cron.ListTasks() 179 ctx.HTML(http.StatusOK, tplCron) 180 } 181 182 func MonitorStats(ctx *context.Context) { 183 ctx.Data["Title"] = ctx.Tr("admin.monitor.stats") 184 ctx.Data["PageIsAdminMonitorStats"] = true 185 bs, err := json.Marshal(activities_model.GetStatistic(ctx).Counter) 186 if err != nil { 187 ctx.ServerError("MonitorStats", err) 188 return 189 } 190 statsCounter := map[string]any{} 191 err = json.Unmarshal(bs, &statsCounter) 192 if err != nil { 193 ctx.ServerError("MonitorStats", err) 194 return 195 } 196 statsKeys := make([]string, 0, len(statsCounter)) 197 for k := range statsCounter { 198 if statsCounter[k] == nil { 199 continue 200 } 201 statsKeys = append(statsKeys, k) 202 } 203 sort.Strings(statsKeys) 204 ctx.Data["StatsKeys"] = statsKeys 205 ctx.Data["StatsCounter"] = statsCounter 206 ctx.HTML(http.StatusOK, tplStats) 207 }