Files

282 lines
9.3 KiB
Go
Raw Permalink Normal View History

2014-03-20 07:50:26 -04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2014-03-20 07:50:26 -04:00
package admin
import (
2014-03-22 07:42:24 -04:00
"fmt"
"net/http"
2014-03-22 07:42:24 -04:00
"runtime"
"sort"
"strings"
2014-03-22 07:42:24 -04:00
"time"
2014-03-21 03:27:59 -04:00
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
2024-06-17 21:22:39 +02:00
"code.gitea.io/gitea/modules/cache"
2023-06-29 18:03:20 +08:00
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
2023-06-29 18:03:20 +08:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/updatechecker"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/cron"
"code.gitea.io/gitea/services/forms"
release_service "code.gitea.io/gitea/services/release"
2023-06-29 18:03:20 +08:00
repo_service "code.gitea.io/gitea/services/repository"
2014-03-20 07:50:26 -04:00
)
2014-06-22 13:14:03 -04:00
const (
tplDashboard templates.TplName = "admin/dashboard"
tplSystemStatus templates.TplName = "admin/system_status"
tplSelfCheck templates.TplName = "admin/self_check"
tplCron templates.TplName = "admin/cron"
tplQueue templates.TplName = "admin/queue"
2025-01-22 02:57:07 +08:00
tplPerfTrace templates.TplName = "admin/perftrace"
tplStacktrace templates.TplName = "admin/stacktrace"
tplQueueManage templates.TplName = "admin/queue_manage"
tplStats templates.TplName = "admin/stats"
2014-06-22 13:14:03 -04:00
)
2014-03-22 07:42:24 -04:00
var sysStatus struct {
StartTime string
2014-03-22 07:42:24 -04:00
NumGoroutine int
// General statistics.
MemAllocated string // bytes allocated and still in use
MemTotal string // bytes allocated (even if freed)
MemSys string // bytes obtained from system (sum of XxxSys below)
Lookups uint64 // number of pointer lookups
MemMallocs uint64 // number of mallocs
MemFrees uint64 // number of frees
// Main allocation heap statistics.
HeapAlloc string // bytes allocated and still in use
HeapSys string // bytes obtained from system
HeapIdle string // bytes in idle spans
HeapInuse string // bytes in non-idle span
HeapReleased string // bytes released to the OS
HeapObjects uint64 // total number of allocated objects
// Low-level fixed-size structure allocator statistics.
// Inuse is bytes used now.
// Sys is bytes obtained from system.
StackInuse string // bootstrap stacks
StackSys string
MSpanInuse string // mspan structures
MSpanSys string
MCacheInuse string // mcache structures
MCacheSys string
BuckHashSys string // profiling bucket hash table
GCSys string // GC metadata
OtherSys string // other system allocations
// Garbage collector statistics.
NextGC string // next run in HeapAlloc time (bytes)
LastGCTime string // last run time
2014-03-22 07:42:24 -04:00
PauseTotalNs string
PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
NumGC uint32
}
func updateSystemStatus() {
sysStatus.StartTime = setting.AppStartTime.Format(time.RFC3339)
2014-03-22 09:21:57 -04:00
2014-03-22 07:42:24 -04:00
m := new(runtime.MemStats)
runtime.ReadMemStats(m)
sysStatus.NumGoroutine = runtime.NumGoroutine()
sysStatus.MemAllocated = base.FileSize(int64(m.Alloc))
sysStatus.MemTotal = base.FileSize(int64(m.TotalAlloc))
sysStatus.MemSys = base.FileSize(int64(m.Sys))
sysStatus.Lookups = m.Lookups
sysStatus.MemMallocs = m.Mallocs
sysStatus.MemFrees = m.Frees
sysStatus.HeapAlloc = base.FileSize(int64(m.HeapAlloc))
sysStatus.HeapSys = base.FileSize(int64(m.HeapSys))
sysStatus.HeapIdle = base.FileSize(int64(m.HeapIdle))
sysStatus.HeapInuse = base.FileSize(int64(m.HeapInuse))
sysStatus.HeapReleased = base.FileSize(int64(m.HeapReleased))
sysStatus.HeapObjects = m.HeapObjects
sysStatus.StackInuse = base.FileSize(int64(m.StackInuse))
sysStatus.StackSys = base.FileSize(int64(m.StackSys))
sysStatus.MSpanInuse = base.FileSize(int64(m.MSpanInuse))
sysStatus.MSpanSys = base.FileSize(int64(m.MSpanSys))
sysStatus.MCacheInuse = base.FileSize(int64(m.MCacheInuse))
sysStatus.MCacheSys = base.FileSize(int64(m.MCacheSys))
sysStatus.BuckHashSys = base.FileSize(int64(m.BuckHashSys))
sysStatus.GCSys = base.FileSize(int64(m.GCSys))
sysStatus.OtherSys = base.FileSize(int64(m.OtherSys))
sysStatus.NextGC = base.FileSize(int64(m.NextGC))
sysStatus.LastGCTime = time.Unix(0, int64(m.LastGC)).Format(time.RFC3339)
2014-03-22 09:21:57 -04:00
sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000)
sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000)
2014-03-22 07:42:24 -04:00
sysStatus.NumGC = m.NumGC
}
func prepareStartupProblemsAlert(ctx *context.Context) {
if len(setting.StartupProblems) > 0 {
content := setting.StartupProblems[0]
if len(setting.StartupProblems) > 1 {
content += fmt.Sprintf(" (and %d more)", len(setting.StartupProblems)-1)
}
ctx.Flash.Error(content, true)
}
}
2016-11-21 11:21:24 +08:00
// Dashboard show admin panel dashboard
2016-03-11 11:56:52 -05:00
func Dashboard(ctx *context.Context) {
2014-08-28 22:29:00 +08:00
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
ctx.Data["PageIsAdminDashboard"] = true
2023-10-15 23:46:06 +08:00
ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate(ctx)
ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion(ctx)
2020-02-25 16:54:13 -06:00
updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus
ctx.Data["SSH"] = setting.SSH
prepareStartupProblemsAlert(ctx)
ctx.HTML(http.StatusOK, tplDashboard)
2020-02-25 16:54:13 -06:00
}
func SystemStatus(ctx *context.Context) {
updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus
ctx.HTML(http.StatusOK, tplSystemStatus)
}
2020-02-25 16:54:13 -06:00
// DashboardPost run an admin operation
2021-01-26 23:36:53 +08:00
func DashboardPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.AdminDashboardForm)
2020-02-25 16:54:13 -06:00
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
ctx.Data["PageIsAdminDashboard"] = true
updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus
2014-05-06 13:47:47 -04:00
// Run operation.
if form.Op != "" {
2023-06-29 18:03:20 +08:00
switch form.Op {
case "sync_repo_branches":
go func() {
if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext()); err != nil {
2023-06-29 18:03:20 +08:00
log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err)
}
}()
ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_branch.started"))
case "sync_repo_tags":
go func() {
if err := release_service.AddAllRepoTagsToSyncQueue(graceful.GetManager().ShutdownContext()); err != nil {
log.Error("AddAllRepoTagsToSyncQueue: %v: %v", ctx.Doer.ID, err)
}
}()
ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_tag.started"))
2023-06-29 18:03:20 +08:00
default:
task := cron.GetTask(form.Op)
if task != nil {
go task.RunWithUser(ctx.Doer, nil)
ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
} else {
ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
}
2014-05-06 13:47:47 -04:00
}
}
if form.From == "monitor" {
ctx.Redirect(setting.AppSubURL + "/-/admin/monitor/cron")
} else {
ctx.Redirect(setting.AppSubURL + "/-/admin")
}
2014-03-20 07:50:26 -04:00
}
func SelfCheck(ctx *context.Context) {
ctx.Data["PageIsAdminSelfCheck"] = true
ctx.Data["StartupProblems"] = setting.StartupProblems
if len(setting.StartupProblems) == 0 && !setting.IsProd {
if time.Now().Unix()%2 == 0 {
ctx.Data["StartupProblems"] = []string{"This is a test warning message in dev mode"}
}
}
r, err := db.CheckCollationsDefaultEngine()
if err != nil {
ctx.Flash.Error(fmt.Sprintf("CheckCollationsDefaultEngine: %v", err), true)
}
if r != nil {
ctx.Data["DatabaseType"] = setting.Database.Type
ctx.Data["DatabaseCheckResult"] = r
hasProblem := false
if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) {
ctx.Data["DatabaseCheckCollationMismatch"] = true
hasProblem = true
}
if !r.IsCollationCaseSensitive(r.DatabaseCollation) {
ctx.Data["DatabaseCheckCollationCaseInsensitive"] = true
hasProblem = true
}
ctx.Data["DatabaseCheckInconsistentCollationColumns"] = r.InconsistentCollationColumns
hasProblem = hasProblem || len(r.InconsistentCollationColumns) > 0
ctx.Data["DatabaseCheckHasProblems"] = hasProblem
}
2024-06-17 21:22:39 +02:00
elapsed, err := cache.Test()
if err != nil {
ctx.Data["CacheError"] = err
} else if elapsed > cache.SlowCacheThreshold {
ctx.Data["CacheSlow"] = fmt.Sprint(elapsed)
}
ctx.HTML(http.StatusOK, tplSelfCheck)
}
func SelfCheckPost(ctx *context.Context) {
var problems []string
frontendAppURL := ctx.FormString("location_origin") + setting.AppSubURL + "/"
ctxAppURL := httplib.GuessCurrentAppURL(ctx)
if !strings.HasPrefix(ctxAppURL, frontendAppURL) {
problems = append(problems, ctx.Locale.TrString("admin.self_check.location_origin_mismatch", frontendAppURL, ctxAppURL))
}
ctx.JSON(http.StatusOK, map[string]any{"problems": problems})
}
func CronTasks(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor.cron")
ctx.Data["PageIsAdminMonitorCron"] = true
2015-08-18 02:19:29 +08:00
ctx.Data["Entries"] = cron.ListTasks()
ctx.HTML(http.StatusOK, tplCron)
}
func MonitorStats(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor.stats")
ctx.Data["PageIsAdminMonitorStats"] = true
2023-09-14 19:09:32 +02:00
bs, err := json.Marshal(activities_model.GetStatistic(ctx).Counter)
if err != nil {
ctx.ServerError("MonitorStats", err)
return
}
statsCounter := map[string]any{}
err = json.Unmarshal(bs, &statsCounter)
if err != nil {
ctx.ServerError("MonitorStats", err)
return
}
statsKeys := make([]string, 0, len(statsCounter))
for k := range statsCounter {
if statsCounter[k] == nil {
continue
}
statsKeys = append(statsKeys, k)
}
sort.Strings(statsKeys)
ctx.Data["StatsKeys"] = statsKeys
ctx.Data["StatsCounter"] = statsCounter
ctx.HTML(http.StatusOK, tplStats)
}