diff --git a/.travis.yml b/.travis.yml index 8c7bfd26..b68af36d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ env: language: go go: - "1.11" + - "1.12" - master before_install: - cd $HOME diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index abfaa8f1..13fe0606 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -377,6 +377,7 @@ func createTables(adapter qgen.Adapter) error { tblColumn{"event", "varchar", 50, false, false, ""}, /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */ tblColumn{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ tblColumn{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ + tblColumn{"createdAt", "createdAt", 0, false, false, ""}, }, []tblKey{ tblKey{"asid", "primary","",false}, @@ -620,6 +621,8 @@ func createTables(adapter qgen.Adapter) error { qgen.Install.CreateTable("memchunks", "", "", []tblColumn{ tblColumn{"count", "int", 0, false, false, "0"}, + tblColumn{"stack", "int", 0, false, false, "0"}, + tblColumn{"heap", "int", 0, false, false, "0"}, tblColumn{"createdAt", "datetime", 0, false, false, ""}, }, nil, ) @@ -636,5 +639,12 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) + qgen.Install.CreateTable("meta", "", "", + []tblColumn{ + tblColumn{"name", "varchar", 200, false, false, ""}, + tblColumn{"value", "varchar", 200, false, false, ""}, + }, nil, + ) + return nil } diff --git a/common/counters/memory.go b/common/counters/memory.go index 890ba646..da4c1cde 100644 --- a/common/counters/memory.go +++ b/common/counters/memory.go @@ -14,14 +14,20 @@ var MemoryCounter *DefaultMemoryCounter type DefaultMemoryCounter struct { insert *sql.Stmt + totMem uint64 totCount uint64 + stackMem uint64 + stackCount uint64 + heapMem uint64 + heapCount uint64 + sync.Mutex } func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, error) { co := &DefaultMemoryCounter{ - insert: acc.Insert("memchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(), + insert: acc.Insert("memchunks").Columns("count, stack, heap, createdAt").Fields("?,?,?,UTC_TIMESTAMP()").Prepare(), } c.AddScheduledFifteenMinuteTask(co.Tick) //c.AddScheduledSecondTask(co.Tick) @@ -36,6 +42,10 @@ func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, error) { co.Lock() co.totCount++ co.totMem += m.Sys + co.stackCount++ + co.stackMem += m.StackInuse + co.heapCount++ + co.heapMem += m.HeapAlloc co.Unlock() } } @@ -46,15 +56,30 @@ func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, error) { func (co *DefaultMemoryCounter) Tick() (err error) { var m runtime.MemStats runtime.ReadMemStats(&m) - var avgMem uint64 + var avgMem, avgStack, avgHeap uint64 co.Lock() + co.totCount++ co.totMem += m.Sys + co.stackCount++ + co.stackMem += m.StackInuse + co.heapCount++ + co.heapMem += m.HeapAlloc + avgMem = co.totMem / co.totCount + avgStack = co.stackMem / co.stackCount + avgHeap = co.heapMem / co.heapCount + co.totMem = 0 co.totCount = 0 + co.stackMem = 0 + co.stackCount = 0 + co.heapMem = 0 + co.heapCount = 0 + co.Unlock() - c.DebugLogf("Inserting a memchunk with a value of %d", avgMem) - _, err = co.insert.Exec(avgMem) + + c.DebugLogf("Inserting a memchunk with a value of %d - %d - %d", avgMem, avgStack, avgHeap) + _, err = co.insert.Exec(avgMem, avgStack, avgHeap) return err } \ No newline at end of file diff --git a/common/meta_store.go b/common/meta_store.go new file mode 100644 index 00000000..6150ea91 --- /dev/null +++ b/common/meta_store.go @@ -0,0 +1,45 @@ +package common + +import "database/sql" +import "github.com/Azareal/Gosora/query_gen" + +var Meta MetaStore + +// MetaStore is a simple key-value store for the system to stash things in when needed +type MetaStore interface { + Get(name string) (val string, err error) + Set(name string, val string) error +} + +type DefaultMetaStore struct { + get *sql.Stmt + set *sql.Stmt + add *sql.Stmt +} + +func NewDefaultMetaStore(acc *qgen.Accumulator) (*DefaultMetaStore, error) { + m := &DefaultMetaStore{ + get: acc.Select("meta").Columns("value").Where("name = ?").Prepare(), + set: acc.Update("meta").Set("value = ?").Where("name = ?").Prepare(), + add: acc.Insert("meta").Columns("name,value").Fields("?,''").Prepare(), + } + return m, acc.FirstError() +} + +func (s *DefaultMetaStore) Get(name string) (val string, err error) { + err = s.get.QueryRow(name).Scan(&val) + return val, err +} + +// TODO: Use timestamped rows as a more robust method of ensuring data integrity +func (s *DefaultMetaStore) Set(name string, val string) error { + _, err := s.Get(name) + if err == sql.ErrNoRows { + _, err := s.add.Exec(name) + if err != nil { + return err + } + } + _, err = s.set.Exec(val, name) + return err +} diff --git a/common/pages.go b/common/pages.go index 02514a6e..f58b9bf5 100644 --- a/common/pages.go +++ b/common/pages.go @@ -334,6 +334,7 @@ type PanelPage struct { type GridElement struct { ID string + Href string Body string Order int // For future use Class string diff --git a/common/topic_store.go b/common/topic_store.go index bc18cbba..e0763ee3 100644 --- a/common/topic_store.go +++ b/common/topic_store.go @@ -1,7 +1,7 @@ /* * * Gosora Topic Store -* Copyright Azareal 2017 - 2019 +* Copyright Azareal 2017 - 2020 * */ package common diff --git a/common/websockets.go b/common/websockets.go index 7c0a7005..e33fffd8 100644 --- a/common/websockets.go +++ b/common/websockets.go @@ -41,6 +41,7 @@ func init() { type WsTopicList struct { Topics []*WsTopicsRow LastPage int // Not for WebSockets, but for the JSON endpoint for /topics/ to keep the paginator functional + LastUpdate int64 } // TODO: How should we handle errors for this? @@ -78,8 +79,7 @@ func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteErr panic("conn must not be nil") } - messages := bytes.Split(message, []byte("\r")) - for _, msg := range messages { + for _, msg := range bytes.Split(message, []byte("\r")) { //StoppedServer("Profile end") // A bit of code for me to profile the software if bytes.HasPrefix(msg, []byte("page ")) { msgblocks := bytes.SplitN(msg, []byte(" "), 2) @@ -93,11 +93,22 @@ func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteErr wsPageResponses(wsUser, conn, currentPage) } } else if bytes.HasPrefix(msg, []byte("resume ")) { - msgblocks := bytes.SplitN(msg, []byte(" "), 2) - if len(msgblocks) < 2 { + msgblocks := bytes.SplitN(msg, []byte(" "), 3) + if len(msgblocks) < 3 { continue } - //log.Print("resuming on " + string(msgblocks[1])) + //log.Print("resuming on " + string(msgblocks[1]) + " at " + string(msgblocks[2])) + + if !bytes.Equal(msgblocks[1], []byte(currentPage)) { + wsLeavePage(wsUser, conn, currentPage) // Avoid clients abusing late resumes + currentPage = string(msgblocks[1]) + // TODO: Synchronise this better? + resume, err := strconv.ParseInt(string(msgblocks[2]), 10, 64) + wsPageResponses(wsUser, conn, currentPage) + if err != nil { + wsPageResume(wsUser, conn, currentPage, resume) + } + } } /*if bytes.Equal(message,[]byte(`start-view`)) { } else if bytes.Equal(message,[]byte(`end-view`)) { @@ -197,6 +208,26 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) { } } +// TODO: Use a map instead of a switch to make this more modular? +// TODO: Implement this +func wsPageResume(wsUser *WSUser, conn *websocket.Conn, page string, resume int64) { + if page == "/" { + page = Config.DefaultPath + } + + switch { + // TODO: Synchronise this bit of resume with tick updating lastTopicList? + case page == "/topics/": + /*if resume >= hub.lastTick.Unix() { + conn.Write([]byte("resume tooslow")) + } else { + conn.Write([]byte("resume success")) + }*/ + default: + return + } +} + // TODO: Use a map instead of a switch to make this more modular? func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) { if page == "/" { diff --git a/common/ws_hub.go b/common/ws_hub.go index 1dfe9bad..808e9516 100644 --- a/common/ws_hub.go +++ b/common/ws_hub.go @@ -77,16 +77,17 @@ func wsTopicListTick(hub *WsHubImpl) error { if !TopicListThaw.Thawed() && hub.lastTopicList != nil { return nil } + tickStart := time.Now() // Don't waste CPU time if nothing has happened // TODO: Get a topic list method which strips stickies? tList, _, _, err := TopicList.GetList(1, "", nil) if err != nil { - hub.lastTick = time.Now() + hub.lastTick = tickStart return err // TODO: Do we get ErrNoRows here? } defer func() { - hub.lastTick = time.Now() + hub.lastTick = tickStart hub.lastTopicList = tList }() if len(tList) == 0 { @@ -177,7 +178,7 @@ func wsTopicListTick(hub *WsHubImpl) error { wsTopicList[i] = topicRow.WebSockets() } - outBytes, err := json.Marshal(&WsTopicList{wsTopicList, 0}) + outBytes, err := json.Marshal(&WsTopicList{wsTopicList, 0, tickStart.Unix()}) if err != nil { return err } diff --git a/gen_router.go b/gen_router.go index 08bb2fa6..b558c443 100644 --- a/gen_router.go +++ b/gen_router.go @@ -95,6 +95,7 @@ var RouteMap = map[string]interface{}{ "panel.AnalyticsReferrerViews": panel.AnalyticsReferrerViews, "panel.AnalyticsPosts": panel.AnalyticsPosts, "panel.AnalyticsMemory": panel.AnalyticsMemory, + "panel.AnalyticsActiveMemory": panel.AnalyticsActiveMemory, "panel.AnalyticsTopics": panel.AnalyticsTopics, "panel.AnalyticsForums": panel.AnalyticsForums, "panel.Groups": panel.Groups, @@ -244,79 +245,80 @@ var routeMapEnum = map[string]int{ "panel.AnalyticsReferrerViews": 69, "panel.AnalyticsPosts": 70, "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.OpenSearchXml": 142, - "routes.BadRoute": 143, - "routes.HTTPSRedirect": 144, + "panel.AnalyticsActiveMemory": 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.OpenSearchXml": 143, + "routes.BadRoute": 144, + "routes.HTTPSRedirect": 145, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -391,79 +393,80 @@ var reverseRouteMapEnum = map[int]string{ 69: "panel.AnalyticsReferrerViews", 70: "panel.AnalyticsPosts", 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.OpenSearchXml", - 143: "routes.BadRoute", - 144: "routes.HTTPSRedirect", + 72: "panel.AnalyticsActiveMemory", + 73: "panel.AnalyticsTopics", + 74: "panel.AnalyticsForums", + 75: "panel.Groups", + 76: "panel.GroupsEdit", + 77: "panel.GroupsEditPerms", + 78: "panel.GroupsEditSubmit", + 79: "panel.GroupsEditPermsSubmit", + 80: "panel.GroupsCreateSubmit", + 81: "panel.Backups", + 82: "panel.LogsRegs", + 83: "panel.LogsMod", + 84: "panel.Debug", + 85: "panel.Dashboard", + 86: "routes.AccountEdit", + 87: "routes.AccountEditPassword", + 88: "routes.AccountEditPasswordSubmit", + 89: "routes.AccountEditAvatarSubmit", + 90: "routes.AccountEditUsernameSubmit", + 91: "routes.AccountEditMFA", + 92: "routes.AccountEditMFASetup", + 93: "routes.AccountEditMFASetupSubmit", + 94: "routes.AccountEditMFADisableSubmit", + 95: "routes.AccountEditEmail", + 96: "routes.AccountEditEmailTokenSubmit", + 97: "routes.AccountLogins", + 98: "routes.LevelList", + 99: "routes.ViewProfile", + 100: "routes.BanUserSubmit", + 101: "routes.UnbanUser", + 102: "routes.ActivateUser", + 103: "routes.IPSearch", + 104: "routes.CreateTopicSubmit", + 105: "routes.EditTopicSubmit", + 106: "routes.DeleteTopicSubmit", + 107: "routes.StickTopicSubmit", + 108: "routes.UnstickTopicSubmit", + 109: "routes.LockTopicSubmit", + 110: "routes.UnlockTopicSubmit", + 111: "routes.MoveTopicSubmit", + 112: "routes.LikeTopicSubmit", + 113: "routes.AddAttachToTopicSubmit", + 114: "routes.RemoveAttachFromTopicSubmit", + 115: "routes.ViewTopic", + 116: "routes.CreateReplySubmit", + 117: "routes.ReplyEditSubmit", + 118: "routes.ReplyDeleteSubmit", + 119: "routes.ReplyLikeSubmit", + 120: "routes.AddAttachToReplySubmit", + 121: "routes.RemoveAttachFromReplySubmit", + 122: "routes.ProfileReplyCreateSubmit", + 123: "routes.ProfileReplyEditSubmit", + 124: "routes.ProfileReplyDeleteSubmit", + 125: "routes.PollVote", + 126: "routes.PollResults", + 127: "routes.AccountLogin", + 128: "routes.AccountRegister", + 129: "routes.AccountLogout", + 130: "routes.AccountLoginSubmit", + 131: "routes.AccountLoginMFAVerify", + 132: "routes.AccountLoginMFAVerifySubmit", + 133: "routes.AccountRegisterSubmit", + 134: "routes.AccountPasswordReset", + 135: "routes.AccountPasswordResetSubmit", + 136: "routes.AccountPasswordResetToken", + 137: "routes.AccountPasswordResetTokenSubmit", + 138: "routes.DynamicRoute", + 139: "routes.UploadedFile", + 140: "routes.StaticFile", + 141: "routes.RobotsTxt", + 142: "routes.SitemapXml", + 143: "routes.OpenSearchXml", + 144: "routes.BadRoute", + 145: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -618,7 +621,7 @@ type HTTPSRedirect struct { func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - counters.RouteViewCounter.Bump(144) + counters.RouteViewCounter.Bump(145) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -822,7 +825,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.GlobalViewCounter.Bump() if prefix == "/static" { - counters.RouteViewCounter.Bump(139) + counters.RouteViewCounter.Bump(140) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -1518,13 +1521,21 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c counters.RouteViewCounter.Bump(71) err = panel.AnalyticsMemory(w,req,user) - case "/panel/analytics/topics/": + case "/panel/analytics/active-memory/": err = c.ParseForm(w,req,user) if err != nil { return err } counters.RouteViewCounter.Bump(72) + err = panel.AnalyticsActiveMemory(w,req,user) + case "/panel/analytics/topics/": + err = c.ParseForm(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(73) err = panel.AnalyticsTopics(w,req,user) case "/panel/analytics/forums/": err = c.ParseForm(w,req,user) @@ -1532,16 +1543,16 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(73) + counters.RouteViewCounter.Bump(74) err = panel.AnalyticsForums(w,req,user) case "/panel/groups/": - counters.RouteViewCounter.Bump(74) + counters.RouteViewCounter.Bump(75) err = panel.Groups(w,req,user) case "/panel/groups/edit/": - counters.RouteViewCounter.Bump(75) + counters.RouteViewCounter.Bump(76) err = panel.GroupsEdit(w,req,user,extraData) case "/panel/groups/edit/perms/": - counters.RouteViewCounter.Bump(76) + counters.RouteViewCounter.Bump(77) err = panel.GroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1549,7 +1560,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.GroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1557,7 +1568,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.GroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = c.NoSessionMismatch(w,req,user) @@ -1565,7 +1576,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(79) + counters.RouteViewCounter.Bump(80) err = panel.GroupsCreateSubmit(w,req,user) case "/panel/backups/": err = c.SuperAdminOnly(w,req,user) @@ -1579,13 +1590,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(80) + counters.RouteViewCounter.Bump(81) err = panel.Backups(w,req,user,extraData) case "/panel/logs/regs/": - counters.RouteViewCounter.Bump(81) + counters.RouteViewCounter.Bump(82) err = panel.LogsRegs(w,req,user) case "/panel/logs/mod/": - counters.RouteViewCounter.Bump(82) + counters.RouteViewCounter.Bump(83) err = panel.LogsMod(w,req,user) case "/panel/debug/": err = c.AdminOnly(w,req,user) @@ -1593,10 +1604,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(83) + counters.RouteViewCounter.Bump(84) err = panel.Debug(w,req,user) default: - counters.RouteViewCounter.Bump(84) + counters.RouteViewCounter.Bump(85) err = panel.Dashboard(w,req,user) } case "/user": @@ -1607,7 +1618,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 @@ -1619,7 +1630,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(86) + counters.RouteViewCounter.Bump(87) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1636,7 +1647,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.AccountEditPasswordSubmit(w,req,user) case "/user/edit/avatar/submit/": err = c.MemberOnly(w,req,user) @@ -1653,7 +1664,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.AccountEditAvatarSubmit(w,req,user) case "/user/edit/username/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1666,7 +1677,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(89) + counters.RouteViewCounter.Bump(90) err = routes.AccountEditUsernameSubmit(w,req,user) case "/user/edit/mfa/": err = c.MemberOnly(w,req,user) @@ -1674,7 +1685,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 @@ -1686,7 +1697,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(91) + counters.RouteViewCounter.Bump(92) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1703,7 +1714,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.AccountEditMFASetupSubmit(w,req,user) case "/user/edit/mfa/disable/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1716,7 +1727,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(93) + counters.RouteViewCounter.Bump(94) err = routes.AccountEditMFADisableSubmit(w,req,user) case "/user/edit/email/": err = c.MemberOnly(w,req,user) @@ -1724,14 +1735,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(94) + counters.RouteViewCounter.Bump(95) 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(95) + counters.RouteViewCounter.Bump(96) err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData) case "/user/edit/logins/": err = c.MemberOnly(w,req,user) @@ -1739,7 +1750,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 @@ -1751,7 +1762,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(97) + counters.RouteViewCounter.Bump(98) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1759,7 +1770,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(98) + counters.RouteViewCounter.Bump(99) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1779,7 +1790,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.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = c.NoSessionMismatch(w,req,user) @@ -1792,7 +1803,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.UnbanUser(w,req,user,extraData) case "/users/activate/": err = c.NoSessionMismatch(w,req,user) @@ -1805,7 +1816,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(101) + counters.RouteViewCounter.Bump(102) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = c.MemberOnly(w,req,user) @@ -1813,7 +1824,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(102) + counters.RouteViewCounter.Bump(103) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1837,7 +1848,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.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1850,7 +1861,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(104) + counters.RouteViewCounter.Bump(105) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1864,7 +1875,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(105) + counters.RouteViewCounter.Bump(106) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1877,7 +1888,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.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1890,7 +1901,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(107) + counters.RouteViewCounter.Bump(108) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1904,7 +1915,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(108) + counters.RouteViewCounter.Bump(109) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1917,7 +1928,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.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1930,7 +1941,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.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1943,7 +1954,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.LikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -1960,7 +1971,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(112) + counters.RouteViewCounter.Bump(113) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1973,10 +1984,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(113) + counters.RouteViewCounter.Bump(114) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - counters.RouteViewCounter.Bump(114) + counters.RouteViewCounter.Bump(115) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2000,7 +2011,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.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2013,7 +2024,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.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2026,7 +2037,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.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2039,7 +2050,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.ReplyLikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2056,7 +2067,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.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2069,7 +2080,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.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2085,7 +2096,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.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2098,7 +2109,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.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2111,7 +2122,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(123) + counters.RouteViewCounter.Bump(124) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2127,23 +2138,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(124) + counters.RouteViewCounter.Bump(125) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - counters.RouteViewCounter.Bump(125) + counters.RouteViewCounter.Bump(126) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - counters.RouteViewCounter.Bump(126) + counters.RouteViewCounter.Bump(127) 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(127) + counters.RouteViewCounter.Bump(128) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2160,7 +2171,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(128) + counters.RouteViewCounter.Bump(129) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2168,10 +2179,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(129) + counters.RouteViewCounter.Bump(130) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - counters.RouteViewCounter.Bump(130) + counters.RouteViewCounter.Bump(131) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2183,7 +2194,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(131) + counters.RouteViewCounter.Bump(132) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2191,10 +2202,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(132) + counters.RouteViewCounter.Bump(133) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - counters.RouteViewCounter.Bump(133) + counters.RouteViewCounter.Bump(134) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2206,10 +2217,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(134) + counters.RouteViewCounter.Bump(135) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - counters.RouteViewCounter.Bump(135) + counters.RouteViewCounter.Bump(136) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2221,7 +2232,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(136) + counters.RouteViewCounter.Bump(137) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2237,7 +2248,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(138) + counters.RouteViewCounter.Bump(139) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2247,7 +2258,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(140) + counters.RouteViewCounter.Bump(141) return routes.RobotsTxt(w,req) case "favicon.ico": req.URL.Path = "/static"+req.URL.Path+extraData @@ -2255,10 +2266,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c routes.StaticFile(w,req) return nil case "opensearch.xml": - counters.RouteViewCounter.Bump(142) + counters.RouteViewCounter.Bump(143) return routes.OpenSearchXml(w,req) /*case "sitemap.xml": - counters.RouteViewCounter.Bump(141) + counters.RouteViewCounter.Bump(142) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2269,7 +2280,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - counters.RouteViewCounter.Bump(137) // TODO: Be more specific about *which* dynamic route it is + counters.RouteViewCounter.Bump(138) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2280,7 +2291,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - counters.RouteViewCounter.Bump(143) + counters.RouteViewCounter.Bump(144) return c.NotFound(w,req,nil) } return err diff --git a/langs/english.json b/langs/english.json index f8f70c2d..eaf999ec 100644 --- a/langs/english.json +++ b/langs/english.json @@ -741,6 +741,7 @@ "panel_menu_statistics_languages":"Languages", "panel_menu_statistics_referrers":"Referrers", "panel_menu_statistics_memory":"Memory", + "panel_menu_statistics_active_memory":"Active Memory", "panel_menu_reports":"Reports", "panel_menu_logs":"Logs", "panel_menu_logs_registrations":"Registrations", @@ -884,6 +885,7 @@ "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_statistics_time_range_one_year":"1 year", "panel_statistics_time_range_three_months":"3 months", diff --git a/main.go b/main.go index b101473c..234acc2f 100644 --- a/main.go +++ b/main.go @@ -222,6 +222,10 @@ func afterDBInit() (err error) { if err != nil { return errors.WithStack(err) } + c.Meta, err = c.NewDefaultMetaStore(acc) + if err != nil { + return errors.WithStack(err) + } return nil } diff --git a/patcher/patches.go b/patcher/patches.go index 90f07a20..09028bf8 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -33,6 +33,7 @@ func init() { addPatch(18, patch18) addPatch(19, patch19) addPatch(20, patch20) + addPatch(21, patch21) } func patch0(scanner *bufio.Scanner) (err error) { @@ -627,3 +628,27 @@ func patch20(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.AddForeignKey("activity_stream_matches", "asid","activity_stream","asid",true)) } + +func patch21(scanner *bufio.Scanner) error { + err := execStmt(qgen.Builder.AddColumn("memchunks", tblColumn{"stack", "int", 0, false, false, "0"}, nil)) + if err != nil { + return err + } + + err = execStmt(qgen.Builder.AddColumn("memchunks", tblColumn{"heap", "int", 0, false, false, "0"}, nil)) + if err != nil { + return err + } + + err = execStmt(qgen.Builder.CreateTable("meta", "", "", + []tblColumn{ + tblColumn{"name", "varchar", 200, false, false, ""}, + tblColumn{"value", "varchar", 200, false, false, ""}, + }, nil, + )) + if err != nil { + return err + } + + return execStmt(qgen.Builder.AddColumn("activity_stream", tblColumn{"createdAt", "createdAt", 0, false, false, ""}, nil)) +} \ No newline at end of file diff --git a/public/global.js b/public/global.js index e6693f40..8e87ef3b 100644 --- a/public/global.js +++ b/public/global.js @@ -215,8 +215,8 @@ function runWebSockets(resume = false) { // TODO: Sync alerts, topic list, etc. conn.onopen = () => { console.log("The WebSockets connection was opened"); - conn.send("page " + document.location.pathname + '\r'); - if(resume) conn.send("resume " + Math.round((new Date()).getTime() / 1000) + '\r'); + if(resume) conn.send("resume " + document.location.pathname + " " + Math.round((new Date()).getTime() / 1000) + '\r'); + else conn.send("page " + document.location.pathname + '\r'); // TODO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on if(me.User.ID > 0) Notification.requestPermission(); } diff --git a/router_gen/routes.go b/router_gen/routes.go index fcc4dd57..f3ae529f 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -213,6 +213,7 @@ func panelRoutes() *RouteGroup { 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.AnalyticsActiveMemory", "/panel/analytics/active-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 26a1c58a..6d3293e6 100644 --- a/routes/panel/analytics.go +++ b/routes/panel/analytics.go @@ -157,6 +157,40 @@ func analyticsRowsToAverageMap(rows *sql.Rows, labelList []int64, avgMap map[int return avgMap, rows.Err() } +func analyticsRowsToAverageMap2(rows *sql.Rows, labelList []int64, avgMap map[int64]int64) (map[int64]int64, error) { + defer rows.Close() + for rows.Next() { + var stack, heap int64 + var createdAt time.Time + err := rows.Scan(&stack, &heap, &createdAt) + if err != nil { + return avgMap, err + } + var unixCreatedAt = createdAt.Unix() + // TODO: Bulk log this + if c.Dev.SuperDebug { + log.Print("stack: ", stack) + log.Print("heap: ", heap) + log.Print("createdAt: ", createdAt) + log.Print("unixCreatedAt: ", unixCreatedAt) + } + var pAvgMap = make(map[int64]pAvg) + for _, value := range labelList { + if unixCreatedAt > value { + prev := pAvgMap[value] + prev.Avg += stack + heap + 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) { basePage, ferr := buildBasePage(w, r, user, "analytics", "analytics") if ferr != nil { @@ -537,6 +571,42 @@ func AnalyticsMemory(w http.ResponseWriter, r *http.Request, user c.User) c.Rout return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right","analytics","panel_analytics_memory", pi}) } +// TODO: Show stack and heap memory separately on the chart +func AnalyticsActiveMemory(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.AnalyticsActiveMemory") + 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) + } + avgMap, err = analyticsRowsToAverageMap2(rows, labelList, avgMap) + 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.PanelAnalyticsStdUnit{graph, avgItems, timeRange.Range, timeRange.Unit, "time"} + return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right","analytics","panel_analytics_active_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 65ede5c5..edfc4ef4 100644 --- a/routes/panel/dashboard.go +++ b/routes/panel/dashboard.go @@ -171,21 +171,20 @@ func Dashboard(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError return c.InternalError(intErr, w, r) } - 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) + var gridElements = []GE{} + var addElem = func(id string, href string, body string, order int, class string, back string, textColour string, tooltip string) { + gridElements = append(gridElements, GE{id,href,body,order,class,back,textColour,tooltip}) } + // TODO: Implement a check for new versions of Gosora + // TODO: Localise this + //addElem("dash-version", "", "v" + version.String(), 0, "grid_istat stat_green", "", "", "Gosora is up-to-date :)") + addElem("dash-version", "","v" + c.SoftwareVersion.String(), 0, "grid_istat", "", "", "") + + addElem("dash-cpu","", p.GetTmplPhrasef("panel_dashboard_cpu",cpustr), 1, "grid_istat " + cpuColour, "", "", p.GetTmplPhrase("panel_dashboard_cpu_desc")) + addElem("dash-ram","", p.GetTmplPhrasef("panel_dashboard_ram",ramstr), 2, "grid_istat " + ramColour, "", "", p.GetTmplPhrase("panel_dashboard_ram_desc")) + addElem("dash-memused","/panel/analytics/memory/", p.GetTmplPhrasef("panel_dashboard_memused",memCount, memUnit), 2, "grid_istat", "", "", p.GetTmplPhrase("panel_dashboard_memused_desc")) + if c.EnableWebsockets { uonline := c.WsHub.UserCount() gonline := c.WsHub.GuestCount() @@ -200,24 +199,24 @@ func Dashboard(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError uonline, uunit := c.ConvertFriendlyUnit(uonline) gonline, gunit := c.ConvertFriendlyUnit(gonline) - addElement(GE{"dash-totonline", p.GetTmplPhrasef("panel_dashboard_online", totonline, totunit), 3, "grid_stat " + onlineColour, "", "", p.GetTmplPhrase("panel_dashboard_online_desc")}) - addElement(GE{"dash-gonline", p.GetTmplPhrasef("panel_dashboard_guests_online", gonline, gunit), 4, "grid_stat " + onlineGuestsColour, "", "", p.GetTmplPhrase("panel_dashboard_guests_online_desc")}) - addElement(GE{"dash-uonline", p.GetTmplPhrasef("panel_dashboard_users_online", uonline, uunit), 5, "grid_stat " + onlineUsersColour, "", "", p.GetTmplPhrase("panel_dashboard_users_online_desc")}) - //addElement(GE{"dash-reqs", strconv.Itoa(reqCount) + " reqs / second", 7, "grid_stat grid_end_group " + topicColour, "", "", "The number of requests over the last 24 hours"}) + addElem("dash-totonline", "",p.GetTmplPhrasef("panel_dashboard_online", totonline, totunit), 3, "grid_stat " + onlineColour, "", "", p.GetTmplPhrase("panel_dashboard_online_desc")) + addElem("dash-gonline","", p.GetTmplPhrasef("panel_dashboard_guests_online", gonline, gunit), 4, "grid_stat " + onlineGuestsColour, "", "", p.GetTmplPhrase("panel_dashboard_guests_online_desc")) + addElem("dash-uonline","", p.GetTmplPhrasef("panel_dashboard_users_online", uonline, uunit), 5, "grid_stat " + onlineUsersColour, "", "", p.GetTmplPhrase("panel_dashboard_users_online_desc")) + //addElem("dash-reqs","", strconv.Itoa(reqCount) + " reqs / second", 7, "grid_stat grid_end_group " + topicColour, "", "", "The number of requests over the last 24 hours") } - addElement(GE{"dash-postsperday", strconv.Itoa(postCount) + " posts" + postInterval, 6, "grid_stat " + postColour, "", "", "The number of new posts over the last 24 hours"}) - addElement(GE{"dash-topicsperday", strconv.Itoa(topicCount) + " topics" + topicInterval, 7, "grid_stat " + topicColour, "", "", "The number of new topics over the last 24 hours"}) - addElement(GE{"dash-totonlineperday", "?? online / day", 8, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*, "The people online over the last 24 hours"*/}) + addElem("dash-postsperday", "",strconv.Itoa(postCount) + " posts" + postInterval, 6, "grid_stat " + postColour, "", "", "The number of new posts over the last 24 hours") + addElem("dash-topicsperday", "",strconv.Itoa(topicCount) + " topics" + topicInterval, 7, "grid_stat " + topicColour, "", "", "The number of new topics over the last 24 hours") + addElem("dash-totonlineperday","", "?? online / day", 8, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*, "The people online over the last 24 hours"*/) - addElement(GE{"dash-searches", "?? searches / week", 9, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The number of searches over the last 7 days"*/}) - addElement(GE{"dash-newusers", strconv.Itoa(newUserCount) + " new users" + newUserInterval, 10, "grid_stat", "", "", "The number of new users over the last 7 days"}) - addElement(GE{"dash-reports", strconv.Itoa(reportCount) + " reports" + reportInterval, 11, "grid_stat", "", "", "The number of reports over the last 7 days"}) + addElem("dash-searches","", "?? searches / week", 9, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The number of searches over the last 7 days"*/) + addElem("dash-newusers","", strconv.Itoa(newUserCount) + " new users" + newUserInterval, 10, "grid_stat", "", "", "The number of new users over the last 7 days") + addElem("dash-reports","", strconv.Itoa(reportCount) + " reports" + reportInterval, 11, "grid_stat", "", "", "The number of reports over the last 7 days") if false { - addElement(GE{"dash-minperuser", "?? minutes / user / week", 12, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The average number of number of minutes spent by each active user over the last 7 days"*/}) - addElement(GE{"dash-visitorsperweek", "?? visitors / week", 13, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The number of unique visitors we've had over the last 7 days"*/}) - addElement(GE{"dash-postsperuser", "?? posts / user / week", 14, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The average number of posts made by each active user over the past week"*/}) + addElem("dash-minperuser","", "?? minutes / user / week", 12, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The average number of number of minutes spent by each active user over the last 7 days"*/) + addElem("dash-visitorsperweek","", "?? visitors / week", 13, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The number of unique visitors we've had over the last 7 days"*/) + addElem("dash-postsperuser","", "?? posts / user / week", 14, "grid_stat stat_disabled", "", "", p.GetTmplPhrase("panel_dashboard_coming_soon") /*"The average number of posts made by each active user over the past week"*/) } return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_dashboard_right","","panel_dashboard", gridElements}) diff --git a/routes/topic_list.go b/routes/topic_list.go index 74672769..12e85966 100644 --- a/routes/topic_list.go +++ b/routes/topic_list.go @@ -16,7 +16,7 @@ func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList { for i, topicRow := range topicList { wsTopicList[i] = topicRow.WebSockets() } - return &c.WsTopicList{wsTopicList, lastPage} + return &c.WsTopicList{wsTopicList, lastPage, 0} } func TopicList(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { diff --git a/schema/mssql/query_activity_stream.sql b/schema/mssql/query_activity_stream.sql index bebd8d25..944f990c 100644 --- a/schema/mssql/query_activity_stream.sql +++ b/schema/mssql/query_activity_stream.sql @@ -5,5 +5,6 @@ CREATE TABLE [activity_stream] ( [event] nvarchar (50) not null, [elementType] nvarchar (50) not null, [elementID] int not null, + [createdAt] datetime not null, primary key([asid]) ); \ No newline at end of file diff --git a/schema/mssql/query_memchunks.sql b/schema/mssql/query_memchunks.sql index 53faf85e..ecaf6b1f 100644 --- a/schema/mssql/query_memchunks.sql +++ b/schema/mssql/query_memchunks.sql @@ -1,4 +1,6 @@ CREATE TABLE [memchunks] ( [count] int DEFAULT 0 not null, + [stack] int DEFAULT 0 not null, + [heap] int DEFAULT 0 not null, [createdAt] datetime not null ); \ No newline at end of file diff --git a/schema/mssql/query_meta.sql b/schema/mssql/query_meta.sql new file mode 100644 index 00000000..abcbe564 --- /dev/null +++ b/schema/mssql/query_meta.sql @@ -0,0 +1,4 @@ +CREATE TABLE [meta] ( + [name] nvarchar (200) not null, + [value] nvarchar (200) not null +); \ No newline at end of file diff --git a/schema/mysql/query_activity_stream.sql b/schema/mysql/query_activity_stream.sql index 07883079..30ab0b59 100644 --- a/schema/mysql/query_activity_stream.sql +++ b/schema/mysql/query_activity_stream.sql @@ -5,5 +5,6 @@ CREATE TABLE `activity_stream` ( `event` varchar(50) not null, `elementType` varchar(50) not null, `elementID` int not null, + `createdAt` datetime not null, primary key(`asid`) ); \ No newline at end of file diff --git a/schema/mysql/query_memchunks.sql b/schema/mysql/query_memchunks.sql index e79f8faf..3cb8464d 100644 --- a/schema/mysql/query_memchunks.sql +++ b/schema/mysql/query_memchunks.sql @@ -1,4 +1,6 @@ CREATE TABLE `memchunks` ( `count` int DEFAULT 0 not null, + `stack` int DEFAULT 0 not null, + `heap` int DEFAULT 0 not null, `createdAt` datetime not null ); \ No newline at end of file diff --git a/schema/mysql/query_meta.sql b/schema/mysql/query_meta.sql new file mode 100644 index 00000000..555fb3e6 --- /dev/null +++ b/schema/mysql/query_meta.sql @@ -0,0 +1,4 @@ +CREATE TABLE `meta` ( + `name` varchar(200) not null, + `value` varchar(200) not null +); \ No newline at end of file diff --git a/schema/pgsql/query_activity_stream.sql b/schema/pgsql/query_activity_stream.sql index 00fe25dc..72c6801b 100644 --- a/schema/pgsql/query_activity_stream.sql +++ b/schema/pgsql/query_activity_stream.sql @@ -5,5 +5,6 @@ CREATE TABLE "activity_stream" ( `event` varchar (50) not null, `elementType` varchar (50) not null, `elementID` int not null, + `createdAt` timestamp not null, primary key(`asid`) ); \ No newline at end of file diff --git a/schema/pgsql/query_memchunks.sql b/schema/pgsql/query_memchunks.sql index ef6b7894..fccc12b9 100644 --- a/schema/pgsql/query_memchunks.sql +++ b/schema/pgsql/query_memchunks.sql @@ -1,4 +1,6 @@ CREATE TABLE "memchunks" ( `count` int DEFAULT 0 not null, + `stack` int DEFAULT 0 not null, + `heap` int DEFAULT 0 not null, `createdAt` timestamp not null ); \ No newline at end of file diff --git a/schema/pgsql/query_meta.sql b/schema/pgsql/query_meta.sql new file mode 100644 index 00000000..e4d25d23 --- /dev/null +++ b/schema/pgsql/query_meta.sql @@ -0,0 +1,4 @@ +CREATE TABLE "meta" ( + `name` varchar (200) not null, + `value` varchar (200) not null +); \ No newline at end of file diff --git a/templates/panel_analytics_active_memory.html b/templates/panel_analytics_active_memory.html new file mode 100644 index 00000000..1f5f0e85 --- /dev/null +++ b/templates/panel_analytics_active_memory.html @@ -0,0 +1,96 @@ +
+
+

{{lang "panel_statistics_active_memory_head"}}

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

{{lang "panel_statistics_details_head"}}

+
+
+
+ {{range .ViewItems}} +
+ {{.Time}} + {{.Count}}{{.Unit}} +
+ {{else}}
{{lang "panel_statistics_memory_no_memory"}}
{{end}} +
+ \ No newline at end of file diff --git a/templates/panel_analytics_memory.html b/templates/panel_analytics_memory.html index 362fb0e8..e2fa6001 100644 --- a/templates/panel_analytics_memory.html +++ b/templates/panel_analytics_memory.html @@ -1,14 +1,7 @@

{{lang "panel_statistics_memory_head"}}

- + {{template "panel_analytics_time_range_month.html" . }}
@@ -20,7 +13,7 @@

{{lang "panel_statistics_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_time_range_month.html b/templates/panel_analytics_time_range_month.html new file mode 100644 index 00000000..e448432e --- /dev/null +++ b/templates/panel_analytics_time_range_month.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/templates/panel_dashboard.html b/templates/panel_dashboard.html index df74a32b..c7f0990e 100644 --- a/templates/panel_dashboard.html +++ b/templates/panel_dashboard.html @@ -4,7 +4,7 @@
{{range .}}
- {{.Body}} + {{if .Href}}{{.Body}}{{else}}{{.Body}}{{end}}
{{end}}
\ No newline at end of file diff --git a/templates/panel_inner_menu.html b/templates/panel_inner_menu.html index fe6b25ae..11342ab2 100644 --- a/templates/panel_inner_menu.html +++ b/templates/panel_inner_menu.html @@ -65,6 +65,9 @@ + {{end}}
{{lang "panel_menu_reports"}} ({{.Stats.Reports}}) diff --git a/themes/nox/overrides/panel_inner_menu.html b/themes/nox/overrides/panel_inner_menu.html index 96d398e8..e31d47f8 100644 --- a/themes/nox/overrides/panel_inner_menu.html +++ b/themes/nox/overrides/panel_inner_menu.html @@ -65,6 +65,9 @@ + {{end}}
{{lang "panel_menu_reports"}} ({{.Stats.Reports}}) diff --git a/tickloop.go b/tickloop.go index bc42c36d..4572143b 100644 --- a/tickloop.go +++ b/tickloop.go @@ -3,8 +3,9 @@ package main import ( "errors" "log" - "sync/atomic" "time" + "strconv" + "sync/atomic" "database/sql" c "github.com/Azareal/Gosora/common" @@ -51,6 +52,18 @@ func runHook(name string) { } func tickLoop(thumbChan chan bool) { + lastDailyStr, err := c.Meta.Get("lastDaily") + // TODO: Report this error back correctly... + if err != nil && err != sql.ErrNoRows { + c.LogError(err) + } + lastDaily, _ := strconv.ParseInt(lastDailyStr, 10, 64) + now := time.Now().Unix() + low := now - (60 * 60 * 24) + if lastDaily < low { + dailies() + } + // TODO: Write tests for these // Run this goroutine once every half second halfSecondTicker := time.NewTicker(time.Second / 2) @@ -127,31 +140,58 @@ func tickLoop(thumbChan chan bool) { runHook("after_hour_tick") // TODO: Handle the instance going down a lot better case <-dailyTicker.C: - // TODO: Find a more efficient way of doing this - err := qgen.NewAcc().Select("activity_stream").Cols("asid").EachInt(func(asid int) error { - count, err := qgen.NewAcc().Count("activity_stream_matches").Where("asid = ?").Total() - if err != sql.ErrNoRows { - return err - } - if count > 0 { - return nil - } - _, err = qgen.NewAcc().Delete("activity_stream").Where("asid = ?").Run(asid) - return err - }) - if err != nil && err != sql.ErrNoRows { - c.LogError(err) - } - - if c.Config.PostIPCutoff > -1 { - // TODO: Use unixtime to remove this MySQLesque logic? - _, err := qgen.NewAcc().Update("replies").Set("ipaddress = '0'").DateOlderThan("createdAt",c.Config.PostIPCutoff,"day").Where("ipaddress != '0'").Exec() - if err != nil { - c.LogError(err) - } - } + dailies() } // TODO: Handle the daily clean-up. } } + +func dailies() { + // TODO: Find a more efficient way of doing this + err := qgen.NewAcc().Select("activity_stream").Cols("asid").EachInt(func(asid int) error { + count, err := qgen.NewAcc().Count("activity_stream_matches").Where("asid = " + strconv.Itoa(asid)).Total() + if err != sql.ErrNoRows { + return err + } + if count > 0 { + return nil + } + _, err = qgen.NewAcc().Delete("activity_stream").Where("asid = ?").Run(asid) + return err + }) + if err != nil && err != sql.ErrNoRows { + c.LogError(err) + } + + if c.Config.PostIPCutoff > -1 { + // TODO: Use unixtime to remove this MySQLesque logic? + _, err := qgen.NewAcc().Update("topics").Set("ipaddress = '0'").DateOlderThan("createdAt",c.Config.PostIPCutoff,"day").Where("ipaddress != '0'").Exec() + if err != nil { + c.LogError(err) + } + + _, err = qgen.NewAcc().Update("replies").Set("ipaddress = '0'").DateOlderThan("createdAt",c.Config.PostIPCutoff,"day").Where("ipaddress != '0'").Exec() + if err != nil { + c.LogError(err) + } + + // TODO: Find some way of purging the ip data in polls_votes without breaking any anti-cheat measures which might be running... maybe hash it instead? + + _, err = qgen.NewAcc().Update("users_replies").Set("ipaddress = '0'").DateOlderThan("createdAt",c.Config.PostIPCutoff,"day").Where("ipaddress != '0'").Exec() + if err != nil { + c.LogError(err) + } + + // TODO: lastActiveAt isn't currently set, so we can't rely on this to purge last_ips of users who haven't been on in a while + /*_, err = qgen.NewAcc().Update("users").Set("last_ip = '0'").DateOlderThan("lastActiveAt",c.Config.PostIPCutoff,"day").Where("last_ip != '0'").Exec() + if err != nil { + c.LogError(err) + }*/ + } + + err = c.Meta.Set("lastDaily", strconv.FormatInt(time.Now().Unix(), 10)) + if err != nil { + c.LogError(err) + } +} \ No newline at end of file