From 459d745cb1b94ac855ff70c034ff7fa07501684c Mon Sep 17 00:00:00 2001 From: Azareal Date: Sun, 23 Feb 2020 19:08:47 +1000 Subject: [PATCH] initial perf anaytics add tasks to debug page ignore .git on debug page for speed add perfchunks table Renamed phrases (changed statistics to stats): panel_menu_stats panel_menu_stats_posts panel_menu_stats_topics panel_menu_stats_forums panel_menu_stats_routes panel_menu_stats_agents panel_menu_stats_systems panel_menu_stats_languages panel_menu_stats_referrers panel_menu_stats_memory panel_menu_stats_active_memory panel_menu_stats_perf panel_stats_views_head_suffix panel_stats_user_agents_head panel_stats_forums_head panel_stats_languages_head panel_stats_post_counts_head panel_stats_referrers_head panel_stats_routes_head panel_stats_operating_systems_head panel_stats_topic_counts_head panel_stats_requests_head panel_stats_memory_head panel_stats_active_memory_head panel_stats_spam_hide panel_stats_spam_show panel_stats_memory_type_total panel_stats_memory_type_stack panel_stats_memory_type_heap panel_stats_time_range_one_year panel_stats_time_range_three_months panel_stats_time_range_one_month panel_stats_time_range_one_week panel_stats_time_range_two_days panel_stats_time_range_one_day panel_stats_time_range_twelve_hours panel_stats_time_range_six_hours panel_stats_post_counts_chart_aria panel_stats_topic_counts_chart_aria panel_stats_requests_chart_aria panel_stats_memory_chart_aria panel_stats_details_head panel_stats_post_counts_table_aria panel_stats_topic_counts_table_aria panel_stats_route_views_table_aria panel_stats_requests_table_aria panel_stats_memory_table_aria panel_stats_views_suffix panel_stats_posts_suffix panel_stats_topics_suffix panel_stats_user_agents_no_user_agents panel_stats_forums_no_forums panel_stats_languages_no_languages panel_stats_post_counts_no_post_counts panel_stats_referrers_no_referrers panel_stats_routes_no_routes panel_stats_operating_systems_no_operating_systems panel_stats_memory_no_memory Added phrases: panel_debug_tasks panel_debug_tasks_half_second panel_debug_tasks_second panel_debug_tasks_fifteen_minute panel_debug_tasks_hour panel_debug_tasks_shutdown panel_stats_perf_head panel_stats_perf_low panel_stats_perf_high panel_stats_perf_avg You will need to run the updater / patcher for this commit. --- cmd/query_gen/tables.go | 9 + common/audit_logs.go | 11 +- common/counters/common.go | 10 + common/counters/performance.go | 113 ++++ common/counters/posts.go | 6 +- common/counters/referrers.go | 12 +- common/counters/requests.go | 3 +- common/pages.go | 19 +- common/routes_common.go | 38 +- common/tasks.go | 21 + common/template_init.go | 3 +- gen_router.go | 573 +++++++++--------- langs/english.json | 126 ++-- patcher/patches.go | 13 + public/analytics.js | 66 +- router_gen/routes.go | 1 + routes/common.go | 13 +- routes/panel/analytics.go | 139 ++++- routes/panel/debug.go | 7 +- schema/mssql/query_perfchunks.sql | 6 + schema/mssql/query_topics.sql | 1 + schema/mysql/inserts.sql | 3 - schema/mysql/query_perfchunks.sql | 6 + schema/mysql/query_topics.sql | 1 + schema/pgsql/query_perfchunks.sql | 6 + schema/pgsql/query_topics.sql | 1 + templates/panel_analytics_active_memory.html | 18 +- templates/panel_analytics_agent_views.html | 2 +- templates/panel_analytics_agents.html | 6 +- templates/panel_analytics_forum_views.html | 2 +- templates/panel_analytics_forums.html | 6 +- templates/panel_analytics_lang_views.html | 2 +- templates/panel_analytics_langs.html | 6 +- templates/panel_analytics_memory.html | 10 +- templates/panel_analytics_performance.html | 30 + templates/panel_analytics_posts.html | 12 +- templates/panel_analytics_referrer_views.html | 2 +- templates/panel_analytics_referrers.html | 10 +- templates/panel_analytics_route_views.html | 8 +- templates/panel_analytics_routes.html | 6 +- templates/panel_analytics_script_memory.html | 32 +- templates/panel_analytics_script_perf.html | 21 + templates/panel_analytics_system_views.html | 2 +- templates/panel_analytics_systems.html | 6 +- templates/panel_analytics_time_range.html | 16 +- .../panel_analytics_time_range_month.html | 12 +- templates/panel_analytics_topics.html | 12 +- templates/panel_analytics_views.html | 12 +- templates/panel_debug.html | 16 + templates/panel_inner_menu.html | 25 +- themes/nox/overrides/panel_inner_menu.html | 25 +- 51 files changed, 985 insertions(+), 521 deletions(-) create mode 100644 common/counters/performance.go create mode 100644 schema/mssql/query_perfchunks.sql create mode 100644 schema/mysql/query_perfchunks.sql create mode 100644 schema/pgsql/query_perfchunks.sql create mode 100644 templates/panel_analytics_performance.html create mode 100644 templates/panel_analytics_script_perf.html diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 7127198f..205ca92d 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -733,6 +733,15 @@ func createTables(adapter qgen.Adapter) (err error) { }, nil, ) + createTable("perfchunks", "", "", + []tC{ + tC{"low", "int", 0, false, false, "0"}, + tC{"high", "int", 0, false, false, "0"}, + tC{"avg", "int", 0, false, false, "0"}, + tC{"createdAt", "datetime", 0, false, false, ""}, + }, nil, + ) + createTable("sync", "", "", []tC{ tC{"last_update", "datetime", 0, false, false, ""}, diff --git a/common/audit_logs.go b/common/audit_logs.go index 2db5e1ad..ab291c2f 100644 --- a/common/audit_logs.go +++ b/common/audit_logs.go @@ -35,10 +35,12 @@ type SQLModLogStore struct { func NewModLogStore(acc *qgen.Accumulator) (*SQLModLogStore, error) { ml := "moderation_logs" + // TODO: Shorten name of ipaddress column to ip + cols := "action, elementID, elementType, ipaddress, actorID, doneAt, extra" return &SQLModLogStore{ - create: acc.Insert(ml).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(), + create: acc.Insert(ml).Columns(cols).Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(), count: acc.Count(ml).Prepare(), - getOffset: acc.Select(ml).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Orderby("doneAt DESC").Limit("?,?").Prepare(), + getOffset: acc.Select(ml).Columns(cols).Orderby("doneAt DESC").Limit("?,?").Prepare(), }, acc.FirstError() } @@ -91,10 +93,11 @@ type SQLAdminLogStore struct { func NewAdminLogStore(acc *qgen.Accumulator) (*SQLAdminLogStore, error) { al := "administration_logs" + cols := "action, elementID, elementType, ipaddress, actorID, doneAt, extra" return &SQLAdminLogStore{ - create: acc.Insert(al).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(), + create: acc.Insert(al).Columns(cols).Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(), count: acc.Count(al).Prepare(), - getOffset: acc.Select(al).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Orderby("doneAt DESC").Limit("?,?").Prepare(), + getOffset: acc.Select(al).Columns(cols).Orderby("doneAt DESC").Limit("?,?").Prepare(), }, acc.FirstError() } diff --git a/common/counters/common.go b/common/counters/common.go index d54408a1..e49f7a49 100644 --- a/common/counters/common.go +++ b/common/counters/common.go @@ -40,3 +40,13 @@ type RWMutexCounterBucket struct { counter int sync.RWMutex } + +type MutexCounterBucket struct { + counter int + sync.Mutex +} + +type MutexCounter64Bucket struct { + counter int64 + sync.Mutex +} \ No newline at end of file diff --git a/common/counters/performance.go b/common/counters/performance.go new file mode 100644 index 00000000..8caae34f --- /dev/null +++ b/common/counters/performance.go @@ -0,0 +1,113 @@ +package counters + +import ( + "database/sql" + "sync/atomic" + "time" + "math" + + c "github.com/Azareal/Gosora/common" + qgen "github.com/Azareal/Gosora/query_gen" + "github.com/pkg/errors" +) + +var PerfCounter *DefaultPerfCounter + +type PerfCounterBucket struct { + low *MutexCounter64Bucket + high *MutexCounter64Bucket + avg *MutexCounter64Bucket +} + +// TODO: Track perf on a per route basis +type DefaultPerfCounter struct { + buckets []PerfCounterBucket + + insert *sql.Stmt +} + +func NewDefaultPerfCounter(acc *qgen.Accumulator) (*DefaultPerfCounter, error) { + co := &DefaultPerfCounter{ + buckets: []PerfCounterBucket{ + PerfCounterBucket{ + low: &MutexCounter64Bucket{counter: 0}, + high: &MutexCounter64Bucket{counter: 0}, + avg: &MutexCounter64Bucket{counter: 0}, + }, + }, + insert: acc.Insert("perfchunks").Columns("low,high,avg,createdAt").Fields("?,?,?,UTC_TIMESTAMP()").Prepare(), + } + + c.AddScheduledFifteenMinuteTask(co.Tick) + //c.AddScheduledSecondTask(co.Tick) + c.AddShutdownTask(co.Tick) + return co, acc.FirstError() +} + +func (co *DefaultPerfCounter) Tick() error { + getCounter := func(b *MutexCounter64Bucket) int64 { + return atomic.SwapInt64(&b.counter, 0) + } + for _, b := range co.buckets { + low := atomic.SwapInt64(&b.low.counter, math.MaxInt64) + if low == math.MaxInt64 { + low = 0 + } + high := getCounter(b.high) + avg := getCounter(b.avg) + + err := co.insertChunk(low, high, avg) // TODO: Bulk insert for speed? + if err != nil { + return errors.Wrap(errors.WithStack(err), "perf counter") + } + } + return nil +} + +func (co *DefaultPerfCounter) insertChunk(low, high, avg int64) error { + if low == 0 && high == 0 && avg == 0 { + return nil + } + c.DebugLogf("Inserting a pchunk with low %d, high %d, avg %d", low, high, avg) + _, err := co.insert.Exec(low, high, avg) + return err +} + +func (co *DefaultPerfCounter) Push(dur time.Duration) { + id := 0 + b := co.buckets[id] + //c.DebugDetail("co.buckets[", id, "]: ", b) + micro := dur.Microseconds() + + low := b.low + if micro < low.counter { + low.Lock() + if micro < low.counter { + atomic.StoreInt64(&low.counter,micro) + } + low.Unlock() + } + + high := b.high + if micro > high.counter { + high.Lock() + if micro > high.counter { + atomic.StoreInt64(&high.counter,micro) + } + high.Unlock() + } + + avg := b.avg + // TODO: Sync semantics are slightly loose but it should be close enough for our purposes here + if micro != avg.counter { + t := false + avg.Lock() + if avg.counter == 0 { + t = atomic.CompareAndSwapInt64(&avg.counter, 0, micro) + } + if !t && micro != avg.counter { + atomic.StoreInt64(&avg.counter,(micro+avg.counter) / 2) + } + avg.Unlock() + } +} diff --git a/common/counters/posts.go b/common/counters/posts.go index 8a54c66c..dd69dbbb 100644 --- a/common/counters/posts.go +++ b/common/counters/posts.go @@ -5,7 +5,7 @@ import ( "sync/atomic" c "github.com/Azareal/Gosora/common" - "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" "github.com/pkg/errors" ) @@ -22,7 +22,7 @@ func NewPostCounter() (*DefaultPostCounter, error) { acc := qgen.NewAcc() co := &DefaultPostCounter{ currentBucket: 0, - insert: acc.Insert("postchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(), + insert: acc.Insert("postchunks").Columns("count,createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(), } c.AddScheduledFifteenMinuteTask(co.Tick) //c.AddScheduledSecondTask(co.Tick) @@ -44,7 +44,7 @@ func (co *DefaultPostCounter) Tick() (err error) { atomic.AddInt64(&co.buckets[oldBucket], -previousViewChunk) err = co.insertChunk(previousViewChunk) if err != nil { - return errors.Wrap(errors.WithStack(err),"post counter") + return errors.Wrap(errors.WithStack(err), "post counter") } return nil } diff --git a/common/counters/referrers.go b/common/counters/referrers.go index f3ea1aed..8917c119 100644 --- a/common/counters/referrers.go +++ b/common/counters/referrers.go @@ -6,7 +6,7 @@ import ( "sync/atomic" c "github.com/Azareal/Gosora/common" - "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" "github.com/pkg/errors" ) @@ -35,7 +35,7 @@ func NewDefaultReferrerTracker() (*DefaultReferrerTracker, error) { refTracker := &DefaultReferrerTracker{ odd: make(map[string]*ReferrerItem), even: make(map[string]*ReferrerItem), - insert: acc.Insert("viewchunks_referrers").Columns("count, createdAt, domain").Fields("?,UTC_TIMESTAMP(),?").Prepare(), // TODO: Do something more efficient than doing a query for each referrer + insert: acc.Insert("viewchunks_referrers").Columns("count,createdAt,domain").Fields("?,UTC_TIMESTAMP(),?").Prepare(), // TODO: Do something more efficient than doing a query for each referrer } c.AddScheduledFifteenMinuteTask(refTracker.Tick) //c.AddScheduledSecondTask(refTracker.Tick) @@ -51,7 +51,7 @@ func (ref *DefaultReferrerTracker) Tick() (err error) { if count != 0 { err := ref.insertChunk(referrer, count) // TODO: Bulk insert for speed? if err != nil { - return errors.Wrap(errors.WithStack(err),"ref counter") + return errors.Wrap(errors.WithStack(err), "ref counter") } } delete(referrersToDelete, referrer) @@ -69,16 +69,16 @@ func (ref *DefaultReferrerTracker) Tick() (err error) { count := atomic.SwapInt64(&counter.Count, 0) err := ref.insertChunk(referrer, count) // TODO: Bulk insert for speed? if err != nil { - return errors.Wrap(errors.WithStack(err),"ref counter") + return errors.Wrap(errors.WithStack(err), "ref counter") } } return nil } - err = refLoop(&ref.oddLock,ref.odd) + err = refLoop(&ref.oddLock, ref.odd) if err != nil { return err } - return refLoop(&ref.evenLock,ref.even) + return refLoop(&ref.evenLock, ref.even) } func (ref *DefaultReferrerTracker) insertChunk(referrer string, count int64) error { diff --git a/common/counters/requests.go b/common/counters/requests.go index 5da9d70d..ea58d116 100644 --- a/common/counters/requests.go +++ b/common/counters/requests.go @@ -23,7 +23,7 @@ type DefaultViewCounter struct { func NewGlobalViewCounter(acc *qgen.Accumulator) (*DefaultViewCounter, error) { co := &DefaultViewCounter{ currentBucket: 0, - insert: acc.Insert("viewchunks").Columns("count, createdAt, route").Fields("?,UTC_TIMESTAMP(),''").Prepare(), + insert: acc.Insert("viewchunks").Columns("count,createdAt,route").Fields("?,UTC_TIMESTAMP(),''").Prepare(), } c.AddScheduledFifteenMinuteTask(co.Tick) // This is run once every fifteen minutes to match the frequency of the RouteViewCounter //c.AddScheduledSecondTask(co.Tick) @@ -31,6 +31,7 @@ func NewGlobalViewCounter(acc *qgen.Accumulator) (*DefaultViewCounter, error) { return co, acc.FirstError() } +// TODO: Simplify the atomics used here func (co *DefaultViewCounter) Tick() (err error) { oldBucket := co.currentBucket var nextBucket int64 // 0 diff --git a/common/pages.go b/common/pages.go index c60c9929..a5128e9e 100644 --- a/common/pages.go +++ b/common/pages.go @@ -354,6 +354,14 @@ type PanelAnalyticsActiveMemory struct { TimeType string MemType int } +type PanelAnalyticsPerf struct { + Graph PanelTimeGraph + ViewItems []PanelAnalyticsItemUnit + TimeRange string + Unit string + TimeType string + PerfType int +} type PanelStats struct { Users int @@ -654,6 +662,14 @@ type PanelRegLogsPage struct { Paginator } +type DebugPageTasks struct { + HalfSecond int + Second int + FifteenMinute int + Hour int + Shutdown int +} + type DebugPageCache struct { Topics int Users int @@ -711,8 +727,9 @@ type PanelDebugPage struct { Goroutines int CPUs int - MemStats runtime.MemStats + Tasks DebugPageTasks + MemStats runtime.MemStats Cache DebugPageCache Database DebugPageDatabase Disk DebugPageDisk diff --git a/common/routes_common.go b/common/routes_common.go index 1d675277..3ec38d75 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -113,25 +113,25 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header } // TODO: We should probably initialise header.ExtData // ? - Should we only show this in debug mode? It might be useful for detecting issues in production, if we show it there as-well - if user.IsAdmin { + //if user.IsAdmin { header.StartedAt = time.Now() - } + //} header.AddSheet(theme.Name + "/main.css") header.AddSheet(theme.Name + "/panel.css") if len(theme.Resources) > 0 { rlist := theme.Resources - for _, resource := range rlist { - if resource.Location == "global" || resource.Location == "panel" { - extarr := strings.Split(resource.Name, ".") + for _, res := range rlist { + if res.Location == "global" || res.Location == "panel" { + extarr := strings.Split(res.Name, ".") ext := extarr[len(extarr)-1] if ext == "css" { - header.AddSheet(resource.Name) + header.AddSheet(res.Name) } else if ext == "js" { - if resource.Async { - header.AddScriptAsync(resource.Name) + if res.Async { + header.AddScriptAsync(res.Name) } else { - header.AddScript(resource.Name) + header.AddScript(res.Name) } } } @@ -224,9 +224,9 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (header *Head // An optimisation so we don't populate StartedAt for users who shouldn't see the stat anyway // ? - Should we only show this in debug mode? It might be useful for detecting issues in production, if we show it there as-well - if user.IsAdmin { + //if user.IsAdmin { header.StartedAt = time.Now() - } + //} //PrepResources(user,header,theme) return header, nil @@ -237,20 +237,20 @@ func PrepResources(user *User, h *Header, theme *Theme) { if len(theme.Resources) > 0 { rlist := theme.Resources - for _, resource := range rlist { - if resource.Loggedin && !user.Loggedin { + for _, res := range rlist { + if res.Loggedin && !user.Loggedin { continue } - if resource.Location == "global" || resource.Location == "frontend" { - extarr := strings.Split(resource.Name, ".") + if res.Location == "global" || res.Location == "frontend" { + extarr := strings.Split(res.Name, ".") ext := extarr[len(extarr)-1] if ext == "css" { - h.AddSheet(resource.Name) + h.AddSheet(res.Name) } else if ext == "js" { - if resource.Async { - h.AddScriptAsync(resource.Name) + if res.Async { + h.AddScriptAsync(res.Name) } else { - h.AddScript(resource.Name) + h.AddScript(res.Name) } } } diff --git a/common/tasks.go b/common/tasks.go index b57c4b5e..febd1e4e 100644 --- a/common/tasks.go +++ b/common/tasks.go @@ -64,6 +64,27 @@ func AddShutdownTask(task func() error) { ShutdownTasks = append(ShutdownTasks, task) } +// ScheduledHalfSecondTaskCount is not concurrency safe +func ScheduledHalfSecondTaskCount() int { + return len(ScheduledHalfSecondTasks) +} +// ScheduledSecondTaskCount is not concurrency safe +func ScheduledSecondTaskCount() int { + return len(ScheduledSecondTasks) +} +// ScheduledFifteenMinuteTaskCount is not concurrency safe +func ScheduledFifteenMinuteTaskCount() int { + return len(ScheduledFifteenMinuteTasks) +} +// ScheduledHourTaskCount is not concurrency safe +func ScheduledHourTaskCount() int { + return len(ScheduledHourTasks) +} +// ShutdownTaskCount is not concurrency safe +func ShutdownTaskCount() int { + return len(ShutdownTasks) +} + // TODO: Use AddScheduledSecondTask func HandleExpiredScheduledGroups() error { rows, err := taskStmts.getExpiredScheduledGroups.Query() diff --git a/common/template_init.go b/common/template_init.go index 1f4e5474..d44b2fa1 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -372,10 +372,11 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string dbVersion := qgen.Builder.DbVersion() var memStats runtime.MemStats runtime.ReadMemStats(&memStats) + debugTasks := DebugPageTasks{0,0,0,0,0} debugCache := DebugPageCache{1, 1, 1, 2, 2, 2, true} debugDatabase := DebugPageDatabase{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} debugDisk := DebugPageDisk{1, 1, 1, 1, 1, 1} - dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, memStats, debugCache, debugDatabase, debugDisk} + dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, debugTasks, memStats, debugCache, debugDatabase, debugDisk} t.AddStd("panel_debug", "c.PanelDebugPage", dpage) //t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter}) diff --git a/gen_router.go b/gen_router.go index 2104ca9c..6e5bcd58 100644 --- a/gen_router.go +++ b/gen_router.go @@ -100,6 +100,7 @@ var RouteMap = map[string]interface{}{ "panel.AnalyticsActiveMemory": panel.AnalyticsActiveMemory, "panel.AnalyticsTopics": panel.AnalyticsTopics, "panel.AnalyticsForums": panel.AnalyticsForums, + "panel.AnalyticsPerf": panel.AnalyticsPerf, "panel.Groups": panel.Groups, "panel.GroupsEdit": panel.GroupsEdit, "panel.GroupsEditPromotions": panel.GroupsEditPromotions, @@ -274,99 +275,100 @@ var routeMapEnum = map[string]int{ "panel.AnalyticsActiveMemory": 74, "panel.AnalyticsTopics": 75, "panel.AnalyticsForums": 76, - "panel.Groups": 77, - "panel.GroupsEdit": 78, - "panel.GroupsEditPromotions": 79, - "panel.GroupsPromotionsCreateSubmit": 80, - "panel.GroupsPromotionsDeleteSubmit": 81, - "panel.GroupsEditPerms": 82, - "panel.GroupsEditSubmit": 83, - "panel.GroupsEditPermsSubmit": 84, - "panel.GroupsCreateSubmit": 85, - "panel.Backups": 86, - "panel.LogsRegs": 87, - "panel.LogsMod": 88, - "panel.LogsAdmin": 89, - "panel.Debug": 90, - "panel.Dashboard": 91, - "routes.AccountEdit": 92, - "routes.AccountEditPassword": 93, - "routes.AccountEditPasswordSubmit": 94, - "routes.AccountEditAvatarSubmit": 95, - "routes.AccountEditRevokeAvatarSubmit": 96, - "routes.AccountEditUsernameSubmit": 97, - "routes.AccountEditPrivacy": 98, - "routes.AccountEditPrivacySubmit": 99, - "routes.AccountEditMFA": 100, - "routes.AccountEditMFASetup": 101, - "routes.AccountEditMFASetupSubmit": 102, - "routes.AccountEditMFADisableSubmit": 103, - "routes.AccountEditEmail": 104, - "routes.AccountEditEmailTokenSubmit": 105, - "routes.AccountLogins": 106, - "routes.AccountBlocked": 107, - "routes.LevelList": 108, - "routes.Convos": 109, - "routes.ConvosCreate": 110, - "routes.Convo": 111, - "routes.ConvosCreateSubmit": 112, - "routes.ConvosCreateReplySubmit": 113, - "routes.ConvosDeleteReplySubmit": 114, - "routes.ConvosEditReplySubmit": 115, - "routes.RelationsBlockCreate": 116, - "routes.RelationsBlockCreateSubmit": 117, - "routes.RelationsBlockRemove": 118, - "routes.RelationsBlockRemoveSubmit": 119, - "routes.ViewProfile": 120, - "routes.BanUserSubmit": 121, - "routes.UnbanUser": 122, - "routes.ActivateUser": 123, - "routes.IPSearch": 124, - "routes.DeletePostsSubmit": 125, - "routes.CreateTopicSubmit": 126, - "routes.EditTopicSubmit": 127, - "routes.DeleteTopicSubmit": 128, - "routes.StickTopicSubmit": 129, - "routes.UnstickTopicSubmit": 130, - "routes.LockTopicSubmit": 131, - "routes.UnlockTopicSubmit": 132, - "routes.MoveTopicSubmit": 133, - "routes.LikeTopicSubmit": 134, - "routes.UnlikeTopicSubmit": 135, - "routes.AddAttachToTopicSubmit": 136, - "routes.RemoveAttachFromTopicSubmit": 137, - "routes.ViewTopic": 138, - "routes.CreateReplySubmit": 139, - "routes.ReplyEditSubmit": 140, - "routes.ReplyDeleteSubmit": 141, - "routes.ReplyLikeSubmit": 142, - "routes.ReplyUnlikeSubmit": 143, - "routes.AddAttachToReplySubmit": 144, - "routes.RemoveAttachFromReplySubmit": 145, - "routes.ProfileReplyCreateSubmit": 146, - "routes.ProfileReplyEditSubmit": 147, - "routes.ProfileReplyDeleteSubmit": 148, - "routes.PollVote": 149, - "routes.PollResults": 150, - "routes.AccountLogin": 151, - "routes.AccountRegister": 152, - "routes.AccountLogout": 153, - "routes.AccountLoginSubmit": 154, - "routes.AccountLoginMFAVerify": 155, - "routes.AccountLoginMFAVerifySubmit": 156, - "routes.AccountRegisterSubmit": 157, - "routes.AccountPasswordReset": 158, - "routes.AccountPasswordResetSubmit": 159, - "routes.AccountPasswordResetToken": 160, - "routes.AccountPasswordResetTokenSubmit": 161, - "routes.DynamicRoute": 162, - "routes.UploadedFile": 163, - "routes.StaticFile": 164, - "routes.RobotsTxt": 165, - "routes.SitemapXml": 166, - "routes.OpenSearchXml": 167, - "routes.BadRoute": 168, - "routes.HTTPSRedirect": 169, + "panel.AnalyticsPerf": 77, + "panel.Groups": 78, + "panel.GroupsEdit": 79, + "panel.GroupsEditPromotions": 80, + "panel.GroupsPromotionsCreateSubmit": 81, + "panel.GroupsPromotionsDeleteSubmit": 82, + "panel.GroupsEditPerms": 83, + "panel.GroupsEditSubmit": 84, + "panel.GroupsEditPermsSubmit": 85, + "panel.GroupsCreateSubmit": 86, + "panel.Backups": 87, + "panel.LogsRegs": 88, + "panel.LogsMod": 89, + "panel.LogsAdmin": 90, + "panel.Debug": 91, + "panel.Dashboard": 92, + "routes.AccountEdit": 93, + "routes.AccountEditPassword": 94, + "routes.AccountEditPasswordSubmit": 95, + "routes.AccountEditAvatarSubmit": 96, + "routes.AccountEditRevokeAvatarSubmit": 97, + "routes.AccountEditUsernameSubmit": 98, + "routes.AccountEditPrivacy": 99, + "routes.AccountEditPrivacySubmit": 100, + "routes.AccountEditMFA": 101, + "routes.AccountEditMFASetup": 102, + "routes.AccountEditMFASetupSubmit": 103, + "routes.AccountEditMFADisableSubmit": 104, + "routes.AccountEditEmail": 105, + "routes.AccountEditEmailTokenSubmit": 106, + "routes.AccountLogins": 107, + "routes.AccountBlocked": 108, + "routes.LevelList": 109, + "routes.Convos": 110, + "routes.ConvosCreate": 111, + "routes.Convo": 112, + "routes.ConvosCreateSubmit": 113, + "routes.ConvosCreateReplySubmit": 114, + "routes.ConvosDeleteReplySubmit": 115, + "routes.ConvosEditReplySubmit": 116, + "routes.RelationsBlockCreate": 117, + "routes.RelationsBlockCreateSubmit": 118, + "routes.RelationsBlockRemove": 119, + "routes.RelationsBlockRemoveSubmit": 120, + "routes.ViewProfile": 121, + "routes.BanUserSubmit": 122, + "routes.UnbanUser": 123, + "routes.ActivateUser": 124, + "routes.IPSearch": 125, + "routes.DeletePostsSubmit": 126, + "routes.CreateTopicSubmit": 127, + "routes.EditTopicSubmit": 128, + "routes.DeleteTopicSubmit": 129, + "routes.StickTopicSubmit": 130, + "routes.UnstickTopicSubmit": 131, + "routes.LockTopicSubmit": 132, + "routes.UnlockTopicSubmit": 133, + "routes.MoveTopicSubmit": 134, + "routes.LikeTopicSubmit": 135, + "routes.UnlikeTopicSubmit": 136, + "routes.AddAttachToTopicSubmit": 137, + "routes.RemoveAttachFromTopicSubmit": 138, + "routes.ViewTopic": 139, + "routes.CreateReplySubmit": 140, + "routes.ReplyEditSubmit": 141, + "routes.ReplyDeleteSubmit": 142, + "routes.ReplyLikeSubmit": 143, + "routes.ReplyUnlikeSubmit": 144, + "routes.AddAttachToReplySubmit": 145, + "routes.RemoveAttachFromReplySubmit": 146, + "routes.ProfileReplyCreateSubmit": 147, + "routes.ProfileReplyEditSubmit": 148, + "routes.ProfileReplyDeleteSubmit": 149, + "routes.PollVote": 150, + "routes.PollResults": 151, + "routes.AccountLogin": 152, + "routes.AccountRegister": 153, + "routes.AccountLogout": 154, + "routes.AccountLoginSubmit": 155, + "routes.AccountLoginMFAVerify": 156, + "routes.AccountLoginMFAVerifySubmit": 157, + "routes.AccountRegisterSubmit": 158, + "routes.AccountPasswordReset": 159, + "routes.AccountPasswordResetSubmit": 160, + "routes.AccountPasswordResetToken": 161, + "routes.AccountPasswordResetTokenSubmit": 162, + "routes.DynamicRoute": 163, + "routes.UploadedFile": 164, + "routes.StaticFile": 165, + "routes.RobotsTxt": 166, + "routes.SitemapXml": 167, + "routes.OpenSearchXml": 168, + "routes.BadRoute": 169, + "routes.HTTPSRedirect": 170, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -446,99 +448,100 @@ var reverseRouteMapEnum = map[int]string{ 74: "panel.AnalyticsActiveMemory", 75: "panel.AnalyticsTopics", 76: "panel.AnalyticsForums", - 77: "panel.Groups", - 78: "panel.GroupsEdit", - 79: "panel.GroupsEditPromotions", - 80: "panel.GroupsPromotionsCreateSubmit", - 81: "panel.GroupsPromotionsDeleteSubmit", - 82: "panel.GroupsEditPerms", - 83: "panel.GroupsEditSubmit", - 84: "panel.GroupsEditPermsSubmit", - 85: "panel.GroupsCreateSubmit", - 86: "panel.Backups", - 87: "panel.LogsRegs", - 88: "panel.LogsMod", - 89: "panel.LogsAdmin", - 90: "panel.Debug", - 91: "panel.Dashboard", - 92: "routes.AccountEdit", - 93: "routes.AccountEditPassword", - 94: "routes.AccountEditPasswordSubmit", - 95: "routes.AccountEditAvatarSubmit", - 96: "routes.AccountEditRevokeAvatarSubmit", - 97: "routes.AccountEditUsernameSubmit", - 98: "routes.AccountEditPrivacy", - 99: "routes.AccountEditPrivacySubmit", - 100: "routes.AccountEditMFA", - 101: "routes.AccountEditMFASetup", - 102: "routes.AccountEditMFASetupSubmit", - 103: "routes.AccountEditMFADisableSubmit", - 104: "routes.AccountEditEmail", - 105: "routes.AccountEditEmailTokenSubmit", - 106: "routes.AccountLogins", - 107: "routes.AccountBlocked", - 108: "routes.LevelList", - 109: "routes.Convos", - 110: "routes.ConvosCreate", - 111: "routes.Convo", - 112: "routes.ConvosCreateSubmit", - 113: "routes.ConvosCreateReplySubmit", - 114: "routes.ConvosDeleteReplySubmit", - 115: "routes.ConvosEditReplySubmit", - 116: "routes.RelationsBlockCreate", - 117: "routes.RelationsBlockCreateSubmit", - 118: "routes.RelationsBlockRemove", - 119: "routes.RelationsBlockRemoveSubmit", - 120: "routes.ViewProfile", - 121: "routes.BanUserSubmit", - 122: "routes.UnbanUser", - 123: "routes.ActivateUser", - 124: "routes.IPSearch", - 125: "routes.DeletePostsSubmit", - 126: "routes.CreateTopicSubmit", - 127: "routes.EditTopicSubmit", - 128: "routes.DeleteTopicSubmit", - 129: "routes.StickTopicSubmit", - 130: "routes.UnstickTopicSubmit", - 131: "routes.LockTopicSubmit", - 132: "routes.UnlockTopicSubmit", - 133: "routes.MoveTopicSubmit", - 134: "routes.LikeTopicSubmit", - 135: "routes.UnlikeTopicSubmit", - 136: "routes.AddAttachToTopicSubmit", - 137: "routes.RemoveAttachFromTopicSubmit", - 138: "routes.ViewTopic", - 139: "routes.CreateReplySubmit", - 140: "routes.ReplyEditSubmit", - 141: "routes.ReplyDeleteSubmit", - 142: "routes.ReplyLikeSubmit", - 143: "routes.ReplyUnlikeSubmit", - 144: "routes.AddAttachToReplySubmit", - 145: "routes.RemoveAttachFromReplySubmit", - 146: "routes.ProfileReplyCreateSubmit", - 147: "routes.ProfileReplyEditSubmit", - 148: "routes.ProfileReplyDeleteSubmit", - 149: "routes.PollVote", - 150: "routes.PollResults", - 151: "routes.AccountLogin", - 152: "routes.AccountRegister", - 153: "routes.AccountLogout", - 154: "routes.AccountLoginSubmit", - 155: "routes.AccountLoginMFAVerify", - 156: "routes.AccountLoginMFAVerifySubmit", - 157: "routes.AccountRegisterSubmit", - 158: "routes.AccountPasswordReset", - 159: "routes.AccountPasswordResetSubmit", - 160: "routes.AccountPasswordResetToken", - 161: "routes.AccountPasswordResetTokenSubmit", - 162: "routes.DynamicRoute", - 163: "routes.UploadedFile", - 164: "routes.StaticFile", - 165: "routes.RobotsTxt", - 166: "routes.SitemapXml", - 167: "routes.OpenSearchXml", - 168: "routes.BadRoute", - 169: "routes.HTTPSRedirect", + 77: "panel.AnalyticsPerf", + 78: "panel.Groups", + 79: "panel.GroupsEdit", + 80: "panel.GroupsEditPromotions", + 81: "panel.GroupsPromotionsCreateSubmit", + 82: "panel.GroupsPromotionsDeleteSubmit", + 83: "panel.GroupsEditPerms", + 84: "panel.GroupsEditSubmit", + 85: "panel.GroupsEditPermsSubmit", + 86: "panel.GroupsCreateSubmit", + 87: "panel.Backups", + 88: "panel.LogsRegs", + 89: "panel.LogsMod", + 90: "panel.LogsAdmin", + 91: "panel.Debug", + 92: "panel.Dashboard", + 93: "routes.AccountEdit", + 94: "routes.AccountEditPassword", + 95: "routes.AccountEditPasswordSubmit", + 96: "routes.AccountEditAvatarSubmit", + 97: "routes.AccountEditRevokeAvatarSubmit", + 98: "routes.AccountEditUsernameSubmit", + 99: "routes.AccountEditPrivacy", + 100: "routes.AccountEditPrivacySubmit", + 101: "routes.AccountEditMFA", + 102: "routes.AccountEditMFASetup", + 103: "routes.AccountEditMFASetupSubmit", + 104: "routes.AccountEditMFADisableSubmit", + 105: "routes.AccountEditEmail", + 106: "routes.AccountEditEmailTokenSubmit", + 107: "routes.AccountLogins", + 108: "routes.AccountBlocked", + 109: "routes.LevelList", + 110: "routes.Convos", + 111: "routes.ConvosCreate", + 112: "routes.Convo", + 113: "routes.ConvosCreateSubmit", + 114: "routes.ConvosCreateReplySubmit", + 115: "routes.ConvosDeleteReplySubmit", + 116: "routes.ConvosEditReplySubmit", + 117: "routes.RelationsBlockCreate", + 118: "routes.RelationsBlockCreateSubmit", + 119: "routes.RelationsBlockRemove", + 120: "routes.RelationsBlockRemoveSubmit", + 121: "routes.ViewProfile", + 122: "routes.BanUserSubmit", + 123: "routes.UnbanUser", + 124: "routes.ActivateUser", + 125: "routes.IPSearch", + 126: "routes.DeletePostsSubmit", + 127: "routes.CreateTopicSubmit", + 128: "routes.EditTopicSubmit", + 129: "routes.DeleteTopicSubmit", + 130: "routes.StickTopicSubmit", + 131: "routes.UnstickTopicSubmit", + 132: "routes.LockTopicSubmit", + 133: "routes.UnlockTopicSubmit", + 134: "routes.MoveTopicSubmit", + 135: "routes.LikeTopicSubmit", + 136: "routes.UnlikeTopicSubmit", + 137: "routes.AddAttachToTopicSubmit", + 138: "routes.RemoveAttachFromTopicSubmit", + 139: "routes.ViewTopic", + 140: "routes.CreateReplySubmit", + 141: "routes.ReplyEditSubmit", + 142: "routes.ReplyDeleteSubmit", + 143: "routes.ReplyLikeSubmit", + 144: "routes.ReplyUnlikeSubmit", + 145: "routes.AddAttachToReplySubmit", + 146: "routes.RemoveAttachFromReplySubmit", + 147: "routes.ProfileReplyCreateSubmit", + 148: "routes.ProfileReplyEditSubmit", + 149: "routes.ProfileReplyDeleteSubmit", + 150: "routes.PollVote", + 151: "routes.PollResults", + 152: "routes.AccountLogin", + 153: "routes.AccountRegister", + 154: "routes.AccountLogout", + 155: "routes.AccountLoginSubmit", + 156: "routes.AccountLoginMFAVerify", + 157: "routes.AccountLoginMFAVerifySubmit", + 158: "routes.AccountRegisterSubmit", + 159: "routes.AccountPasswordReset", + 160: "routes.AccountPasswordResetSubmit", + 161: "routes.AccountPasswordResetToken", + 162: "routes.AccountPasswordResetTokenSubmit", + 163: "routes.DynamicRoute", + 164: "routes.UploadedFile", + 165: "routes.StaticFile", + 166: "routes.RobotsTxt", + 167: "routes.SitemapXml", + 168: "routes.OpenSearchXml", + 169: "routes.BadRoute", + 170: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -696,7 +699,7 @@ type HTTPSRedirect struct {} func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - co.RouteViewCounter.Bump(169) + co.RouteViewCounter.Bump(170) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -904,7 +907,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { co.GlobalViewCounter.Bump() if prefix == "/s" { //old prefix: /static - co.RouteViewCounter.Bump(164) + co.RouteViewCounter.Bump(165) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -917,6 +920,10 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { r.requestLogger.Print("before PreRoute") } + /*if c.Dev.QuicPort != 0 { + w.Header().Set("Alt-Svc", "quic=\":"+strconv.Itoa(c.Dev.QuicPort)+"\"; ma=2592000; v=\"44,43,39\", h3-23=\":"+strconv.Itoa(c.Dev.QuicPort)+"\"; ma=3600, h3-24=\":"+strconv.Itoa(c.Dev.QuicPort)+"\"; ma=3600, h2=\":443\"; ma=3600") + }*/ + // Track the user agents. Unfortunately, everyone pretends to be Mozilla, so this'll be a little less efficient than I would like. // TODO: Add a setting to disable this? // TODO: Use a more efficient detector instead of smashing every possible combination in @@ -1646,14 +1653,22 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c co.RouteViewCounter.Bump(76) err = panel.AnalyticsForums(w,req,user) - case "/panel/groups/": + case "/panel/analytics/perf/": + err = c.ParseForm(w,req,user) + if err != nil { + return err + } + co.RouteViewCounter.Bump(77) + err = panel.AnalyticsPerf(w,req,user) + case "/panel/groups/": + co.RouteViewCounter.Bump(78) err = panel.Groups(w,req,user) case "/panel/groups/edit/": - co.RouteViewCounter.Bump(78) + co.RouteViewCounter.Bump(79) err = panel.GroupsEdit(w,req,user,extraData) case "/panel/groups/edit/promotions/": - co.RouteViewCounter.Bump(79) + co.RouteViewCounter.Bump(80) err = panel.GroupsEditPromotions(w,req,user,extraData) case "/panel/groups/promotions/create/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1661,7 +1676,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(80) + co.RouteViewCounter.Bump(81) err = panel.GroupsPromotionsCreateSubmit(w,req,user,extraData) case "/panel/groups/promotions/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1669,10 +1684,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(81) + co.RouteViewCounter.Bump(82) err = panel.GroupsPromotionsDeleteSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/": - co.RouteViewCounter.Bump(82) + co.RouteViewCounter.Bump(83) err = panel.GroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1680,7 +1695,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(83) + co.RouteViewCounter.Bump(84) err = panel.GroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1688,7 +1703,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(84) + co.RouteViewCounter.Bump(85) err = panel.GroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = c.NoSessionMismatch(w,req,user) @@ -1696,7 +1711,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(85) + co.RouteViewCounter.Bump(86) err = panel.GroupsCreateSubmit(w,req,user) case "/panel/backups/": err = c.SuperAdminOnly(w,req,user) @@ -1710,16 +1725,16 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c w.Header().Del("Content-Type") w.Header().Del("Content-Encoding") } - co.RouteViewCounter.Bump(86) + co.RouteViewCounter.Bump(87) err = panel.Backups(w,req,user,extraData) case "/panel/logs/regs/": - co.RouteViewCounter.Bump(87) + co.RouteViewCounter.Bump(88) err = panel.LogsRegs(w,req,user) case "/panel/logs/mod/": - co.RouteViewCounter.Bump(88) + co.RouteViewCounter.Bump(89) err = panel.LogsMod(w,req,user) case "/panel/logs/admin/": - co.RouteViewCounter.Bump(89) + co.RouteViewCounter.Bump(90) err = panel.LogsAdmin(w,req,user) case "/panel/debug/": err = c.AdminOnly(w,req,user) @@ -1727,10 +1742,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(90) + co.RouteViewCounter.Bump(91) err = panel.Debug(w,req,user) default: - co.RouteViewCounter.Bump(91) + co.RouteViewCounter.Bump(92) err = panel.Dashboard(w,req,user) } case "/user": @@ -1741,7 +1756,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(92) + co.RouteViewCounter.Bump(93) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1753,7 +1768,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(93) + co.RouteViewCounter.Bump(94) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1770,7 +1785,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(94) + co.RouteViewCounter.Bump(95) err = routes.AccountEditPasswordSubmit(w,req,user) case "/user/edit/avatar/submit/": err = c.MemberOnly(w,req,user) @@ -1787,7 +1802,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(95) + co.RouteViewCounter.Bump(96) err = routes.AccountEditAvatarSubmit(w,req,user) case "/user/edit/avatar/revoke/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1800,7 +1815,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(96) + co.RouteViewCounter.Bump(97) err = routes.AccountEditRevokeAvatarSubmit(w,req,user) case "/user/edit/username/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1813,7 +1828,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(97) + co.RouteViewCounter.Bump(98) err = routes.AccountEditUsernameSubmit(w,req,user) case "/user/edit/privacy/": err = c.MemberOnly(w,req,user) @@ -1821,7 +1836,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(98) + co.RouteViewCounter.Bump(99) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1838,7 +1853,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(99) + co.RouteViewCounter.Bump(100) err = routes.AccountEditPrivacySubmit(w,req,user) case "/user/edit/mfa/": err = c.MemberOnly(w,req,user) @@ -1846,7 +1861,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(100) + co.RouteViewCounter.Bump(101) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1858,7 +1873,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(101) + co.RouteViewCounter.Bump(102) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1875,7 +1890,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(102) + co.RouteViewCounter.Bump(103) err = routes.AccountEditMFASetupSubmit(w,req,user) case "/user/edit/mfa/disable/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1888,7 +1903,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(103) + co.RouteViewCounter.Bump(104) err = routes.AccountEditMFADisableSubmit(w,req,user) case "/user/edit/email/": err = c.MemberOnly(w,req,user) @@ -1896,14 +1911,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(104) + co.RouteViewCounter.Bump(105) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountEditEmail(w,req,user,head) case "/user/edit/token/": - co.RouteViewCounter.Bump(105) + co.RouteViewCounter.Bump(106) err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData) case "/user/edit/logins/": err = c.MemberOnly(w,req,user) @@ -1911,7 +1926,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(106) + co.RouteViewCounter.Bump(107) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1923,7 +1938,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(107) + co.RouteViewCounter.Bump(108) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1935,7 +1950,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(108) + co.RouteViewCounter.Bump(109) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1947,7 +1962,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(109) + co.RouteViewCounter.Bump(110) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1959,7 +1974,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(110) + co.RouteViewCounter.Bump(111) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1971,7 +1986,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(111) + co.RouteViewCounter.Bump(112) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1988,7 +2003,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(112) + co.RouteViewCounter.Bump(113) err = routes.ConvosCreateSubmit(w,req,user) case "/user/convo/create/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2001,7 +2016,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(113) + co.RouteViewCounter.Bump(114) err = routes.ConvosCreateReplySubmit(w,req,user,extraData) case "/user/convo/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2014,7 +2029,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(114) + co.RouteViewCounter.Bump(115) err = routes.ConvosDeleteReplySubmit(w,req,user,extraData) case "/user/convo/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2027,7 +2042,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(115) + co.RouteViewCounter.Bump(116) err = routes.ConvosEditReplySubmit(w,req,user,extraData) case "/user/block/create/": err = c.MemberOnly(w,req,user) @@ -2035,7 +2050,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(116) + co.RouteViewCounter.Bump(117) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2052,7 +2067,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(117) + co.RouteViewCounter.Bump(118) err = routes.RelationsBlockCreateSubmit(w,req,user,extraData) case "/user/block/remove/": err = c.MemberOnly(w,req,user) @@ -2060,7 +2075,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(118) + co.RouteViewCounter.Bump(119) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2077,11 +2092,11 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(119) + co.RouteViewCounter.Bump(120) err = routes.RelationsBlockRemoveSubmit(w,req,user,extraData) default: req.URL.Path += extraData - co.RouteViewCounter.Bump(120) + co.RouteViewCounter.Bump(121) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2101,7 +2116,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(121) + co.RouteViewCounter.Bump(122) err = routes.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = c.NoSessionMismatch(w,req,user) @@ -2114,7 +2129,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(122) + co.RouteViewCounter.Bump(123) err = routes.UnbanUser(w,req,user,extraData) case "/users/activate/": err = c.NoSessionMismatch(w,req,user) @@ -2127,7 +2142,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(123) + co.RouteViewCounter.Bump(124) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = c.MemberOnly(w,req,user) @@ -2135,7 +2150,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(124) + co.RouteViewCounter.Bump(125) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2152,7 +2167,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(125) + co.RouteViewCounter.Bump(126) err = routes.DeletePostsSubmit(w,req,user,extraData) } case "/topic": @@ -2172,7 +2187,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(126) + co.RouteViewCounter.Bump(127) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2185,7 +2200,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(127) + co.RouteViewCounter.Bump(128) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2199,7 +2214,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - co.RouteViewCounter.Bump(128) + co.RouteViewCounter.Bump(129) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2212,7 +2227,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(129) + co.RouteViewCounter.Bump(130) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2225,7 +2240,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(130) + co.RouteViewCounter.Bump(131) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2239,7 +2254,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - co.RouteViewCounter.Bump(131) + co.RouteViewCounter.Bump(132) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2252,7 +2267,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(132) + co.RouteViewCounter.Bump(133) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2265,7 +2280,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(133) + co.RouteViewCounter.Bump(134) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2278,7 +2293,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(134) + co.RouteViewCounter.Bump(135) err = routes.LikeTopicSubmit(w,req,user,extraData) case "/topic/unlike/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2291,7 +2306,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(135) + co.RouteViewCounter.Bump(136) err = routes.UnlikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2308,7 +2323,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(136) + co.RouteViewCounter.Bump(137) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2321,10 +2336,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(137) + co.RouteViewCounter.Bump(138) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - co.RouteViewCounter.Bump(138) + co.RouteViewCounter.Bump(139) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2348,7 +2363,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(139) + co.RouteViewCounter.Bump(140) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2361,7 +2376,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(140) + co.RouteViewCounter.Bump(141) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2374,7 +2389,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(141) + co.RouteViewCounter.Bump(142) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2387,7 +2402,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(142) + co.RouteViewCounter.Bump(143) err = routes.ReplyLikeSubmit(w,req,user,extraData) case "/reply/unlike/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2400,7 +2415,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(143) + co.RouteViewCounter.Bump(144) err = routes.ReplyUnlikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2417,7 +2432,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(144) + co.RouteViewCounter.Bump(145) err = routes.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2430,7 +2445,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(145) + co.RouteViewCounter.Bump(146) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2446,7 +2461,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(146) + co.RouteViewCounter.Bump(147) err = routes.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2459,7 +2474,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(147) + co.RouteViewCounter.Bump(148) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2472,7 +2487,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(148) + co.RouteViewCounter.Bump(149) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2488,23 +2503,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(149) + co.RouteViewCounter.Bump(150) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - co.RouteViewCounter.Bump(150) + co.RouteViewCounter.Bump(151) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - co.RouteViewCounter.Bump(151) + co.RouteViewCounter.Bump(152) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountLogin(w,req,user,head) case "/accounts/create/": - co.RouteViewCounter.Bump(152) + co.RouteViewCounter.Bump(153) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2521,7 +2536,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(153) + co.RouteViewCounter.Bump(154) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2529,10 +2544,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(154) + co.RouteViewCounter.Bump(155) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - co.RouteViewCounter.Bump(155) + co.RouteViewCounter.Bump(156) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2544,7 +2559,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(156) + co.RouteViewCounter.Bump(157) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2552,10 +2567,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(157) + co.RouteViewCounter.Bump(158) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - co.RouteViewCounter.Bump(158) + co.RouteViewCounter.Bump(159) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2567,10 +2582,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(159) + co.RouteViewCounter.Bump(160) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - co.RouteViewCounter.Bump(160) + co.RouteViewCounter.Bump(161) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2582,7 +2597,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(161) + co.RouteViewCounter.Bump(162) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2599,7 +2614,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c h.Del("Content-Type") h.Del("Content-Encoding") } - co.RouteViewCounter.Bump(163) + co.RouteViewCounter.Bump(164) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2609,7 +2624,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c // TODO: Add support for favicons and robots.txt files switch(extraData) { case "robots.txt": - co.RouteViewCounter.Bump(165) + co.RouteViewCounter.Bump(166) return routes.RobotsTxt(w,req) case "favicon.ico": gzw, ok := w.(c.GzipResponseWriter) @@ -2623,10 +2638,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c routes.StaticFile(w,req) return nil case "opensearch.xml": - co.RouteViewCounter.Bump(167) + co.RouteViewCounter.Bump(168) return routes.OpenSearchXml(w,req) /*case "sitemap.xml": - co.RouteViewCounter.Bump(166) + co.RouteViewCounter.Bump(167) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2637,7 +2652,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - co.RouteViewCounter.Bump(162) // TODO: Be more specific about *which* dynamic route it is + co.RouteViewCounter.Bump(163) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2648,7 +2663,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - co.RouteViewCounter.Bump(168) + co.RouteViewCounter.Bump(169) return c.NotFound(w,req,nil) } return err diff --git a/langs/english.json b/langs/english.json index 80d851b6..94b6cb25 100644 --- a/langs/english.json +++ b/langs/english.json @@ -795,17 +795,18 @@ "panel_menu_menus":"Menus", "panel_menu_widgets":"Widgets", "panel_menu_events":"Events", - "panel_menu_statistics":"Statistics", - "panel_menu_statistics_posts":"Posts", - "panel_menu_statistics_topics":"Topics", - "panel_menu_statistics_forums":"Forums", - "panel_menu_statistics_routes":"Routes", - "panel_menu_statistics_agents":"Agents", - "panel_menu_statistics_systems":"Systems", - "panel_menu_statistics_languages":"Languages", - "panel_menu_statistics_referrers":"Referrers", - "panel_menu_statistics_memory":"Memory", - "panel_menu_statistics_active_memory":"Active Memory", + "panel_menu_stats":"Statistics", + "panel_menu_stats_posts":"Posts", + "panel_menu_stats_topics":"Topics", + "panel_menu_stats_forums":"Forums", + "panel_menu_stats_routes":"Routes", + "panel_menu_stats_agents":"Agents", + "panel_menu_stats_systems":"Systems", + "panel_menu_stats_languages":"Languages", + "panel_menu_stats_referrers":"Referrers", + "panel_menu_stats_memory":"Memory", + "panel_menu_stats_active_memory":"Active Memory", + "panel_menu_stats_perf":"Performance", "panel_menu_reports":"Reports", "panel_menu_logs":"Logs", "panel_menu_logs_registrations":"Registrations", @@ -975,56 +976,60 @@ "panel_pages_title":"Title", "panel_pages_edit_update_button":"Update Page", - "panel_statistics_views_head_suffix":" Views", - "panel_statistics_user_agents_head":"User Agents", - "panel_statistics_forums_head":"Forums", - "panel_statistics_languages_head":"Languages", - "panel_statistics_post_counts_head":"Post Counts", - "panel_statistics_referrers_head":"Referrers", - "panel_statistics_routes_head":"Routes", - "panel_statistics_operating_systems_head":"Operating Systems", - "panel_statistics_topic_counts_head":"Topic Counts", - "panel_statistics_requests_head":"Requests", - "panel_statistics_memory_head":"Memory Usage", - "panel_statistics_active_memory_head":"Active Memory", + "panel_stats_views_head_suffix":" Views", + "panel_stats_user_agents_head":"User Agents", + "panel_stats_forums_head":"Forums", + "panel_stats_languages_head":"Languages", + "panel_stats_post_counts_head":"Post Counts", + "panel_stats_referrers_head":"Referrers", + "panel_stats_routes_head":"Routes", + "panel_stats_operating_systems_head":"Operating Systems", + "panel_stats_topic_counts_head":"Topic Counts", + "panel_stats_requests_head":"Requests", + "panel_stats_memory_head":"Memory Usage", + "panel_stats_active_memory_head":"Active Memory", + "panel_stats_perf_head":"Performance", - "panel_statistics_spam_hide":"Hide Spam", - "panel_statistics_spam_show":"Show Spam", - "panel_statistics_memory_type_total":"Total", - "panel_statistics_memory_type_stack":"Stack", - "panel_statistics_memory_type_heap":"Heap", + "panel_stats_spam_hide":"Hide Spam", + "panel_stats_spam_show":"Show Spam", + "panel_stats_memory_type_total":"Total", + "panel_stats_memory_type_stack":"Stack", + "panel_stats_memory_type_heap":"Heap", + "panel_stats_perf_low":"Low", + "panel_stats_perf_high":"High", + "panel_stats_perf_avg":"Average", - "panel_statistics_time_range_one_year":"1 year", - "panel_statistics_time_range_three_months":"3 months", - "panel_statistics_time_range_one_month":"1 month", - "panel_statistics_time_range_one_week":"1 week", - "panel_statistics_time_range_two_days":"2 days", - "panel_statistics_time_range_one_day":"1 day", - "panel_statistics_time_range_twelve_hours":"12 hours", - "panel_statistics_time_range_six_hours":"6 hours", + "panel_stats_time_range_one_year":"1 year", + "panel_stats_time_range_three_months":"3 months", + "panel_stats_time_range_one_month":"1 month", + "panel_stats_time_range_one_week":"1 week", + "panel_stats_time_range_two_days":"2 days", + "panel_stats_time_range_one_day":"1 day", + "panel_stats_time_range_twelve_hours":"12 hours", + "panel_stats_time_range_six_hours":"6 hours", - "panel_statistics_post_counts_chart_aria":"Post Chart", - "panel_statistics_topic_counts_chart_aria":"Topic Chart", - "panel_statistics_requests_chart_aria":"Requests Chart", - "panel_statistics_memory_chart_aria":"Memory Use Chart", - "panel_statistics_details_head":"Details", - "panel_statistics_post_counts_table_aria":"Post Table, this has the same information as the post chart", - "panel_statistics_topic_counts_table_aria":"Topic Table, this has the same information as the topic chart", - "panel_statistics_route_views_table_aria":"View Table, this has the same information as the view chart", - "panel_statistics_requests_table_aria":"View Table, this has the same information as the view chart", - "panel_statistics_memory_table_aria":"Memory Use Table, this has the same information as the memory use chart", - "panel_statistics_views_suffix":" views", - "panel_statistics_posts_suffix":" posts", - "panel_statistics_topics_suffix":" topics", + "panel_stats_post_counts_chart_aria":"Post Chart", + "panel_stats_topic_counts_chart_aria":"Topic Chart", + "panel_stats_requests_chart_aria":"Requests Chart", + "panel_stats_memory_chart_aria":"Memory Use Chart", + "panel_stats_details_head":"Details", + "panel_stats_post_counts_table_aria":"Post Table, this has the same information as the post chart", + "panel_stats_topic_counts_table_aria":"Topic Table, this has the same information as the topic chart", + "panel_stats_route_views_table_aria":"View Table, this has the same information as the view chart", + "panel_stats_requests_table_aria":"View Table, this has the same information as the view chart", + "panel_stats_memory_table_aria":"Memory Use Table, this has the same information as the memory use chart", + "panel_stats_views_suffix":" views", + "panel_stats_posts_suffix":" posts", + "panel_stats_topics_suffix":" topics", - "panel_statistics_user_agents_no_user_agents":"No user agents could be found in the selected time range", - "panel_statistics_forums_no_forums":"No forum view counts could be found in the selected time range", - "panel_statistics_languages_no_languages":"No language could be found in the selected time range", - "panel_statistics_post_counts_no_post_counts":"No posts could be found in the selected time range", - "panel_statistics_referrers_no_referrers":"No referrers could be found in the selected time range", - "panel_statistics_routes_no_routes":"No route view counts could be found in the selected time range", - "panel_statistics_operating_systems_no_operating_systems":"No operating systems could be found in the selected time range", - "panel_statistics_memory_no_memory":"No memory chunks could be found in the selected time range", + "panel_stats_user_agents_no_user_agents":"No user agents could be found in the selected time range", + "panel_stats_forums_no_forums":"No forum view counts could be found in the selected time range", + "panel_stats_languages_no_languages":"No language could be found in the selected time range", + "panel_stats_post_counts_no_post_counts":"No posts could be found in the selected time range", + "panel_stats_referrers_no_referrers":"No referrers could be found in the selected time range", + "panel_stats_routes_no_routes":"No route view counts could be found in the selected time range", + "panel_stats_operating_systems_no_operating_systems":"No operating systems could be found in the selected time range", + "panel_stats_memory_no_memory":"No memory chunks could be found in the selected time range", "panel_logs_menu_head":"Logs", "panel_logs_reg_head":"Registrations", @@ -1173,6 +1178,13 @@ "panel_debug_goroutine_count_label":"Goroutines", "panel_debug_cpu_count_label":"CPUs", + "panel_debug_tasks":"Tasks", + "panel_debug_tasks_half_second":"Half Second", + "panel_debug_tasks_second":"Second", + "panel_debug_tasks_fifteen_minute":"Fifteen Minute", + "panel_debug_tasks_hour":"Hourly", + "panel_debug_tasks_shutdown":"Shutdown", + "panel_debug_memory_stats":"Memory Statistics", "panel_debug_memory_stats_sys":"Sys", "panel_debug_memory_stats_heapsys":"HeapSys", diff --git a/patcher/patches.go b/patcher/patches.go index e806f525..ce8f11f3 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -49,6 +49,7 @@ func init() { addPatch(29, patch29) addPatch(30, patch30) addPatch(31, patch31) + addPatch(32, patch32) } func patch0(scanner *bufio.Scanner) (err error) { @@ -892,3 +893,15 @@ func patch31(scanner *bufio.Scanner) error { } return nil } + + +func patch32(scanner *bufio.Scanner) error { + return execStmt(qgen.Builder.CreateTable("perfchunks", "", "", + []tC{ + tC{"low", "int", 0, false, false, "0"}, + tC{"high", "int", 0, false, false, "0"}, + tC{"avg", "int", 0, false, false, "0"}, + tC{"createdAt", "datetime", 0, false, false, ""}, + }, nil, +)) +} \ No newline at end of file diff --git a/public/analytics.js b/public/analytics.js index c7cc5646..9424a81e 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -38,6 +38,46 @@ function memStuff(window, document, Chartist) { }; } +function perfStuff(window, document, Chartist) { + 'use strict'; + + Chartist.plugins = Chartist.plugins || {}; + Chartist.plugins.perfUnits = function(options) { + options = Chartist.extend({}, {}, options); + + return function perfUnits(chart) { + if(!chart instanceof Chartist.Line) return; + + chart.on('created', function() { + console.log("running created") + const vbits = document.getElementsByClassName("ct-vertical"); + if(vbits==null) return; + + let tbits = []; + for(let i = 0; i < vbits.length; i++) { + tbits[i] = vbits[i].innerHTML; + } + console.log("tbits:",tbits); + + const calc = (places) => { + if(places==3) return; + + const matcher = vbits[0].innerHTML; + let allMatch = true; + for(let i = 0; i < tbits.length; i++) { + let val = convertPerfUnit(tbits[i], places); + if(val!=matcher) allMatch = false; + vbits[i].innerHTML = val; + } + + if(allMatch) calc(places + 1); + } + calc(0); + }); + }; + }; +} + const Kilobyte = 1024; const Megabyte = Kilobyte * 1024; const Gigabyte = Megabyte * 1024; @@ -60,9 +100,30 @@ function convertByteUnit(bytes, places = 0) { } } +let ms = 1000; +let sec = ms * 1000; +let min = sec * 60; +let hour = min * 60; +let day = hour * 24; +function convertPerfUnit(quan, places = 0) { + let out; + if(quan >= day) out = [quan / day, "d"]; + else if(quan >= hour) out = [quan / hour, "h"]; + else if(quan >= min) out = [quan / min, "m"]; + else if(quan >= sec) out = [quan / sec, "s"]; + else if(quan >= ms) out = [quan / ms, "ms"]; + else out = [quan,"μs"]; + + if(places==0) return Math.ceil(out[0]) + out[1]; + else { + let ex = Math.pow(10, places); + return (Math.round(out[0], ex) / ex) + out[1]; + } +} + // TODO: Fully localise this // TODO: Load rawLabels and seriesData dynamically rather than potentially fiddling with nonces for the CSP? -function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, bytes = false) { +function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, typ=0) { console.log("buildStatsChart"); console.log("seriesData:",seriesData); let labels = []; @@ -122,7 +183,8 @@ function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, bytes = if(legendNames.length > 0) config.plugins = [ Chartist.plugins.legend({legendNames: legendNames}) ]; - if(bytes) config.plugins.push(Chartist.plugins.byteUnits()); + if(typ==1) config.plugins.push(Chartist.plugins.byteUnits()); + else if(typ==2) config.plugins.push(Chartist.plugins.perfUnits()); Chartist.Line('.ct_chart', { labels: labels, series: seriesData, diff --git a/router_gen/routes.go b/router_gen/routes.go index 0e1198c9..3eac0d71 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -258,6 +258,7 @@ func panelRoutes() *RouteGroup { View("panel.AnalyticsActiveMemory", "/panel/analytics/active-memory/").Before("ParseForm"), View("panel.AnalyticsTopics", "/panel/analytics/topics/").Before("ParseForm"), View("panel.AnalyticsForums", "/panel/analytics/forums/").Before("ParseForm"), + View("panel.AnalyticsPerf", "/panel/analytics/perf/").Before("ParseForm"), View("panel.Groups", "/panel/groups/"), View("panel.GroupsEdit", "/panel/groups/edit/", "extraData"), diff --git a/routes/common.go b/routes/common.go index 7b520a1e..cc32cd77 100644 --- a/routes/common.go +++ b/routes/common.go @@ -8,6 +8,7 @@ import ( "time" c "github.com/Azareal/Gosora/common" + co "github.com/Azareal/Gosora/common/counters" ) var successJSONBytes = []byte(`{"success":1}`) @@ -89,7 +90,7 @@ func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, hea return renderTemplate2(tmplName, tmplName, w, r, header, pi) } -func renderTemplate2(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) c.RouteError { +func renderTemplate2(tmplName, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) c.RouteError { err := renderTemplate3(tmplName, tmplName, w, r, header, pi) if err != nil { return c.InternalError(err, w, r) @@ -114,7 +115,7 @@ func FootHeaders(w http.ResponseWriter, header *c.Header) { } } -func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, h *c.Header, pi interface{}) error { +func renderTemplate3(tmplName, hookName string, w http.ResponseWriter, r *http.Request, h *c.Header, pi interface{}) error { s := h.Stylesheets h.Stylesheets = nil c.PrepResources(&h.CurrentUser, h, h.Theme) @@ -134,9 +135,11 @@ func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r } FootHeaders(w, h) - if h.CurrentUser.IsAdmin { - h.Elapsed1 = time.Since(h.StartedAt).String() - } + since := time.Since(h.StartedAt) + //if h.CurrentUser.IsAdmin { + h.Elapsed1 = since.String() + //} + co.PerfCounter.Push(since) if c.RunPreRenderHook("pre_render_"+hookName, w, r, &h.CurrentUser, pi) { return nil } diff --git a/routes/panel/analytics.go b/routes/panel/analytics.go index bd91426c..2eb646a0 100644 --- a/routes/panel/analytics.go +++ b/routes/panel/analytics.go @@ -11,7 +11,7 @@ import ( c "github.com/Azareal/Gosora/common" p "github.com/Azareal/Gosora/common/phrases" - "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" ) // TODO: Move this to another file, probably common/pages.go @@ -25,11 +25,11 @@ type AnalyticsTimeRange struct { func analyticsTimeRange(rawTimeRange string) (*AnalyticsTimeRange, error) { tRange := &AnalyticsTimeRange{ - Quantity: 6, - Unit: "hour", - Slices: 12, + Quantity: 6, + Unit: "hour", + Slices: 12, SliceWidth: 60 * 30, - Range: "six-hours", + Range: "six-hours", } switch rawTimeRange { @@ -198,6 +198,50 @@ func analyticsRowsToAverageMap2(rows *sql.Rows, labelList []int64, avgMap map[in return avgMap, rows.Err() } +func analyticsRowsToAverageMap3(rows *sql.Rows, labelList []int64, avgMap map[int64]int64, typ int) (map[int64]int64, error) { + defer rows.Close() + for rows.Next() { + var low, high, avg int64 + var createdAt time.Time + err := rows.Scan(&low, &high, &avg, &createdAt) + if err != nil { + return avgMap, err + } + unixCreatedAt := createdAt.Unix() + // TODO: Bulk log this + if c.Dev.SuperDebug { + log.Print("low: ", low) + log.Print("high: ", high) + log.Print("avg: ", avg) + log.Print("createdAt: ", createdAt) + log.Print("unixCreatedAt: ", unixCreatedAt) + } + var dat int64 + switch typ { + case 0: + dat = low + case 1: + dat = high + default: + dat = avg + } + pAvgMap := make(map[int64]pAvg) + for _, value := range labelList { + if unixCreatedAt > value { + prev := pAvgMap[value] + prev.Avg += dat + prev.Tot++ + pAvgMap[value] = prev + break + } + } + for key, pAvg := range pAvgMap { + avgMap[key] = pAvg.Avg / pAvg.Tot + } + } + return avgMap, rows.Err() +} + func PreAnalyticsDetail(w http.ResponseWriter, r *http.Request, user *c.User) (*c.BasePanelPage, c.RouteError) { bp, ferr := buildBasePage(w, r, user, "analytics", "analytics") if ferr != nil { @@ -223,7 +267,7 @@ func AnalyticsViews(w http.ResponseWriter, r *http.Request, user c.User) c.Route c.DebugLog("in panel.AnalyticsViews") // TODO: Add some sort of analytics store / iterator? - rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks").Columns("count,createdAt").Where("route=''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -232,13 +276,13 @@ func AnalyticsViews(w http.ResponseWriter, r *http.Request, user c.User) c.Route return c.InternalError(err, w, r) } - viewList := make([]int64,len(revLabelList)) - viewItems := make([]c.PanelAnalyticsItem,len(revLabelList)) + viewList := make([]int64, len(revLabelList)) + viewItems := make([]c.PanelAnalyticsItem, len(revLabelList)) for i, value := range revLabelList { viewList[i] = viewMap[value] viewItems[i] = c.PanelAnalyticsItem{Time: value, Count: viewMap[value]} } - + graph := c.PanelTimeGraph{Series: [][]int64{viewList}, Labels: labelList} c.DebugLogf("graph: %+v\n", graph) var ttime string @@ -263,7 +307,7 @@ func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user c.User, ro c.DebugLog("in panel.AnalyticsRouteViews") // TODO: Validate the route is valid - rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(route) + rows, err := qgen.NewAcc().Select("viewchunks").Columns("count,createdAt").Where("route=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(route) if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -300,7 +344,7 @@ func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user c.User, ag c.DebugLog("in panel.AnalyticsAgentViews") // TODO: Verify the agent is valid - rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, createdAt").Where("browser = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(agent) + rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count,createdAt").Where("browser=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(agent) if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -343,7 +387,7 @@ func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user c.User, sf c.DebugLog("in panel.AnalyticsForumViews") // TODO: Verify the agent is valid - rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(fid) + rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count,createdAt").Where("forum=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(fid) if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -382,7 +426,7 @@ func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user c.User, s c.DebugLog("in panel.AnalyticsSystemViews") // TODO: Verify the OS name is valid - rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, createdAt").Where("system = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system) + rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count,createdAt").Where("system=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system) if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -421,7 +465,7 @@ func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user c.User, c.DebugLog("in panel.AnalyticsLanguageViews") // TODO: Verify the language code is valid - rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, createdAt").Where("lang = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(lang) + rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count,createdAt").Where("lang=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(lang) if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -460,7 +504,7 @@ func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user c.User, c.DebugLog("in panel.AnalyticsReferrerViews") // TODO: Verify the agent is valid - rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, createdAt").Where("domain = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(domain) + rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count,createdAt").Where("domain=?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(domain) if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -491,7 +535,7 @@ func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user c.User) c.Rout revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) c.DebugLog("in panel.AnalyticsTopics") - rows, err := qgen.NewAcc().Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("topicchunks").Columns("count,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -524,7 +568,7 @@ func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user c.User) c.Route revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) c.DebugLog("in panel.AnalyticsPosts") - rows, err := qgen.NewAcc().Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("postchunks").Columns("count,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -557,7 +601,7 @@ func AnalyticsMemory(w http.ResponseWriter, r *http.Request, user c.User) c.Rout revLabelList, labelList, avgMap := analyticsTimeRangeToLabelList(timeRange) c.DebugLog("in panel.AnalyticsMemory") - rows, err := qgen.NewAcc().Select("memchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("memchunks").Columns("count,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -593,7 +637,7 @@ func AnalyticsActiveMemory(w http.ResponseWriter, r *http.Request, user c.User) revLabelList, labelList, avgMap := analyticsTimeRangeToLabelList(timeRange) c.DebugLog("in panel.AnalyticsActiveMemory") - rows, err := qgen.NewAcc().Select("memchunks").Columns("stack, heap, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("memchunks").Columns("stack,heap,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -626,6 +670,51 @@ func AnalyticsActiveMemory(w http.ResponseWriter, r *http.Request, user c.User) return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right", "analytics", "panel_analytics_active_memory", pi}) } +func AnalyticsPerf(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { + basePage, ferr := PreAnalyticsDetail(w, r, &user) + if ferr != nil { + return ferr + } + timeRange, err := analyticsTimeRange(r.FormValue("timeRange")) + if err != nil { + return c.LocalError(err.Error(), w, r, user) + } + revLabelList, labelList, avgMap := analyticsTimeRangeToLabelList(timeRange) + + c.DebugLog("in panel.AnalyticsPerf") + rows, err := qgen.NewAcc().Select("perfchunks").Columns("low,high,avg,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + if err != nil && err != sql.ErrNoRows { + return c.InternalError(err, w, r) + } + + var typ int + switch r.FormValue("type") { + case "0": + typ = 0 + case "1": + typ = 1 + default: + typ = 2 + } + avgMap, err = analyticsRowsToAverageMap3(rows, labelList, avgMap, typ) + if err != nil { + return c.InternalError(err, w, r) + } + + // TODO: Adjust for the missing chunks in week and month + var avgList []int64 + var avgItems []c.PanelAnalyticsItemUnit + for _, value := range revLabelList { + avgList = append(avgList, avgMap[value]) + cv, cu := c.ConvertByteUnit(float64(avgMap[value])) + avgItems = append(avgItems, c.PanelAnalyticsItemUnit{Time: value, Unit: cu, Count: int64(cv)}) + } + graph := c.PanelTimeGraph{Series: [][]int64{avgList}, Labels: labelList} + c.DebugLogf("graph: %+v\n", graph) + pi := c.PanelAnalyticsPerf{graph, avgItems, timeRange.Range, timeRange.Unit, "time", typ} + return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right", "analytics", "panel_analytics_performance", pi}) +} + func analyticsRowsToRefMap(rows *sql.Rows) (map[string]int, error) { nameMap := make(map[string]int) defer rows.Close() @@ -736,7 +825,7 @@ func AnalyticsForums(w http.ResponseWriter, r *http.Request, user c.User) c.Rout } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, forum, createdAt").Where("forum != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count,forum,createdAt").Where("forum!=''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -820,7 +909,7 @@ func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user c.User) c.Rout } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, route, createdAt").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks").Columns("count,route,createdAt").Where("route!=''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -892,7 +981,7 @@ func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user c.User) c.Rout } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, browser, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count,browser,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -978,7 +1067,7 @@ func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user c.User) c.Rou } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, system, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count,system,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -1042,7 +1131,7 @@ func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user c.User) c.R } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, lang, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count,lang,createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } @@ -1125,7 +1214,7 @@ func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user c.User) c.R return c.LocalError(err.Error(), w, r, user) } - rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() + rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count,domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } diff --git a/routes/panel/debug.go b/routes/panel/debug.go index 5428abfd..53a22c49 100644 --- a/routes/panel/debug.go +++ b/routes/panel/debug.go @@ -38,6 +38,8 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { // TODO: Fetch the adapter from Builder rather than getting it from a global? goroutines := runtime.NumGoroutine() cpus := runtime.NumCPU() + + debugTasks := c.DebugPageTasks{c.ScheduledHalfSecondTaskCount(),c.ScheduledSecondTaskCount(),c.ScheduledFifteenMinuteTaskCount(),c.ScheduledHourTaskCount(),c.ShutdownTaskCount()} var memStats runtime.MemStats runtime.ReadMemStats(&memStats) @@ -113,10 +115,11 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { if fErr != nil { return c.InternalError(fErr,w,r) } - gitSize, _ := c.DirSize("./.git") + //gitSize, _ := c.DirSize("./.git") + gitSize := 0 debugDisk := c.DebugPageDisk{staticSize,attachSize,uploadsSize,logsSize,backupsSize,gitSize} - pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats, debugCache, debugDatabase, debugDisk} + pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus,debugTasks, memStats, debugCache, debugDatabase, debugDisk} return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_dashboard_right", "debug_page", "panel_debug", pi}) } diff --git a/schema/mssql/query_perfchunks.sql b/schema/mssql/query_perfchunks.sql new file mode 100644 index 00000000..835b72b8 --- /dev/null +++ b/schema/mssql/query_perfchunks.sql @@ -0,0 +1,6 @@ +CREATE TABLE [perfchunks] ( + [low] int DEFAULT 0 not null, + [high] int DEFAULT 0 not null, + [avg] int DEFAULT 0 not null, + [createdAt] datetime not null +); \ No newline at end of file diff --git a/schema/mssql/query_topics.sql b/schema/mssql/query_topics.sql index cd4de826..fa917394 100644 --- a/schema/mssql/query_topics.sql +++ b/schema/mssql/query_topics.sql @@ -21,5 +21,6 @@ CREATE TABLE [topics] ( [poll] int DEFAULT 0 not null, [data] nvarchar (200) DEFAULT '' not null, primary key([tid]), + fulltext key([title]), fulltext key([content]) ); \ No newline at end of file diff --git a/schema/mysql/inserts.sql b/schema/mysql/inserts.sql index 2af63517..a699fee1 100644 --- a/schema/mysql/inserts.sql +++ b/schema/mysql/inserts.sql @@ -6,9 +6,6 @@ ALTER TABLE `emails` ADD INDEX `i_uid` (`uid`);; ALTER TABLE `attachments` ADD INDEX `i_originID` (`originID`);; ALTER TABLE `attachments` ADD INDEX `i_path` (`path`);; ALTER TABLE `activity_stream_matches` ADD INDEX `i_watcher` (`watcher`);; -ALTER TABLE `topics` ADD FULLTEXT(`title`); -ALTER TABLE `topics` ADD FULLTEXT(`content`); -ALTER TABLE `replies` ADD FULLTEXT(`content`); INSERT INTO `sync`(`last_update`) VALUES (UTC_TIMESTAMP()); INSERT INTO `settings`(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3'); INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int'); diff --git a/schema/mysql/query_perfchunks.sql b/schema/mysql/query_perfchunks.sql new file mode 100644 index 00000000..508af153 --- /dev/null +++ b/schema/mysql/query_perfchunks.sql @@ -0,0 +1,6 @@ +CREATE TABLE `perfchunks` ( + `low` int DEFAULT 0 not null, + `high` int DEFAULT 0 not null, + `avg` int DEFAULT 0 not null, + `createdAt` datetime not null +); \ No newline at end of file diff --git a/schema/mysql/query_topics.sql b/schema/mysql/query_topics.sql index 2cde46c4..3adb03df 100644 --- a/schema/mysql/query_topics.sql +++ b/schema/mysql/query_topics.sql @@ -21,5 +21,6 @@ CREATE TABLE `topics` ( `poll` int DEFAULT 0 not null, `data` varchar(200) DEFAULT '' not null, primary key(`tid`), + fulltext key(`title`), fulltext key(`content`) ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; \ No newline at end of file diff --git a/schema/pgsql/query_perfchunks.sql b/schema/pgsql/query_perfchunks.sql new file mode 100644 index 00000000..894c27e4 --- /dev/null +++ b/schema/pgsql/query_perfchunks.sql @@ -0,0 +1,6 @@ +CREATE TABLE "perfchunks" ( + `low` int DEFAULT 0 not null, + `high` int DEFAULT 0 not null, + `avg` int DEFAULT 0 not null, + `createdAt` timestamp not null +); \ No newline at end of file diff --git a/schema/pgsql/query_topics.sql b/schema/pgsql/query_topics.sql index 75994d70..6178e811 100644 --- a/schema/pgsql/query_topics.sql +++ b/schema/pgsql/query_topics.sql @@ -21,5 +21,6 @@ CREATE TABLE "topics" ( `poll` int DEFAULT 0 not null, `data` varchar (200) DEFAULT '' not null, primary key(`tid`), + fulltext key(`title`), fulltext key(`content`) ); \ No newline at end of file diff --git a/templates/panel_analytics_active_memory.html b/templates/panel_analytics_active_memory.html index dcd06b1b..966bcf96 100644 --- a/templates/panel_analytics_active_memory.html +++ b/templates/panel_analytics_active_memory.html @@ -1,10 +1,10 @@
-

{{lang "panel_statistics_active_memory_head"}}

+

{{lang "panel_stats_active_memory_head"}}

{{template "panel_analytics_time_range_month.html" . }} @@ -12,19 +12,19 @@
-
+
-

{{lang "panel_statistics_details_head"}}

+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
- {{.Time}} + {{.Time}} {{.Count}}{{.Unit}}
- {{else}}
{{lang "panel_statistics_memory_no_memory"}}
{{end}} + {{else}}
{{lang "panel_stats_memory_no_memory"}}
{{end}}
{{template "panel_analytics_script_memory.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_agent_views.html b/templates/panel_analytics_agent_views.html index 120a51c7..585f97b0 100644 --- a/templates/panel_analytics_agent_views.html +++ b/templates/panel_analytics_agent_views.html @@ -1,6 +1,6 @@
-

{{.FriendlyAgent}}{{lang "panel_statistics_views_head_suffix"}}

+

{{.FriendlyAgent}}{{lang "panel_stats_views_head_suffix"}}

{{template "panel_analytics_time_range.html" . }}
diff --git a/templates/panel_analytics_agents.html b/templates/panel_analytics_agents.html index b865adc9..52644e73 100644 --- a/templates/panel_analytics_agents.html +++ b/templates/panel_analytics_agents.html @@ -1,6 +1,6 @@
-

{{lang "panel_statistics_user_agents_head"}}

+

{{lang "panel_stats_user_agents_head"}}

{{template "panel_analytics_time_range.html" . }}
@@ -12,8 +12,8 @@ {{range .ItemList}}
{{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
- {{else}}
{{lang "panel_statistics_user_agents_no_user_agents"}}
{{end}} + {{else}}
{{lang "panel_stats_user_agents_no_user_agents"}}
{{end}}
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_forum_views.html b/templates/panel_analytics_forum_views.html index c09381e3..e347549d 100644 --- a/templates/panel_analytics_forum_views.html +++ b/templates/panel_analytics_forum_views.html @@ -1,6 +1,6 @@
-

{{.FriendlyAgent}}{{lang "panel_statistics_views_head_suffix"}}

+

{{.FriendlyAgent}}{{lang "panel_stats_views_head_suffix"}}

{{template "panel_analytics_time_range.html" . }}
diff --git a/templates/panel_analytics_forums.html b/templates/panel_analytics_forums.html index 0e87aaa7..7ae1f00e 100644 --- a/templates/panel_analytics_forums.html +++ b/templates/panel_analytics_forums.html @@ -1,6 +1,6 @@
-

{{lang "panel_statistics_forums_head"}}

+

{{lang "panel_stats_forums_head"}}

{{template "panel_analytics_time_range.html" . }}
@@ -12,8 +12,8 @@ {{range .ItemList}}
{{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
- {{else}}
{{lang "panel_statistics_forums_no_forums"}}
{{end}} + {{else}}
{{lang "panel_stats_forums_no_forums"}}
{{end}}
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_lang_views.html b/templates/panel_analytics_lang_views.html index 40c45ec9..b3457e35 100644 --- a/templates/panel_analytics_lang_views.html +++ b/templates/panel_analytics_lang_views.html @@ -1,6 +1,6 @@
-

{{.FriendlyAgent}}{{lang "panel_statistics_views_head_suffix"}}

+

{{.FriendlyAgent}}{{lang "panel_stats_views_head_suffix"}}

{{template "panel_analytics_time_range.html" . }}
diff --git a/templates/panel_analytics_langs.html b/templates/panel_analytics_langs.html index 718bb19f..09ff0990 100644 --- a/templates/panel_analytics_langs.html +++ b/templates/panel_analytics_langs.html @@ -1,6 +1,6 @@
-

{{lang "panel_statistics_languages_head"}}

+

{{lang "panel_stats_languages_head"}}

{{template "panel_analytics_time_range.html" . }}
@@ -12,8 +12,8 @@ {{range .ItemList}}
{{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
- {{else}}
{{lang "panel_statistics_languages_no_languages"}}
{{end}} + {{else}}
{{lang "panel_stats_languages_no_languages"}}
{{end}} {{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_memory.html b/templates/panel_analytics_memory.html index 5408b208..6d46060f 100644 --- a/templates/panel_analytics_memory.html +++ b/templates/panel_analytics_memory.html @@ -1,24 +1,24 @@
-

{{lang "panel_statistics_memory_head"}}

+

{{lang "panel_stats_memory_head"}}

{{template "panel_analytics_time_range_month.html" . }}
-
+
-

{{lang "panel_statistics_details_head"}}

+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} {{.Count}}{{.Unit}}
- {{else}}
{{lang "panel_statistics_memory_no_memory"}}
{{end}} + {{else}}
{{lang "panel_stats_memory_no_memory"}}
{{end}}
{{template "panel_analytics_script_memory.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_performance.html b/templates/panel_analytics_performance.html new file mode 100644 index 00000000..49b3e491 --- /dev/null +++ b/templates/panel_analytics_performance.html @@ -0,0 +1,30 @@ +
+
+

{{lang "panel_stats_perf_head"}}

+ + + {{template "panel_analytics_time_range_month.html" . }} +
+
+
+
+
+
+
+
+

{{lang "panel_stats_details_head"}}

+
+
+
+ {{range .ViewItems}} +
+ {{.Time}} + {{.Count}}{{.Unit}} +
+ {{else}}
{{lang "panel_stats_perf_no_perf"}}
{{end}} +
+{{template "panel_analytics_script_perf.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_posts.html b/templates/panel_analytics_posts.html index db31a7ca..e097e908 100644 --- a/templates/panel_analytics_posts.html +++ b/templates/panel_analytics_posts.html @@ -1,24 +1,24 @@
-

{{lang "panel_statistics_post_counts_head"}}

+

{{lang "panel_stats_post_counts_head"}}

{{template "panel_analytics_time_range.html" . }}
-
+
-

{{lang "panel_statistics_details_head"}}

+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} - {{.Count}}{{lang "panel_statistics_posts_suffix"}} + {{.Count}}{{lang "panel_stats_posts_suffix"}}
- {{else}}
{{lang "panel_statistics_post_counts_no_post_counts"}}
{{end}} + {{else}}
{{lang "panel_stats_post_counts_no_post_counts"}}
{{end}}
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_referrer_views.html b/templates/panel_analytics_referrer_views.html index 7ec69251..50fce0fe 100644 --- a/templates/panel_analytics_referrer_views.html +++ b/templates/panel_analytics_referrer_views.html @@ -1,6 +1,6 @@
-

{{.Agent}}{{lang "panel_statistics_views_head_suffix"}}

+

{{.Agent}}{{lang "panel_stats_views_head_suffix"}}

{{template "panel_analytics_time_range.html" . }}
diff --git a/templates/panel_analytics_referrers.html b/templates/panel_analytics_referrers.html index 9dbd8544..0fde1758 100644 --- a/templates/panel_analytics_referrers.html +++ b/templates/panel_analytics_referrers.html @@ -1,9 +1,9 @@
-

{{lang "panel_statistics_referrers_head"}}

+

{{lang "panel_stats_referrers_head"}}

{{template "panel_analytics_time_range.html" . }} @@ -14,7 +14,7 @@ {{range .ItemList}}
{{.Agent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
- {{else}}
{{lang "panel_statistics_referrers_no_referrers"}}
{{end}} + {{else}}
{{lang "panel_stats_referrers_no_referrers"}}
{{end}}
\ No newline at end of file diff --git a/templates/panel_analytics_route_views.html b/templates/panel_analytics_route_views.html index 6a6a9518..c16457bd 100644 --- a/templates/panel_analytics_route_views.html +++ b/templates/panel_analytics_route_views.html @@ -1,6 +1,6 @@
-

{{.Route}}{{lang "panel_statistics_views_head_suffix"}}

+

{{.Route}}{{lang "panel_stats_views_head_suffix"}}

{{template "panel_analytics_time_range.html" . }}
@@ -9,13 +9,13 @@
-
+
{{range .ViewItems}}
{{.Time}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
{{end}}
diff --git a/templates/panel_analytics_routes.html b/templates/panel_analytics_routes.html index 5ae4275f..bf46e393 100644 --- a/templates/panel_analytics_routes.html +++ b/templates/panel_analytics_routes.html @@ -1,6 +1,6 @@
-

{{lang "panel_statistics_routes_head"}}

+

{{lang "panel_stats_routes_head"}}

{{template "panel_analytics_time_range.html" . }}
@@ -12,8 +12,8 @@ {{range .ItemList}}
{{.Route}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
- {{else}}
{{lang "panel_statistics_routes_no_routes"}}
{{end}} + {{else}}
{{lang "panel_stats_routes_no_routes"}}
{{end}}
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_script_memory.html b/templates/panel_analytics_script_memory.html index fb44e331..eece85a4 100644 --- a/templates/panel_analytics_script_memory.html +++ b/templates/panel_analytics_script_memory.html @@ -1,21 +1,21 @@ \ No newline at end of file diff --git a/templates/panel_analytics_script_perf.html b/templates/panel_analytics_script_perf.html new file mode 100644 index 00000000..30bb1f5a --- /dev/null +++ b/templates/panel_analytics_script_perf.html @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/templates/panel_analytics_system_views.html b/templates/panel_analytics_system_views.html index 75796d38..f8214aa7 100644 --- a/templates/panel_analytics_system_views.html +++ b/templates/panel_analytics_system_views.html @@ -1,6 +1,6 @@
-

{{.FriendlyAgent}}{{lang "panel_statistics_views_head_suffix"}}

+

{{.FriendlyAgent}}{{lang "panel_stats_views_head_suffix"}}

{{template "panel_analytics_time_range.html" . }}
diff --git a/templates/panel_analytics_systems.html b/templates/panel_analytics_systems.html index 4185f409..4dbb3a3a 100644 --- a/templates/panel_analytics_systems.html +++ b/templates/panel_analytics_systems.html @@ -1,6 +1,6 @@
-

{{lang "panel_statistics_operating_systems_head"}}

+

{{lang "panel_stats_operating_systems_head"}}

{{template "panel_analytics_time_range.html" . }}
@@ -12,8 +12,8 @@ {{range .ItemList}}
{{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
- {{else}}
{{lang "panel_statistics_operating_systems_no_operating_systems"}}
{{end}} + {{else}}
{{lang "panel_stats_operating_systems_no_operating_systems"}}
{{end}}
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_time_range.html b/templates/panel_analytics_time_range.html index 21b2bd3c..3f82507f 100644 --- a/templates/panel_analytics_time_range.html +++ b/templates/panel_analytics_time_range.html @@ -1,11 +1,11 @@ \ No newline at end of file diff --git a/templates/panel_analytics_time_range_month.html b/templates/panel_analytics_time_range_month.html index bdaa6481..712aff47 100644 --- a/templates/panel_analytics_time_range_month.html +++ b/templates/panel_analytics_time_range_month.html @@ -1,9 +1,9 @@ \ No newline at end of file diff --git a/templates/panel_analytics_topics.html b/templates/panel_analytics_topics.html index a7888f7b..5a2092e8 100644 --- a/templates/panel_analytics_topics.html +++ b/templates/panel_analytics_topics.html @@ -1,23 +1,23 @@
-

{{lang "panel_statistics_topic_counts_head"}}

+

{{lang "panel_stats_topic_counts_head"}}

{{template "panel_analytics_time_range.html" . }}
-
+
-

{{lang "panel_statistics_details_head"}}

+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
- {{.Time}} - {{.Count}}{{lang "panel_statistics_topics_suffix"}} + {{.Time}} + {{.Count}}{{lang "panel_stats_topics_suffix"}}
{{end}}
diff --git a/templates/panel_analytics_views.html b/templates/panel_analytics_views.html index 6720987a..952e1eb8 100644 --- a/templates/panel_analytics_views.html +++ b/templates/panel_analytics_views.html @@ -1,23 +1,23 @@
-

{{lang "panel_statistics_requests_head"}}

+

{{lang "panel_stats_requests_head"}}

{{template "panel_analytics_time_range.html" . }}
-
+
-

{{lang "panel_statistics_details_head"}}

+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
- {{.Time}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} + {{.Time}} + {{.Count}}{{lang "panel_stats_views_suffix"}}
{{end}}
diff --git a/templates/panel_debug.html b/templates/panel_debug.html index 4f45c22d..8abcbad8 100644 --- a/templates/panel_debug.html +++ b/templates/panel_debug.html @@ -24,6 +24,22 @@ {{template "panel_debug_stat.html" .CPUs}} {{template "panel_debug_stat_q.html"}}
+{{template "panel_debug_subhead.html" "panel_debug_tasks"}} +
+ {{template "panel_debug_stat_head.html" "panel_debug_tasks_half_second"}} + {{template "panel_debug_stat_head.html" "panel_debug_tasks_second"}} + {{template "panel_debug_stat_head.html" "panel_debug_tasks_fifteen_minute"}} + {{template "panel_debug_stat.html" .Tasks.HalfSecond}} + {{template "panel_debug_stat.html" .Tasks.Second}} + {{template "panel_debug_stat.html" .Tasks.FifteenMinute}} + + {{template "panel_debug_stat_head.html" "panel_debug_tasks_hour"}} + {{template "panel_debug_stat_head.html" "panel_debug_tasks_shutdown"}} + {{template "panel_debug_stat_head_q.html"}} + {{template "panel_debug_stat.html" .Tasks.Hour}} + {{template "panel_debug_stat.html" .Tasks.Shutdown}} + {{template "panel_debug_stat_q.html"}} +
{{template "panel_debug_subhead.html" "panel_debug_memory_stats"}}
{{template "panel_debug_stat_head.html" "panel_debug_memory_stats_sys"}} diff --git a/templates/panel_inner_menu.html b/templates/panel_inner_menu.html index ff6e58e0..ca035b1a 100644 --- a/templates/panel_inner_menu.html +++ b/templates/panel_inner_menu.html @@ -35,38 +35,41 @@
{{if eq .Zone "analytics"}} + {{end}}
diff --git a/themes/nox/overrides/panel_inner_menu.html b/themes/nox/overrides/panel_inner_menu.html index ff6e58e0..ca035b1a 100644 --- a/themes/nox/overrides/panel_inner_menu.html +++ b/themes/nox/overrides/panel_inner_menu.html @@ -35,38 +35,41 @@