diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 06c18125..69fda816 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -614,6 +614,13 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) + qgen.Install.CreateTable("memchunks", "", "", + []tblColumn{ + tblColumn{"count", "int", 0, false, false, "0"}, + tblColumn{"createdAt", "datetime", 0, false, false, ""}, + }, nil, + ) + qgen.Install.CreateTable("sync", "", "", []tblColumn{ tblColumn{"last_update", "datetime", 0, false, false, ""}, diff --git a/common/counters/memory.go b/common/counters/memory.go new file mode 100644 index 00000000..b35319c7 --- /dev/null +++ b/common/counters/memory.go @@ -0,0 +1,33 @@ +package counters + +import ( + "runtime" + "database/sql" + + c "github.com/Azareal/Gosora/common" + "github.com/Azareal/Gosora/query_gen" +) + +var MemoryCounter *DefaultMemoryCounter + +type DefaultMemoryCounter struct { + insert *sql.Stmt +} + +func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, error) { + co := &DefaultMemoryCounter{ + insert: acc.Insert("memchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(), + } + c.AddScheduledFifteenMinuteTask(co.Tick) + //c.AddScheduledSecondTask(co.Tick) + c.AddShutdownTask(co.Tick) + return co, acc.FirstError() +} + +func (co *DefaultMemoryCounter) Tick() (err error) { + var m runtime.MemStats + runtime.ReadMemStats(&m) + c.DebugLogf("Inserting a memchunk with a value of %d", m.Sys) + _, err = co.insert.Exec(m.Sys) + return err +} \ No newline at end of file diff --git a/common/pages.go b/common/pages.go index 17e1fcfc..02514a6e 100644 --- a/common/pages.go +++ b/common/pages.go @@ -300,6 +300,13 @@ type PanelAnalyticsStd struct{ Unit string TimeType string } +type PanelAnalyticsStdUnit struct{ + Graph PanelTimeGraph + ViewItems []PanelAnalyticsItemUnit + TimeRange string + Unit string + TimeType string +} type PanelStats struct { Users int @@ -376,6 +383,11 @@ type PanelAnalyticsItem struct { Time int64 Count int64 } +type PanelAnalyticsItemUnit struct { + Time int64 + Count int64 + Unit string +} type PanelAnalyticsPage struct { *BasePanelPage diff --git a/common/utils.go b/common/utils.go index 8a6c2032..397fc532 100644 --- a/common/utils.go +++ b/common/utils.go @@ -1,7 +1,7 @@ /* * * Utility Functions And Stuff -* Copyright Azareal 2017 - 2019 +* Copyright Azareal 2017 - 2020 * */ package common diff --git a/gen_router.go b/gen_router.go index 31d80c6c..8f66b05f 100644 --- a/gen_router.go +++ b/gen_router.go @@ -94,6 +94,7 @@ var RouteMap = map[string]interface{}{ "panel.AnalyticsLanguageViews": panel.AnalyticsLanguageViews, "panel.AnalyticsReferrerViews": panel.AnalyticsReferrerViews, "panel.AnalyticsPosts": panel.AnalyticsPosts, + "panel.AnalyticsMemory": panel.AnalyticsMemory, "panel.AnalyticsTopics": panel.AnalyticsTopics, "panel.AnalyticsForums": panel.AnalyticsForums, "panel.Groups": panel.Groups, @@ -241,78 +242,79 @@ var routeMapEnum = map[string]int{ "panel.AnalyticsLanguageViews": 68, "panel.AnalyticsReferrerViews": 69, "panel.AnalyticsPosts": 70, - "panel.AnalyticsTopics": 71, - "panel.AnalyticsForums": 72, - "panel.Groups": 73, - "panel.GroupsEdit": 74, - "panel.GroupsEditPerms": 75, - "panel.GroupsEditSubmit": 76, - "panel.GroupsEditPermsSubmit": 77, - "panel.GroupsCreateSubmit": 78, - "panel.Backups": 79, - "panel.LogsRegs": 80, - "panel.LogsMod": 81, - "panel.Debug": 82, - "panel.Dashboard": 83, - "routes.AccountEdit": 84, - "routes.AccountEditPassword": 85, - "routes.AccountEditPasswordSubmit": 86, - "routes.AccountEditAvatarSubmit": 87, - "routes.AccountEditUsernameSubmit": 88, - "routes.AccountEditMFA": 89, - "routes.AccountEditMFASetup": 90, - "routes.AccountEditMFASetupSubmit": 91, - "routes.AccountEditMFADisableSubmit": 92, - "routes.AccountEditEmail": 93, - "routes.AccountEditEmailTokenSubmit": 94, - "routes.AccountLogins": 95, - "routes.LevelList": 96, - "routes.ViewProfile": 97, - "routes.BanUserSubmit": 98, - "routes.UnbanUser": 99, - "routes.ActivateUser": 100, - "routes.IPSearch": 101, - "routes.CreateTopicSubmit": 102, - "routes.EditTopicSubmit": 103, - "routes.DeleteTopicSubmit": 104, - "routes.StickTopicSubmit": 105, - "routes.UnstickTopicSubmit": 106, - "routes.LockTopicSubmit": 107, - "routes.UnlockTopicSubmit": 108, - "routes.MoveTopicSubmit": 109, - "routes.LikeTopicSubmit": 110, - "routes.AddAttachToTopicSubmit": 111, - "routes.RemoveAttachFromTopicSubmit": 112, - "routes.ViewTopic": 113, - "routes.CreateReplySubmit": 114, - "routes.ReplyEditSubmit": 115, - "routes.ReplyDeleteSubmit": 116, - "routes.ReplyLikeSubmit": 117, - "routes.AddAttachToReplySubmit": 118, - "routes.RemoveAttachFromReplySubmit": 119, - "routes.ProfileReplyCreateSubmit": 120, - "routes.ProfileReplyEditSubmit": 121, - "routes.ProfileReplyDeleteSubmit": 122, - "routes.PollVote": 123, - "routes.PollResults": 124, - "routes.AccountLogin": 125, - "routes.AccountRegister": 126, - "routes.AccountLogout": 127, - "routes.AccountLoginSubmit": 128, - "routes.AccountLoginMFAVerify": 129, - "routes.AccountLoginMFAVerifySubmit": 130, - "routes.AccountRegisterSubmit": 131, - "routes.AccountPasswordReset": 132, - "routes.AccountPasswordResetSubmit": 133, - "routes.AccountPasswordResetToken": 134, - "routes.AccountPasswordResetTokenSubmit": 135, - "routes.DynamicRoute": 136, - "routes.UploadedFile": 137, - "routes.StaticFile": 138, - "routes.RobotsTxt": 139, - "routes.SitemapXml": 140, - "routes.BadRoute": 141, - "routes.HTTPSRedirect": 142, + "panel.AnalyticsMemory": 71, + "panel.AnalyticsTopics": 72, + "panel.AnalyticsForums": 73, + "panel.Groups": 74, + "panel.GroupsEdit": 75, + "panel.GroupsEditPerms": 76, + "panel.GroupsEditSubmit": 77, + "panel.GroupsEditPermsSubmit": 78, + "panel.GroupsCreateSubmit": 79, + "panel.Backups": 80, + "panel.LogsRegs": 81, + "panel.LogsMod": 82, + "panel.Debug": 83, + "panel.Dashboard": 84, + "routes.AccountEdit": 85, + "routes.AccountEditPassword": 86, + "routes.AccountEditPasswordSubmit": 87, + "routes.AccountEditAvatarSubmit": 88, + "routes.AccountEditUsernameSubmit": 89, + "routes.AccountEditMFA": 90, + "routes.AccountEditMFASetup": 91, + "routes.AccountEditMFASetupSubmit": 92, + "routes.AccountEditMFADisableSubmit": 93, + "routes.AccountEditEmail": 94, + "routes.AccountEditEmailTokenSubmit": 95, + "routes.AccountLogins": 96, + "routes.LevelList": 97, + "routes.ViewProfile": 98, + "routes.BanUserSubmit": 99, + "routes.UnbanUser": 100, + "routes.ActivateUser": 101, + "routes.IPSearch": 102, + "routes.CreateTopicSubmit": 103, + "routes.EditTopicSubmit": 104, + "routes.DeleteTopicSubmit": 105, + "routes.StickTopicSubmit": 106, + "routes.UnstickTopicSubmit": 107, + "routes.LockTopicSubmit": 108, + "routes.UnlockTopicSubmit": 109, + "routes.MoveTopicSubmit": 110, + "routes.LikeTopicSubmit": 111, + "routes.AddAttachToTopicSubmit": 112, + "routes.RemoveAttachFromTopicSubmit": 113, + "routes.ViewTopic": 114, + "routes.CreateReplySubmit": 115, + "routes.ReplyEditSubmit": 116, + "routes.ReplyDeleteSubmit": 117, + "routes.ReplyLikeSubmit": 118, + "routes.AddAttachToReplySubmit": 119, + "routes.RemoveAttachFromReplySubmit": 120, + "routes.ProfileReplyCreateSubmit": 121, + "routes.ProfileReplyEditSubmit": 122, + "routes.ProfileReplyDeleteSubmit": 123, + "routes.PollVote": 124, + "routes.PollResults": 125, + "routes.AccountLogin": 126, + "routes.AccountRegister": 127, + "routes.AccountLogout": 128, + "routes.AccountLoginSubmit": 129, + "routes.AccountLoginMFAVerify": 130, + "routes.AccountLoginMFAVerifySubmit": 131, + "routes.AccountRegisterSubmit": 132, + "routes.AccountPasswordReset": 133, + "routes.AccountPasswordResetSubmit": 134, + "routes.AccountPasswordResetToken": 135, + "routes.AccountPasswordResetTokenSubmit": 136, + "routes.DynamicRoute": 137, + "routes.UploadedFile": 138, + "routes.StaticFile": 139, + "routes.RobotsTxt": 140, + "routes.SitemapXml": 141, + "routes.BadRoute": 142, + "routes.HTTPSRedirect": 143, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -386,78 +388,79 @@ var reverseRouteMapEnum = map[int]string{ 68: "panel.AnalyticsLanguageViews", 69: "panel.AnalyticsReferrerViews", 70: "panel.AnalyticsPosts", - 71: "panel.AnalyticsTopics", - 72: "panel.AnalyticsForums", - 73: "panel.Groups", - 74: "panel.GroupsEdit", - 75: "panel.GroupsEditPerms", - 76: "panel.GroupsEditSubmit", - 77: "panel.GroupsEditPermsSubmit", - 78: "panel.GroupsCreateSubmit", - 79: "panel.Backups", - 80: "panel.LogsRegs", - 81: "panel.LogsMod", - 82: "panel.Debug", - 83: "panel.Dashboard", - 84: "routes.AccountEdit", - 85: "routes.AccountEditPassword", - 86: "routes.AccountEditPasswordSubmit", - 87: "routes.AccountEditAvatarSubmit", - 88: "routes.AccountEditUsernameSubmit", - 89: "routes.AccountEditMFA", - 90: "routes.AccountEditMFASetup", - 91: "routes.AccountEditMFASetupSubmit", - 92: "routes.AccountEditMFADisableSubmit", - 93: "routes.AccountEditEmail", - 94: "routes.AccountEditEmailTokenSubmit", - 95: "routes.AccountLogins", - 96: "routes.LevelList", - 97: "routes.ViewProfile", - 98: "routes.BanUserSubmit", - 99: "routes.UnbanUser", - 100: "routes.ActivateUser", - 101: "routes.IPSearch", - 102: "routes.CreateTopicSubmit", - 103: "routes.EditTopicSubmit", - 104: "routes.DeleteTopicSubmit", - 105: "routes.StickTopicSubmit", - 106: "routes.UnstickTopicSubmit", - 107: "routes.LockTopicSubmit", - 108: "routes.UnlockTopicSubmit", - 109: "routes.MoveTopicSubmit", - 110: "routes.LikeTopicSubmit", - 111: "routes.AddAttachToTopicSubmit", - 112: "routes.RemoveAttachFromTopicSubmit", - 113: "routes.ViewTopic", - 114: "routes.CreateReplySubmit", - 115: "routes.ReplyEditSubmit", - 116: "routes.ReplyDeleteSubmit", - 117: "routes.ReplyLikeSubmit", - 118: "routes.AddAttachToReplySubmit", - 119: "routes.RemoveAttachFromReplySubmit", - 120: "routes.ProfileReplyCreateSubmit", - 121: "routes.ProfileReplyEditSubmit", - 122: "routes.ProfileReplyDeleteSubmit", - 123: "routes.PollVote", - 124: "routes.PollResults", - 125: "routes.AccountLogin", - 126: "routes.AccountRegister", - 127: "routes.AccountLogout", - 128: "routes.AccountLoginSubmit", - 129: "routes.AccountLoginMFAVerify", - 130: "routes.AccountLoginMFAVerifySubmit", - 131: "routes.AccountRegisterSubmit", - 132: "routes.AccountPasswordReset", - 133: "routes.AccountPasswordResetSubmit", - 134: "routes.AccountPasswordResetToken", - 135: "routes.AccountPasswordResetTokenSubmit", - 136: "routes.DynamicRoute", - 137: "routes.UploadedFile", - 138: "routes.StaticFile", - 139: "routes.RobotsTxt", - 140: "routes.SitemapXml", - 141: "routes.BadRoute", - 142: "routes.HTTPSRedirect", + 71: "panel.AnalyticsMemory", + 72: "panel.AnalyticsTopics", + 73: "panel.AnalyticsForums", + 74: "panel.Groups", + 75: "panel.GroupsEdit", + 76: "panel.GroupsEditPerms", + 77: "panel.GroupsEditSubmit", + 78: "panel.GroupsEditPermsSubmit", + 79: "panel.GroupsCreateSubmit", + 80: "panel.Backups", + 81: "panel.LogsRegs", + 82: "panel.LogsMod", + 83: "panel.Debug", + 84: "panel.Dashboard", + 85: "routes.AccountEdit", + 86: "routes.AccountEditPassword", + 87: "routes.AccountEditPasswordSubmit", + 88: "routes.AccountEditAvatarSubmit", + 89: "routes.AccountEditUsernameSubmit", + 90: "routes.AccountEditMFA", + 91: "routes.AccountEditMFASetup", + 92: "routes.AccountEditMFASetupSubmit", + 93: "routes.AccountEditMFADisableSubmit", + 94: "routes.AccountEditEmail", + 95: "routes.AccountEditEmailTokenSubmit", + 96: "routes.AccountLogins", + 97: "routes.LevelList", + 98: "routes.ViewProfile", + 99: "routes.BanUserSubmit", + 100: "routes.UnbanUser", + 101: "routes.ActivateUser", + 102: "routes.IPSearch", + 103: "routes.CreateTopicSubmit", + 104: "routes.EditTopicSubmit", + 105: "routes.DeleteTopicSubmit", + 106: "routes.StickTopicSubmit", + 107: "routes.UnstickTopicSubmit", + 108: "routes.LockTopicSubmit", + 109: "routes.UnlockTopicSubmit", + 110: "routes.MoveTopicSubmit", + 111: "routes.LikeTopicSubmit", + 112: "routes.AddAttachToTopicSubmit", + 113: "routes.RemoveAttachFromTopicSubmit", + 114: "routes.ViewTopic", + 115: "routes.CreateReplySubmit", + 116: "routes.ReplyEditSubmit", + 117: "routes.ReplyDeleteSubmit", + 118: "routes.ReplyLikeSubmit", + 119: "routes.AddAttachToReplySubmit", + 120: "routes.RemoveAttachFromReplySubmit", + 121: "routes.ProfileReplyCreateSubmit", + 122: "routes.ProfileReplyEditSubmit", + 123: "routes.ProfileReplyDeleteSubmit", + 124: "routes.PollVote", + 125: "routes.PollResults", + 126: "routes.AccountLogin", + 127: "routes.AccountRegister", + 128: "routes.AccountLogout", + 129: "routes.AccountLoginSubmit", + 130: "routes.AccountLoginMFAVerify", + 131: "routes.AccountLoginMFAVerifySubmit", + 132: "routes.AccountRegisterSubmit", + 133: "routes.AccountPasswordReset", + 134: "routes.AccountPasswordResetSubmit", + 135: "routes.AccountPasswordResetToken", + 136: "routes.AccountPasswordResetTokenSubmit", + 137: "routes.DynamicRoute", + 138: "routes.UploadedFile", + 139: "routes.StaticFile", + 140: "routes.RobotsTxt", + 141: "routes.SitemapXml", + 142: "routes.BadRoute", + 143: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -612,7 +615,7 @@ type HTTPSRedirect struct { func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - counters.RouteViewCounter.Bump(142) + counters.RouteViewCounter.Bump(143) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -816,7 +819,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.GlobalViewCounter.Bump() if prefix == "/static" { - counters.RouteViewCounter.Bump(138) + counters.RouteViewCounter.Bump(139) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -1504,13 +1507,21 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c counters.RouteViewCounter.Bump(70) err = panel.AnalyticsPosts(w,req,user) - case "/panel/analytics/topics/": + case "/panel/analytics/memory/": err = c.ParseForm(w,req,user) if err != nil { return err } counters.RouteViewCounter.Bump(71) + err = panel.AnalyticsMemory(w,req,user) + case "/panel/analytics/topics/": + err = c.ParseForm(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(72) err = panel.AnalyticsTopics(w,req,user) case "/panel/analytics/forums/": err = c.ParseForm(w,req,user) @@ -1518,16 +1529,16 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(72) + counters.RouteViewCounter.Bump(73) err = panel.AnalyticsForums(w,req,user) case "/panel/groups/": - counters.RouteViewCounter.Bump(73) + counters.RouteViewCounter.Bump(74) err = panel.Groups(w,req,user) case "/panel/groups/edit/": - counters.RouteViewCounter.Bump(74) + counters.RouteViewCounter.Bump(75) err = panel.GroupsEdit(w,req,user,extraData) case "/panel/groups/edit/perms/": - counters.RouteViewCounter.Bump(75) + counters.RouteViewCounter.Bump(76) err = panel.GroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1535,7 +1546,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(76) + counters.RouteViewCounter.Bump(77) err = panel.GroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1543,7 +1554,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(77) + counters.RouteViewCounter.Bump(78) err = panel.GroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = c.NoSessionMismatch(w,req,user) @@ -1551,7 +1562,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(78) + counters.RouteViewCounter.Bump(79) err = panel.GroupsCreateSubmit(w,req,user) case "/panel/backups/": err = c.SuperAdminOnly(w,req,user) @@ -1565,13 +1576,13 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c w.Header().Del("Content-Type") w.Header().Del("Content-Encoding") } - counters.RouteViewCounter.Bump(79) + counters.RouteViewCounter.Bump(80) err = panel.Backups(w,req,user,extraData) case "/panel/logs/regs/": - counters.RouteViewCounter.Bump(80) + counters.RouteViewCounter.Bump(81) err = panel.LogsRegs(w,req,user) case "/panel/logs/mod/": - counters.RouteViewCounter.Bump(81) + counters.RouteViewCounter.Bump(82) err = panel.LogsMod(w,req,user) case "/panel/debug/": err = c.AdminOnly(w,req,user) @@ -1579,10 +1590,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(82) + counters.RouteViewCounter.Bump(83) err = panel.Debug(w,req,user) default: - counters.RouteViewCounter.Bump(83) + counters.RouteViewCounter.Bump(84) err = panel.Dashboard(w,req,user) } case "/user": @@ -1593,7 +1604,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(84) + counters.RouteViewCounter.Bump(85) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1605,7 +1616,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(85) + counters.RouteViewCounter.Bump(86) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1622,7 +1633,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(86) + counters.RouteViewCounter.Bump(87) err = routes.AccountEditPasswordSubmit(w,req,user) case "/user/edit/avatar/submit/": err = c.MemberOnly(w,req,user) @@ -1639,7 +1650,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(87) + counters.RouteViewCounter.Bump(88) err = routes.AccountEditAvatarSubmit(w,req,user) case "/user/edit/username/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1652,7 +1663,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(88) + counters.RouteViewCounter.Bump(89) err = routes.AccountEditUsernameSubmit(w,req,user) case "/user/edit/mfa/": err = c.MemberOnly(w,req,user) @@ -1660,7 +1671,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(89) + counters.RouteViewCounter.Bump(90) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1672,7 +1683,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(90) + counters.RouteViewCounter.Bump(91) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1689,7 +1700,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(91) + counters.RouteViewCounter.Bump(92) err = routes.AccountEditMFASetupSubmit(w,req,user) case "/user/edit/mfa/disable/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1702,7 +1713,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(92) + counters.RouteViewCounter.Bump(93) err = routes.AccountEditMFADisableSubmit(w,req,user) case "/user/edit/email/": err = c.MemberOnly(w,req,user) @@ -1710,14 +1721,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(93) + counters.RouteViewCounter.Bump(94) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountEditEmail(w,req,user,head) case "/user/edit/token/": - counters.RouteViewCounter.Bump(94) + counters.RouteViewCounter.Bump(95) err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData) case "/user/edit/logins/": err = c.MemberOnly(w,req,user) @@ -1725,7 +1736,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(95) + counters.RouteViewCounter.Bump(96) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1737,7 +1748,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(96) + counters.RouteViewCounter.Bump(97) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1745,7 +1756,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c err = routes.LevelList(w,req,user,head) default: req.URL.Path += extraData - counters.RouteViewCounter.Bump(97) + counters.RouteViewCounter.Bump(98) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1765,7 +1776,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(98) + counters.RouteViewCounter.Bump(99) err = routes.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = c.NoSessionMismatch(w,req,user) @@ -1778,7 +1789,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(99) + counters.RouteViewCounter.Bump(100) err = routes.UnbanUser(w,req,user,extraData) case "/users/activate/": err = c.NoSessionMismatch(w,req,user) @@ -1791,7 +1802,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(100) + counters.RouteViewCounter.Bump(101) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = c.MemberOnly(w,req,user) @@ -1799,7 +1810,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(101) + counters.RouteViewCounter.Bump(102) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1823,7 +1834,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(102) + counters.RouteViewCounter.Bump(103) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1836,7 +1847,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(103) + counters.RouteViewCounter.Bump(104) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1850,7 +1861,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(104) + counters.RouteViewCounter.Bump(105) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1863,7 +1874,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(105) + counters.RouteViewCounter.Bump(106) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1876,7 +1887,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(106) + counters.RouteViewCounter.Bump(107) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1890,7 +1901,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(107) + counters.RouteViewCounter.Bump(108) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1903,7 +1914,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(108) + counters.RouteViewCounter.Bump(109) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1916,7 +1927,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(109) + counters.RouteViewCounter.Bump(110) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1929,7 +1940,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(110) + counters.RouteViewCounter.Bump(111) err = routes.LikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -1946,7 +1957,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(111) + counters.RouteViewCounter.Bump(112) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1959,10 +1970,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(112) + counters.RouteViewCounter.Bump(113) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - counters.RouteViewCounter.Bump(113) + counters.RouteViewCounter.Bump(114) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1986,7 +1997,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(114) + counters.RouteViewCounter.Bump(115) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1999,7 +2010,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(115) + counters.RouteViewCounter.Bump(116) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2012,7 +2023,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(116) + counters.RouteViewCounter.Bump(117) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2025,7 +2036,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(117) + counters.RouteViewCounter.Bump(118) err = routes.ReplyLikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2042,7 +2053,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(118) + counters.RouteViewCounter.Bump(119) err = routes.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2055,7 +2066,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(119) + counters.RouteViewCounter.Bump(120) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2071,7 +2082,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(120) + counters.RouteViewCounter.Bump(121) err = routes.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2084,7 +2095,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(121) + counters.RouteViewCounter.Bump(122) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2097,7 +2108,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(122) + counters.RouteViewCounter.Bump(123) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2113,23 +2124,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(123) + counters.RouteViewCounter.Bump(124) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - counters.RouteViewCounter.Bump(124) + counters.RouteViewCounter.Bump(125) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - counters.RouteViewCounter.Bump(125) + counters.RouteViewCounter.Bump(126) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountLogin(w,req,user,head) case "/accounts/create/": - counters.RouteViewCounter.Bump(126) + counters.RouteViewCounter.Bump(127) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2146,7 +2157,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(127) + counters.RouteViewCounter.Bump(128) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2154,10 +2165,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(128) + counters.RouteViewCounter.Bump(129) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - counters.RouteViewCounter.Bump(129) + counters.RouteViewCounter.Bump(130) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2169,7 +2180,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(130) + counters.RouteViewCounter.Bump(131) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2177,10 +2188,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(131) + counters.RouteViewCounter.Bump(132) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - counters.RouteViewCounter.Bump(132) + counters.RouteViewCounter.Bump(133) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2192,10 +2203,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(133) + counters.RouteViewCounter.Bump(134) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - counters.RouteViewCounter.Bump(134) + counters.RouteViewCounter.Bump(135) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2207,7 +2218,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(135) + counters.RouteViewCounter.Bump(136) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2223,7 +2234,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c w.Header().Del("Content-Type") w.Header().Del("Content-Encoding") } - counters.RouteViewCounter.Bump(137) + counters.RouteViewCounter.Bump(138) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2233,7 +2244,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": - counters.RouteViewCounter.Bump(139) + counters.RouteViewCounter.Bump(140) return routes.RobotsTxt(w,req) case "favicon.ico": req.URL.Path = "/static"+req.URL.Path+extraData @@ -2241,7 +2252,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c routes.StaticFile(w,req) return nil /*case "sitemap.xml": - counters.RouteViewCounter.Bump(140) + counters.RouteViewCounter.Bump(141) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2252,7 +2263,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - counters.RouteViewCounter.Bump(136) // TODO: Be more specific about *which* dynamic route it is + counters.RouteViewCounter.Bump(137) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2263,7 +2274,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - counters.RouteViewCounter.Bump(141) + counters.RouteViewCounter.Bump(142) return c.NotFound(w,req,nil) } return err diff --git a/langs/english.json b/langs/english.json index d471a05d..f0be358a 100644 --- a/langs/english.json +++ b/langs/english.json @@ -740,6 +740,7 @@ "panel_menu_statistics_systems":"Systems", "panel_menu_statistics_languages":"Languages", "panel_menu_statistics_referrers":"Referrers", + "panel_menu_statistics_memory":"Memory", "panel_menu_reports":"Reports", "panel_menu_logs":"Logs", "panel_menu_logs_registrations":"Registrations", @@ -755,6 +756,8 @@ "panel_dashboard_cpu_desc":"The global CPU usage of this server", "panel_dashboard_ram":"RAM: %s", "panel_dashboard_ram_desc":"The global RAM usage of this server", + "panel_dashboard_memused":"Mem: %.1f%s", + "panel_dashboard_memused_desc":"The amount of memory likely being used by this instance", "panel_dashboard_online": "%d%s online", "panel_dashboard_online_desc":"The number of people who are currently online", "panel_dashboard_guests_online":"%d%s guests online", @@ -880,6 +883,7 @@ "panel_statistics_operating_systems_head":"Operating Systems", "panel_statistics_topic_counts_head":"Topic Counts", "panel_statistics_requests_head":"Requests", + "panel_statistics_memory_head":"Memory", "panel_statistics_time_range_one_year":"1 year", "panel_statistics_time_range_three_months":"3 months", diff --git a/main.go b/main.go index fd9e43dc..b101473c 100644 --- a/main.go +++ b/main.go @@ -218,6 +218,10 @@ func afterDBInit() (err error) { if err != nil { return errors.WithStack(err) } + counters.MemoryCounter, err = counters.NewMemoryCounter(acc) + if err != nil { + return errors.WithStack(err) + } return nil } diff --git a/patcher/patches.go b/patcher/patches.go index 5cf757b1..ec4a92a9 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -31,6 +31,7 @@ func init() { addPatch(16, patch16) addPatch(17, patch17) addPatch(18, patch18) + addPatch(19, patch19) } func patch0(scanner *bufio.Scanner) (err error) { @@ -592,4 +593,13 @@ func patch17(scanner *bufio.Scanner) error { func patch18(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.AddColumn("forums", tblColumn{"order", "int", 0, false, false, "0"}, nil)) +} + +func patch19(scanner *bufio.Scanner) error { + return execStmt(qgen.Builder.CreateTable("memchunks", "", "", + []tblColumn{ + tblColumn{"count", "int", 0, false, false, "0"}, + tblColumn{"createdAt", "datetime", 0, false, false, ""}, + }, nil, + )) } \ No newline at end of file diff --git a/public/analytics.js b/public/analytics.js index a47a8a1a..6b8ea95e 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -2,29 +2,41 @@ })*/ +const Kilobyte = 1024; +const Megabyte = Kilobyte * 1024; +const Gigabyte = Megabyte * 1024; +const Terabyte = Gigabyte * 1024; +const Petabyte = Terabyte * 1024; + +function convertByteUnit(bytes) { + if(bytes >= Petabyte) return Math.ceil(bytes / Petabyte) + "PB"; + else if(bytes >= Terabyte) return Math.ceil(bytes / Terabyte) + "TB"; + else if(bytes >= Gigabyte) return Math.ceil(bytes / Gigabyte) + "GB"; + else if(bytes >= Megabyte) return Math.ceil(bytes / Megabyte) + "MB"; + else if(bytes >= Kilobyte) return Math.ceil(bytes / Kilobyte) + "KB"; + return bytes; +} + // 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) { +function buildStatsChart(rawLabels, seriesData, timeRange, legendNames, bytes = false) { console.log("buildStatsChart"); let labels = []; let aphrases = phraseBox["analytics"]; if(timeRange=="one-year") { labels = [aphrases["analytics.now"],"1" + aphrases["analytics.months_short"]]; for(let i = 2; i < 12; i++) { - let label = i + aphrases["analytics.months_short"]; - labels.push(label); + labels.push(i + aphrases["analytics.months_short"]); } } else if(timeRange=="three-months") { labels = [aphrases["analytics.now"],"3" + aphrases["analytics.days_short"]] for(let i = 6; i < 90; i = i + 3) { - let label = i + aphrases["analytics.days_short"]; - labels.push(label); + labels.push(i + aphrases["analytics.days_short"]); } } else if(timeRange=="one-month") { labels = [aphrases["analytics.now"],"1" + aphrases["analytics.days_short"]]; for(let i = 2; i < 30; i++) { - let label = i + aphrases["analytics.days_short"]; - labels.push(label); + labels.push(i + aphrases["analytics.days_short"]); } } else if(timeRange=="one-week") { labels = [aphrases["analytics.now"]]; @@ -43,18 +55,15 @@ function buildStatsChart(rawLabels, seriesData, timeRange, legendNames) { } } labels = labels.reverse() - for(let i = 0; i < seriesData.length;i++) { + for(let i = 0; i < seriesData.length; i++) { seriesData[i] = seriesData[i].reverse(); } - let config = { - height: '250px', - }; + let config = {height: '250px', plugins:[]}; if(legendNames.length > 0) config.plugins = [ - Chartist.plugins.legend({ - legendNames: legendNames, - }) - ]; + Chartist.plugins.legend({legendNames: legendNames}) + ]; + if(bytes) config.plugins.push(Chartist.plugins.byteUnits()); Chartist.Line('.ct_chart', { labels: labels, series: seriesData, diff --git a/router_gen/routes.go b/router_gen/routes.go index 6bacc3da..fcc4dd57 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -212,6 +212,7 @@ func panelRoutes() *RouteGroup { View("panel.AnalyticsLanguageViews", "/panel/analytics/lang/", "extraData"), View("panel.AnalyticsReferrerViews", "/panel/analytics/referrer/", "extraData"), View("panel.AnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"), + View("panel.AnalyticsMemory", "/panel/analytics/memory/").Before("ParseForm"), View("panel.AnalyticsTopics", "/panel/analytics/topics/").Before("ParseForm"), View("panel.AnalyticsForums", "/panel/analytics/forums/").Before("ParseForm"), diff --git a/routes/panel/analytics.go b/routes/panel/analytics.go index 17283607..b3d9d712 100644 --- a/routes/panel/analytics.go +++ b/routes/panel/analytics.go @@ -464,6 +464,58 @@ func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user c.User) c.Route return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right","analytics","panel_analytics_posts", pi}) } +func AnalyticsMemory(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, viewMap := analyticsTimeRangeToLabelList(timeRange) + + c.DebugLog("in panel.AnalyticsMemory") + 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) + } + viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap) + if err != nil { + return c.InternalError(err, w, r) + } + + var divBy int64 = 1 + switch timeRange.Range { + case "one-year": + divBy = 2 * 30 * 12 + case "three-months": + divBy = 2 * 30 * 3 + case "one-month": + divBy = 2 * 30 + case "one-week": + divBy = 1 * 7 + case "two-days": + divBy = 4 + case "one-day": + divBy = 2 + } + + // TODO: Adjust for the missing chunks in week and month + var viewList []int64 + var viewItems []c.PanelAnalyticsItemUnit + for _, value := range revLabelList { + viewMap[value] = viewMap[value] / divBy + viewList = append(viewList, viewMap[value]) + cv, cu := c.ConvertByteUnit(float64(viewMap[value])) + viewItems = append(viewItems, c.PanelAnalyticsItemUnit{Time: value, Unit: cu, Count: int64(cv)}) + } + graph := c.PanelTimeGraph{Series: [][]int64{viewList}, Labels: labelList} + c.DebugLogf("graph: %+v\n", graph) + pi := c.PanelAnalyticsStdUnit{graph, viewItems, timeRange.Range, timeRange.Unit, "time"} + return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right","analytics","panel_analytics_memory", pi}) +} + func analyticsRowsToNameMap(rows *sql.Rows) (map[string]int, error) { nameMap := make(map[string]int) defer rows.Close() diff --git a/routes/panel/dashboard.go b/routes/panel/dashboard.go index 6cd37252..65ede5c5 100644 --- a/routes/panel/dashboard.go +++ b/routes/panel/dashboard.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "strconv" + "runtime" c "github.com/Azareal/Gosora/common" p "github.com/Azareal/Gosora/common/phrases" @@ -113,6 +114,10 @@ func Dashboard(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError ramColour = lessThanSwitch(int(ramperc), 50, 75) } + var m runtime.MemStats + runtime.ReadMemStats(&m) + memCount, memUnit := c.ConvertByteUnit(float64(m.Sys)) + greaterThanSwitch := func(number int, lowerBound int, midBound int) string { switch { case number > midBound: @@ -166,14 +171,16 @@ func Dashboard(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError return c.InternalError(intErr, w, r) } - // TODO: Localise these var gridElements = []GE{ // TODO: Implement a check for new versions of Gosora + // TODO: Localise this //GE{"dash-version", "v" + version.String(), 0, "grid_istat stat_green", "", "", "Gosora is up-to-date :)"}, GE{"dash-version", "v" + c.SoftwareVersion.String(), 0, "grid_istat", "", "", ""}, GE{"dash-cpu", p.GetTmplPhrasef("panel_dashboard_cpu",cpustr), 1, "grid_istat " + cpuColour, "", "", p.GetTmplPhrase("panel_dashboard_cpu_desc")}, GE{"dash-ram", p.GetTmplPhrasef("panel_dashboard_ram",ramstr), 2, "grid_istat " + ramColour, "", "", p.GetTmplPhrase("panel_dashboard_ram_desc")}, + + GE{"dash-memused", p.GetTmplPhrasef("panel_dashboard_memused",memCount, memUnit), 2, "grid_istat", "", "", p.GetTmplPhrase("panel_dashboard_memused_desc")}, } var addElement = func(element GE) { gridElements = append(gridElements, element) diff --git a/schema/mssql/query_memchunks.sql b/schema/mssql/query_memchunks.sql new file mode 100644 index 00000000..53faf85e --- /dev/null +++ b/schema/mssql/query_memchunks.sql @@ -0,0 +1,4 @@ +CREATE TABLE [memchunks] ( + [count] int DEFAULT 0 not null, + [createdAt] datetime not null +); \ No newline at end of file diff --git a/schema/mysql/query_memchunks.sql b/schema/mysql/query_memchunks.sql new file mode 100644 index 00000000..e79f8faf --- /dev/null +++ b/schema/mysql/query_memchunks.sql @@ -0,0 +1,4 @@ +CREATE TABLE `memchunks` ( + `count` int DEFAULT 0 not null, + `createdAt` datetime not null +); \ No newline at end of file diff --git a/schema/pgsql/query_memchunks.sql b/schema/pgsql/query_memchunks.sql new file mode 100644 index 00000000..ef6b7894 --- /dev/null +++ b/schema/pgsql/query_memchunks.sql @@ -0,0 +1,4 @@ +CREATE TABLE "memchunks" ( + `count` int DEFAULT 0 not null, + `createdAt` timestamp not null +); \ No newline at end of file diff --git a/templates/panel_analytics_agent_views.html b/templates/panel_analytics_agent_views.html index 9f591e3e..120a51c7 100644 --- a/templates/panel_analytics_agent_views.html +++ b/templates/panel_analytics_agent_views.html @@ -1,11 +1,11 @@ -
-
-

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

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

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

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_agents.html b/templates/panel_analytics_agents.html index 33ad79ae..b865adc9 100644 --- a/templates/panel_analytics_agents.html +++ b/templates/panel_analytics_agents.html @@ -1,19 +1,19 @@ -
-
-

{{lang "panel_statistics_user_agents_head"}}

- {{template "panel_analytics_time_range.html" . }} -
-
-
-
-
-
-
- {{range .ItemList}} -
- {{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} -
- {{else}}
{{lang "panel_statistics_user_agents_no_user_agents"}}
{{end}} -
+
+
+

{{lang "panel_statistics_user_agents_head"}}

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
+
+ {{range .ItemList}} +
+ {{.FriendlyAgent}} + {{.Count}}{{lang "panel_statistics_views_suffix"}} +
+ {{else}}
{{lang "panel_statistics_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 128d4b22..c09381e3 100644 --- a/templates/panel_analytics_forum_views.html +++ b/templates/panel_analytics_forum_views.html @@ -1,11 +1,11 @@ -
-
-

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

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

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

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
{{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 c12268f4..40c45ec9 100644 --- a/templates/panel_analytics_lang_views.html +++ b/templates/panel_analytics_lang_views.html @@ -1,11 +1,11 @@ -
-
-

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

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

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

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_langs.html b/templates/panel_analytics_langs.html index 620df717..718bb19f 100644 --- a/templates/panel_analytics_langs.html +++ b/templates/panel_analytics_langs.html @@ -1,19 +1,19 @@ -
-
-

{{lang "panel_statistics_languages_head"}}

- {{template "panel_analytics_time_range.html" . }} -
-
-
-
-
-
-
- {{range .ItemList}} -
- {{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} -
- {{else}}
{{lang "panel_statistics_languages_no_languages"}}
{{end}} -
+
+
+

{{lang "panel_statistics_languages_head"}}

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
+
+ {{range .ItemList}} +
+ {{.FriendlyAgent}} + {{.Count}}{{lang "panel_statistics_views_suffix"}} +
+ {{else}}
{{lang "panel_statistics_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 new file mode 100644 index 00000000..c7d6e8d0 --- /dev/null +++ b/templates/panel_analytics_memory.html @@ -0,0 +1,71 @@ +
+
+

{{lang "panel_statistics_memory_head"}}

+ +
+
+
+
+
+
+
+
+

{{lang "panel_statistics_details_head"}}

+
+
+
+ {{range .ViewItems}} +
+ {{.Time}} + {{.Count}}{{.Unit}} +
+ {{else}}
{{lang "panel_statistics_post_counts_no_post_counts"}}
{{end}} +
+ \ No newline at end of file diff --git a/templates/panel_analytics_routes.html b/templates/panel_analytics_routes.html index e0577190..5ae4275f 100644 --- a/templates/panel_analytics_routes.html +++ b/templates/panel_analytics_routes.html @@ -1,19 +1,19 @@ -
-
-

{{lang "panel_statistics_routes_head"}}

- {{template "panel_analytics_time_range.html" . }} -
-
-
-
-
-
-
- {{range .ItemList}} -
- {{.Route}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} -
- {{else}}
{{lang "panel_statistics_routes_no_routes"}}
{{end}} -
+
+
+

{{lang "panel_statistics_routes_head"}}

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
+
+ {{range .ItemList}} +
+ {{.Route}} + {{.Count}}{{lang "panel_statistics_views_suffix"}} +
+ {{else}}
{{lang "panel_statistics_routes_no_routes"}}
{{end}} +
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_systems.html b/templates/panel_analytics_systems.html index 89e05505..4185f409 100644 --- a/templates/panel_analytics_systems.html +++ b/templates/panel_analytics_systems.html @@ -1,19 +1,19 @@ -
-
-

{{lang "panel_statistics_operating_systems_head"}}

- {{template "panel_analytics_time_range.html" . }} -
-
-
-
-
-
-
- {{range .ItemList}} -
- {{.FriendlyAgent}} - {{.Count}}{{lang "panel_statistics_views_suffix"}} -
- {{else}}
{{lang "panel_statistics_operating_systems_no_operating_systems"}}
{{end}} -
+
+
+

{{lang "panel_statistics_operating_systems_head"}}

+ {{template "panel_analytics_time_range.html" . }} +
+
+
+
+
+
+
+ {{range .ItemList}} +
+ {{.FriendlyAgent}} + {{.Count}}{{lang "panel_statistics_views_suffix"}} +
+ {{else}}
{{lang "panel_statistics_operating_systems_no_operating_systems"}}
{{end}} +
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_inner_menu.html b/templates/panel_inner_menu.html index bdc5d943..fe6b25ae 100644 --- a/templates/panel_inner_menu.html +++ b/templates/panel_inner_menu.html @@ -62,6 +62,9 @@ + {{end}}
{{lang "panel_menu_reports"}} ({{.Stats.Reports}}) @@ -88,4 +91,4 @@ {{if .CurrentUser.IsAdmin}}
{{lang "panel_menu_debug"}}
{{end}} -
+ \ No newline at end of file diff --git a/themes/nox/overrides/panel_inner_menu.html b/themes/nox/overrides/panel_inner_menu.html index bdc5d943..96d398e8 100644 --- a/themes/nox/overrides/panel_inner_menu.html +++ b/themes/nox/overrides/panel_inner_menu.html @@ -62,6 +62,9 @@ + {{end}}
{{lang "panel_menu_reports"}} ({{.Stats.Reports}}) diff --git a/themes/nox/public/panel.css b/themes/nox/public/panel.css index b3021a3a..1a758728 100644 --- a/themes/nox/public/panel.css +++ b/themes/nox/public/panel.css @@ -175,6 +175,7 @@ button, .formbutton, .panel_right_button:not(.has_inner_button), #panel_users .p padding: 16px; padding-bottom: 0px; padding-left: 0px; + padding-right: 0px; margin-bottom: 10px; } .colstack_graph_holder .ct-label {