From b4ffaa2cd63ae7617194c44e2a02c95e0d9cb717 Mon Sep 17 00:00:00 2001 From: Azareal Date: Sat, 20 Apr 2019 14:55:22 +1000 Subject: [PATCH] Initial work on Plugin Hyperdrive. Reduced the amount of boilerplate in Plugin Markdown. Reduced the amount of boilerplate in Plugin Sendmail. Reduced the amount of boilerplate in sample plugins Heythere and Skeleton. Fixed up Plugin GeoIP. It's not ready for use though. Added the routes.FootHeaders function. Added the route_topic_list_start plugin hook. --- common/extend.go | 2 + experimental/plugin_geoip.go | 15 +++--- experimental/plugin_hyperdrive.go | 90 +++++++++++++++++++++++++++---- experimental/plugin_sendmail.go | 14 ++--- plugin_heythere.go | 12 ++--- plugin_markdown.go | 10 ++-- plugin_skeleton.go | 10 ++-- routes/common.go | 24 +++++---- routes/topic_list.go | 6 ++- 9 files changed, 131 insertions(+), 52 deletions(-) diff --git a/common/extend.go b/common/extend.go index 64bc83a2..c9855cfe 100644 --- a/common/extend.go +++ b/common/extend.go @@ -83,6 +83,8 @@ var hookTable = &HookTable{ "simple_forum_check_pre_perms": nil, "forum_check_pre_perms": nil, + "route_topic_list_start": nil, + "action_end_create_topic": nil, "action_end_edit_topic":nil, "action_end_delete_topic":nil, diff --git a/experimental/plugin_geoip.go b/experimental/plugin_geoip.go index 6531df7f..cda437c4 100644 --- a/experimental/plugin_geoip.go +++ b/experimental/plugin_geoip.go @@ -1,19 +1,20 @@ package main +import c "github.com/Azareal/Gosora/common" import "github.com/oschwald/geoip2-golang" -var geoip_db *geoip.DB -var geoip_db_location string = "geoip_db.mmdb" +var geoipDB *geoip.DB +var geoipDBLocation = "geoip_db.mmdb" func init() { - plugins["geoip"] = NewPlugin("geoip","Geoip","Azareal","http://github.com/Azareal","","","",init_geoip,nil,deactivate_geoip,nil,nil) + c.Plugins.Add(&c.Plugin{UName: "geoip", Name: "Geoip", Author: "Azareal", Init: initGeoip, Deactivate: deactivateGeoip}) } -func init_geoip() (err error) { - geoip_db, err = geoip2.Open(geoip_db_location) +func initGeoip(plugin *c.Plugin) (err error) { + geoipDB, err = geoip2.Open(geoipDBLocation) return err } -func deactivate_geoip() { - geoip_db.Close() +func deactivateGeoip(plugin *c.Plugin) { + geoipDB.Close() } diff --git a/experimental/plugin_hyperdrive.go b/experimental/plugin_hyperdrive.go index 07ed50bd..eb770f87 100644 --- a/experimental/plugin_hyperdrive.go +++ b/experimental/plugin_hyperdrive.go @@ -2,33 +2,101 @@ package main import ( + "log" + "bytes" "sync/atomic" + "net/http" + "net/http/httptest" - "github.com/Azareal/Gosora/common" + c "github.com/Azareal/Gosora/common" + "github.com/Azareal/Gosora/routes" ) -var hyperPageCache *HyperPageCache +var hyperspace *Hyperspace func init() { - common.Plugins.Add(&common.Plugin{UName: "hyperdrive", Name: "Hyperdrive", Author: "Azareal", Init: initHyperdrive, Deactivate: deactivateHyperdrive}) + c.Plugins.Add(&c.Plugin{UName: "hyperdrive", Name: "Hyperdrive", Author: "Azareal", Init: initHdrive, Deactivate: deactivateHdrive}) } -func initHyperdrive(plugin *common.Plugin) error { - hyperPageCache = newHyperPageCache() - plugin.AddHook("somewhere", deactivateHyperdrive) +func initHdrive(plugin *c.Plugin) error { + hyperspace = newHyperspace() + plugin.AddHook("tasks_tick_topic_list",tickHdrive) + plugin.AddHook("route_topic_list_start",jumpHdrive) return nil } -func deactivateHyperdrive(plugin *common.Plugin) { - hyperPageCache = nil +func deactivateHdrive(plugin *c.Plugin) { + plugin.RemoveHook("tasks_tick_topic_list",tickHdrive) + plugin.RemoveHook("route_topic_list_start",jumpHdrive) + hyperspace = nil } -type HyperPageCache struct { +type Hyperspace struct { topicList atomic.Value } -func newHyperPageCache() *HyperPageCache { - pageCache := new(HyperPageCache) +func newHyperspace() *Hyperspace { + pageCache := new(Hyperspace) pageCache.topicList.Store([]byte("")) return pageCache } + +// TODO: Find a better way of doing this +func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { + log.Print("Refueling...") + w := httptest.NewRecorder() + req := httptest.NewRequest("get", "/topics/", bytes.NewReader(nil)) + user := c.GuestUser + + head, err := c.UserCheck(w, req, &user) + if err != nil { + c.LogWarning(err) + return true, rerr + } + + rerr = routes.TopicList(w, req, user, head) + if rerr != nil { + c.LogWarning(err) + return true, rerr + } + if w.Code != 200 { + c.LogWarning(err) + } + + buf := new(bytes.Buffer) + buf.ReadFrom(w.Result().Body) + hyperspace.topicList.Store(buf.Bytes()) + + return false, nil +} + +func jumpHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { + tList := hyperspace.topicList.Load().([]byte) + if len(tList) == 0 { + log.Print("no topiclist in hyperspace") + return false, nil + } + + // Avoid intercepting user requests as we only have guests in cache right now + user := args[2].(*c.User) + if user.ID != 0 { + log.Print("not guest") + return false, nil + } + + // Avoid intercepting search requests and filters as we don't have those in cache + r := args[1].(*http.Request) + //log.Print("r.URL.Path:",r.URL.Path) + log.Print("r.URL.RawQuery:",r.URL.RawQuery) + if r.URL.RawQuery != "" { + return false, nil + } + log.Print("Successful jump") + + w := args[0].(http.ResponseWriter) + header := args[3].(*c.Header) + routes.FootHeaders(w, header) + w.Write(tList) + + return true, nil +} \ No newline at end of file diff --git a/experimental/plugin_sendmail.go b/experimental/plugin_sendmail.go index 84248744..6538099a 100644 --- a/experimental/plugin_sendmail.go +++ b/experimental/plugin_sendmail.go @@ -6,7 +6,7 @@ import ( "os/exec" "runtime" - "github.com/Azareal/Gosora/common" + c "github.com/Azareal/Gosora/common" ) /* @@ -18,17 +18,17 @@ func init() { if runtime.GOOS != "linux" { return } - common.Plugins.Add(&common.Plugin{UName: "sendmail", Name: "Sendmail", Author: "Azareal", URL: "http://github.com/Azareal", Tag: "Linux Only", Init: initSendmail, Activate: activateSendmail, Deactivate: deactivateSendmail}) + c.Plugins.Add(&c.Plugin{UName: "sendmail", Name: "Sendmail", Author: "Azareal", URL: "http://github.com/Azareal", Tag: "Linux Only", Init: initSendmail, Activate: activateSendmail, Deactivate: deactivateSendmail}) } -func initSendmail(plugin *common.Plugin) error { +func initSendmail(plugin *c.Plugin) error { plugin.AddHook("email_send_intercept", sendSendmail) return nil } // /usr/sbin/sendmail is only available on Linux -func activateSendmail(plugin *common.Plugin) error { - if !common.Site.EnableEmails { +func activateSendmail(plugin *c.Plugin) error { + if !c.Site.EnableEmails { return errors.New("You have emails disabled in your configuration file") } if runtime.GOOS != "linux" { @@ -37,7 +37,7 @@ func activateSendmail(plugin *common.Plugin) error { return nil } -func deactivateSendmail(plugin *common.Plugin) { +func deactivateSendmail(plugin *c.Plugin) { plugin.RemoveHook("email_send_intercept", sendSendmail) } @@ -46,7 +46,7 @@ func sendSendmail(data ...interface{}) interface{} { subject := data[1].(string) body := data[2].(string) - msg := "From: " + common.Site.Email + "\n" + msg := "From: " + c.Site.Email + "\n" msg += "To: " + to + "\n" msg += "Subject: " + subject + "\n\n" msg += body + "\n" diff --git a/plugin_heythere.go b/plugin_heythere.go index 80c26c7e..c9f75c68 100644 --- a/plugin_heythere.go +++ b/plugin_heythere.go @@ -1,24 +1,24 @@ package main -import "github.com/Azareal/Gosora/common" +import c "github.com/Azareal/Gosora/common" func init() { - common.Plugins.Add(&common.Plugin{UName: "heythere", Name: "Hey There", Author: "Azareal", URL: "https://github.com/Azareal", Init: initHeythere, Deactivate: deactivateHeythere}) + c.Plugins.Add(&c.Plugin{UName: "heythere", Name: "Hey There", Author: "Azareal", URL: "https://github.com/Azareal", Init: initHeythere, Deactivate: deactivateHeythere}) } // init_heythere is separate from init() as we don't want the plugin to run if the plugin is disabled -func initHeythere(plugin *common.Plugin) error { +func initHeythere(plugin *c.Plugin) error { plugin.AddHook("topic_reply_row_assign", heythereReply) return nil } -func deactivateHeythere(plugin *common.Plugin) { +func deactivateHeythere(plugin *c.Plugin) { plugin.RemoveHook("topic_reply_row_assign", heythereReply) } func heythereReply(data ...interface{}) interface{} { - currentUser := data[0].(*common.TopicPage).Header.CurrentUser - reply := data[1].(*common.ReplyUser) + currentUser := data[0].(*c.TopicPage).Header.CurrentUser + reply := data[1].(*c.ReplyUser) reply.Content = "Hey there, " + currentUser.Name + "!" reply.ContentHtml = "Hey there, " + currentUser.Name + "!" reply.Tag = "Auto" diff --git a/plugin_markdown.go b/plugin_markdown.go index fe4c1d84..eef85d6f 100644 --- a/plugin_markdown.go +++ b/plugin_markdown.go @@ -3,7 +3,7 @@ package main import ( "strings" - "github.com/Azareal/Gosora/common" + c "github.com/Azareal/Gosora/common" ) var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings @@ -23,10 +23,10 @@ var markdownH1TagOpen []byte var markdownH1TagClose []byte func init() { - common.Plugins.Add(&common.Plugin{UName: "markdown", Name: "Markdown", Author: "Azareal", URL: "https://github.com/Azareal", Init: initMarkdown, Deactivate: deactivateMarkdown}) + c.Plugins.Add(&c.Plugin{UName: "markdown", Name: "Markdown", Author: "Azareal", URL: "https://github.com/Azareal", Init: initMarkdown, Deactivate: deactivateMarkdown}) } -func initMarkdown(plugin *common.Plugin) error { +func initMarkdown(plugin *c.Plugin) error { plugin.AddHook("parse_assign", markdownParse) markdownUnclosedElement = []byte("[Unclosed Element]") @@ -46,7 +46,7 @@ func initMarkdown(plugin *common.Plugin) error { return nil } -func deactivateMarkdown(plugin *common.Plugin) { +func deactivateMarkdown(plugin *c.Plugin) { plugin.RemoveHook("parse_assign", markdownParse) } @@ -69,7 +69,7 @@ func _markdownParse(msg string, n int) string { var outbytes []byte var lastElement int var breaking = false - common.DebugLogf("Initial Message: %+v\n", strings.Replace(msg, "\r", "\\r", -1)) + c.DebugLogf("Initial Message: %+v\n", strings.Replace(msg, "\r", "\\r", -1)) for index := 0; index < len(msg); index++ { var simpleMatch = func(char byte, o []byte, c []byte) { diff --git a/plugin_skeleton.go b/plugin_skeleton.go index 88c1560b..191465fc 100644 --- a/plugin_skeleton.go +++ b/plugin_skeleton.go @@ -1,6 +1,6 @@ package main -import "github.com/Azareal/Gosora/common" +import c "github.com/Azareal/Gosora/common" func init() { /* @@ -28,12 +28,12 @@ func init() { That Uninstallation field which is currently unused is for not only deactivating this plugin, but for purging any data associated with it such a new tables or data produced by the end-user. */ - common.Plugins.Add(&common.Plugin{UName: "skeleton", Name: "Skeleton", Author: "Azareal", Init: initSkeleton, Activate: activateSkeleton, Deactivate: deactivateSkeleton}) + c.Plugins.Add(&c.Plugin{UName: "skeleton", Name: "Skeleton", Author: "Azareal", Init: initSkeleton, Activate: activateSkeleton, Deactivate: deactivateSkeleton}) } -func initSkeleton(plugin *common.Plugin) error { return nil } +func initSkeleton(plugin *c.Plugin) error { return nil } // Any errors encountered while trying to activate the plugin are reported back to the admin and the activation is aborted -func activateSkeleton(plugin *common.Plugin) error { return nil } +func activateSkeleton(plugin *c.Plugin) error { return nil } -func deactivateSkeleton(plugin *common.Plugin) {} +func deactivateSkeleton(plugin *c.Plugin) {} diff --git a/routes/common.go b/routes/common.go index 707bf527..b171d3de 100644 --- a/routes/common.go +++ b/routes/common.go @@ -70,6 +70,7 @@ func doPush(w http.ResponseWriter, header *c.Header) { var push = func(in []string) { for _, path := range in { //fmt.Println("pushing /static/" + path) + // TODO: Avoid concatenating here err := pusher.Push("/static/"+path, nil) if err != nil { break @@ -96,20 +97,11 @@ func renderTemplate2(tmplName string, hookName string, w http.ResponseWriter, r return nil } -func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) error { - c.PrepResources(&header.CurrentUser, header, header.Theme) - - if header.CurrentUser.Loggedin { - header.MetaDesc = "" - header.OGDesc = "" - } else if header.MetaDesc != "" && header.OGDesc == "" { - header.OGDesc = header.MetaDesc - } +func FootHeaders(w http.ResponseWriter, header *c.Header) { // TODO: Expand this to non-HTTPS requests too if !header.LooseCSP && c.Site.EnableSsl { w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self' www.youtube-nocookie.com;upgrade-insecure-requests") } - header.AddScript("global.js") // Server pushes can backfire on certain browsers, so we want to make sure it's only triggered for ones where it'll help lastAgent := header.CurrentUser.LastAgent @@ -117,7 +109,19 @@ func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r if lastAgent == "chrome" || lastAgent == "firefox" { doPush(w, header) } +} +func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) error { + c.PrepResources(&header.CurrentUser, header, header.Theme) + if header.CurrentUser.Loggedin { + header.MetaDesc = "" + header.OGDesc = "" + } else if header.MetaDesc != "" && header.OGDesc == "" { + header.OGDesc = header.MetaDesc + } + header.AddScript("global.js") + + FootHeaders(w, header) if header.CurrentUser.IsAdmin { header.Elapsed1 = time.Since(header.StartedAt).String() } diff --git a/routes/topic_list.go b/routes/topic_list.go index a934530c..74672769 100644 --- a/routes/topic_list.go +++ b/routes/topic_list.go @@ -20,6 +20,10 @@ func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList { } func TopicList(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { + skip, rerr := header.Hooks.VhookSkippable("route_topic_list_start", w, r, &user, header) + if skip || rerr != nil { + return rerr + } return TopicListCommon(w, r, user, header, "lastupdated", "") } @@ -106,7 +110,7 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user c.User, header if err != nil && err != sql.ErrNoRows { return c.InternalError(err, w, r) } - //fmt.Printf("tids %+v\n", tids) + //log.Printf("tids %+v\n", tids) // TODO: Handle the case where there aren't any items... // TODO: Add a BulkGet method which returns a slice? tMap, err := c.Topics.BulkGetMap(tids)