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.
This commit is contained in:
Azareal 2019-04-20 14:55:22 +10:00
parent 114afe0b13
commit b4ffaa2cd6
9 changed files with 131 additions and 52 deletions

View File

@ -83,6 +83,8 @@ var hookTable = &HookTable{
"simple_forum_check_pre_perms": nil, "simple_forum_check_pre_perms": nil,
"forum_check_pre_perms": nil, "forum_check_pre_perms": nil,
"route_topic_list_start": nil,
"action_end_create_topic": nil, "action_end_create_topic": nil,
"action_end_edit_topic":nil, "action_end_edit_topic":nil,
"action_end_delete_topic":nil, "action_end_delete_topic":nil,

View File

@ -1,19 +1,20 @@
package main package main
import c "github.com/Azareal/Gosora/common"
import "github.com/oschwald/geoip2-golang" import "github.com/oschwald/geoip2-golang"
var geoip_db *geoip.DB var geoipDB *geoip.DB
var geoip_db_location string = "geoip_db.mmdb" var geoipDBLocation = "geoip_db.mmdb"
func init() { 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) { func initGeoip(plugin *c.Plugin) (err error) {
geoip_db, err = geoip2.Open(geoip_db_location) geoipDB, err = geoip2.Open(geoipDBLocation)
return err return err
} }
func deactivate_geoip() { func deactivateGeoip(plugin *c.Plugin) {
geoip_db.Close() geoipDB.Close()
} }

View File

@ -2,33 +2,101 @@
package main package main
import ( import (
"log"
"bytes"
"sync/atomic" "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() { 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 { func initHdrive(plugin *c.Plugin) error {
hyperPageCache = newHyperPageCache() hyperspace = newHyperspace()
plugin.AddHook("somewhere", deactivateHyperdrive) plugin.AddHook("tasks_tick_topic_list",tickHdrive)
plugin.AddHook("route_topic_list_start",jumpHdrive)
return nil return nil
} }
func deactivateHyperdrive(plugin *common.Plugin) { func deactivateHdrive(plugin *c.Plugin) {
hyperPageCache = nil 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 topicList atomic.Value
} }
func newHyperPageCache() *HyperPageCache { func newHyperspace() *Hyperspace {
pageCache := new(HyperPageCache) pageCache := new(Hyperspace)
pageCache.topicList.Store([]byte("")) pageCache.topicList.Store([]byte(""))
return pageCache 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
}

View File

@ -6,7 +6,7 @@ import (
"os/exec" "os/exec"
"runtime" "runtime"
"github.com/Azareal/Gosora/common" c "github.com/Azareal/Gosora/common"
) )
/* /*
@ -18,17 +18,17 @@ func init() {
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
return 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) plugin.AddHook("email_send_intercept", sendSendmail)
return nil return nil
} }
// /usr/sbin/sendmail is only available on Linux // /usr/sbin/sendmail is only available on Linux
func activateSendmail(plugin *common.Plugin) error { func activateSendmail(plugin *c.Plugin) error {
if !common.Site.EnableEmails { if !c.Site.EnableEmails {
return errors.New("You have emails disabled in your configuration file") return errors.New("You have emails disabled in your configuration file")
} }
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
@ -37,7 +37,7 @@ func activateSendmail(plugin *common.Plugin) error {
return nil return nil
} }
func deactivateSendmail(plugin *common.Plugin) { func deactivateSendmail(plugin *c.Plugin) {
plugin.RemoveHook("email_send_intercept", sendSendmail) plugin.RemoveHook("email_send_intercept", sendSendmail)
} }
@ -46,7 +46,7 @@ func sendSendmail(data ...interface{}) interface{} {
subject := data[1].(string) subject := data[1].(string)
body := data[2].(string) body := data[2].(string)
msg := "From: " + common.Site.Email + "\n" msg := "From: " + c.Site.Email + "\n"
msg += "To: " + to + "\n" msg += "To: " + to + "\n"
msg += "Subject: " + subject + "\n\n" msg += "Subject: " + subject + "\n\n"
msg += body + "\n" msg += body + "\n"

View File

@ -1,24 +1,24 @@
package main package main
import "github.com/Azareal/Gosora/common" import c "github.com/Azareal/Gosora/common"
func init() { 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 // 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) plugin.AddHook("topic_reply_row_assign", heythereReply)
return nil return nil
} }
func deactivateHeythere(plugin *common.Plugin) { func deactivateHeythere(plugin *c.Plugin) {
plugin.RemoveHook("topic_reply_row_assign", heythereReply) plugin.RemoveHook("topic_reply_row_assign", heythereReply)
} }
func heythereReply(data ...interface{}) interface{} { func heythereReply(data ...interface{}) interface{} {
currentUser := data[0].(*common.TopicPage).Header.CurrentUser currentUser := data[0].(*c.TopicPage).Header.CurrentUser
reply := data[1].(*common.ReplyUser) reply := data[1].(*c.ReplyUser)
reply.Content = "Hey there, " + currentUser.Name + "!" reply.Content = "Hey there, " + currentUser.Name + "!"
reply.ContentHtml = "Hey there, " + currentUser.Name + "!" reply.ContentHtml = "Hey there, " + currentUser.Name + "!"
reply.Tag = "Auto" reply.Tag = "Auto"

View File

@ -3,7 +3,7 @@ package main
import ( import (
"strings" "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 var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
@ -23,10 +23,10 @@ var markdownH1TagOpen []byte
var markdownH1TagClose []byte var markdownH1TagClose []byte
func init() { 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) plugin.AddHook("parse_assign", markdownParse)
markdownUnclosedElement = []byte("<red>[Unclosed Element]</red>") markdownUnclosedElement = []byte("<red>[Unclosed Element]</red>")
@ -46,7 +46,7 @@ func initMarkdown(plugin *common.Plugin) error {
return nil return nil
} }
func deactivateMarkdown(plugin *common.Plugin) { func deactivateMarkdown(plugin *c.Plugin) {
plugin.RemoveHook("parse_assign", markdownParse) plugin.RemoveHook("parse_assign", markdownParse)
} }
@ -69,7 +69,7 @@ func _markdownParse(msg string, n int) string {
var outbytes []byte var outbytes []byte
var lastElement int var lastElement int
var breaking = false 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++ { for index := 0; index < len(msg); index++ {
var simpleMatch = func(char byte, o []byte, c []byte) { var simpleMatch = func(char byte, o []byte, c []byte) {

View File

@ -1,6 +1,6 @@
package main package main
import "github.com/Azareal/Gosora/common" import c "github.com/Azareal/Gosora/common"
func init() { 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. 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 // 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) {}

View File

@ -70,6 +70,7 @@ func doPush(w http.ResponseWriter, header *c.Header) {
var push = func(in []string) { var push = func(in []string) {
for _, path := range in { for _, path := range in {
//fmt.Println("pushing /static/" + path) //fmt.Println("pushing /static/" + path)
// TODO: Avoid concatenating here
err := pusher.Push("/static/"+path, nil) err := pusher.Push("/static/"+path, nil)
if err != nil { if err != nil {
break break
@ -96,20 +97,11 @@ func renderTemplate2(tmplName string, hookName string, w http.ResponseWriter, r
return nil return nil
} }
func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) error { func FootHeaders(w http.ResponseWriter, header *c.Header) {
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
}
// TODO: Expand this to non-HTTPS requests too // TODO: Expand this to non-HTTPS requests too
if !header.LooseCSP && c.Site.EnableSsl { 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") 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 // 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 lastAgent := header.CurrentUser.LastAgent
@ -117,7 +109,19 @@ func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r
if lastAgent == "chrome" || lastAgent == "firefox" { if lastAgent == "chrome" || lastAgent == "firefox" {
doPush(w, header) 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 { if header.CurrentUser.IsAdmin {
header.Elapsed1 = time.Since(header.StartedAt).String() header.Elapsed1 = time.Since(header.StartedAt).String()
} }

View File

@ -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 { 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", "") 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 { if err != nil && err != sql.ErrNoRows {
return c.InternalError(err, w, r) 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: Handle the case where there aren't any items...
// TODO: Add a BulkGet method which returns a slice? // TODO: Add a BulkGet method which returns a slice?
tMap, err := c.Topics.BulkGetMap(tids) tMap, err := c.Topics.BulkGetMap(tids)