ETags are now set by /api/phrases and Hyperdrive on the topic list for better caching.
Initialised the fields in hyperspace properly. Eliminate an unnecessary line in client templates. Cleaned up some thaw code in the forum store. Fixed a potential premature thaw in Forums.BypassGet
This commit is contained in:
parent
4414957885
commit
efe7a0f3f0
|
@ -55,7 +55,7 @@ func (list SFileList) JSTmplInit() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
path = strings.TrimPrefix(path, "tmpl_client/")
|
path = strings.TrimPrefix(path, "tmpl_client/")
|
||||||
tmplName := strings.TrimSuffix(path, ".go")
|
tmplName := strings.TrimSuffix(path, ".jgo")
|
||||||
shortName := strings.TrimPrefix(tmplName, "template_")
|
shortName := strings.TrimPrefix(tmplName, "template_")
|
||||||
|
|
||||||
var replace = func(data []byte, replaceThis string, withThis string) []byte {
|
var replace = func(data []byte, replaceThis string, withThis string) []byte {
|
||||||
|
@ -104,10 +104,6 @@ func (list SFileList) JSTmplInit() error {
|
||||||
}
|
}
|
||||||
return out + "]"
|
return out + "]"
|
||||||
}*/
|
}*/
|
||||||
data = replace(data, `)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("invalid page struct value")
|
|
||||||
}`, "*/tmpl_"+shortName+"_vars = tmpl_"+shortName+"_i")
|
|
||||||
|
|
||||||
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
|
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
|
||||||
var each = func(phrase string, handle func(index int)) {
|
var each = func(phrase string, handle func(index int)) {
|
||||||
|
|
|
@ -143,6 +143,7 @@ func (mfs *MemoryForumStore) rebuildView() {
|
||||||
})
|
})
|
||||||
sort.Sort(SortForum(forumView))
|
sort.Sort(SortForum(forumView))
|
||||||
mfs.forumView.Store(forumView)
|
mfs.forumView.Store(forumView)
|
||||||
|
TopicListThaw.Thaw()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mfs *MemoryForumStore) DirtyGet(id int) *Forum {
|
func (mfs *MemoryForumStore) DirtyGet(id int) *Forum {
|
||||||
|
@ -190,7 +191,7 @@ func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) {
|
||||||
forum.Link = BuildForumURL(NameToSlug(forum.Name), forum.ID)
|
forum.Link = BuildForumURL(NameToSlug(forum.Name), forum.ID)
|
||||||
forum.LastTopic = Topics.DirtyGet(forum.LastTopicID)
|
forum.LastTopic = Topics.DirtyGet(forum.LastTopicID)
|
||||||
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
|
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
|
||||||
TopicListThaw.Thaw()
|
//TopicListThaw.Thaw()
|
||||||
|
|
||||||
return forum, err
|
return forum, err
|
||||||
}
|
}
|
||||||
|
@ -219,7 +220,6 @@ func (mfs *MemoryForumStore) Reload(id int) error {
|
||||||
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
|
forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID)
|
||||||
|
|
||||||
mfs.CacheSet(forum)
|
mfs.CacheSet(forum)
|
||||||
TopicListThaw.Thaw()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,6 @@ func (mfs *MemoryForumStore) Delete(id int) error {
|
||||||
}
|
}
|
||||||
_, err := mfs.delete.Exec(id)
|
_, err := mfs.delete.Exec(id)
|
||||||
mfs.CacheDelete(id)
|
mfs.CacheDelete(id)
|
||||||
TopicListThaw.Thaw()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -495,7 +495,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
|
||||||
if tname != "" {
|
if tname != "" {
|
||||||
tname = "_" + tname
|
tname = "_" + tname
|
||||||
}
|
}
|
||||||
err := writeFile(dirPrefix+"template_"+name+tname+".go", content)
|
err := writeFile(dirPrefix+"template_"+name+tname+".jgo", content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -442,13 +442,13 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
||||||
fout += "}\n\n"
|
fout += "}\n\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.lang == "normal" {
|
||||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n"
|
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n"
|
||||||
fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
|
fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("invalid page struct value")
|
return errors.New("invalid page struct value")
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
if c.lang == "normal" {
|
|
||||||
fout += `var iw http.ResponseWriter
|
fout += `var iw http.ResponseWriter
|
||||||
gzw, ok := w.(common.GzipResponseWriter)
|
gzw, ok := w.(common.GzipResponseWriter)
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -456,6 +456,8 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
||||||
}
|
}
|
||||||
_ = iw
|
_ = iw
|
||||||
`
|
`
|
||||||
|
} else {
|
||||||
|
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars interface{}, w io.Writer) error {\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.langIndexToName) > 0 {
|
if len(c.langIndexToName) > 0 {
|
||||||
|
|
|
@ -5,6 +5,9 @@ import (
|
||||||
//"log"
|
//"log"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
@ -37,11 +40,14 @@ func deactivateHdrive(plugin *c.Plugin) {
|
||||||
type Hyperspace struct {
|
type Hyperspace struct {
|
||||||
topicList atomic.Value
|
topicList atomic.Value
|
||||||
gzipTopicList atomic.Value
|
gzipTopicList atomic.Value
|
||||||
|
lastTopicListUpdate atomic.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHyperspace() *Hyperspace {
|
func newHyperspace() *Hyperspace {
|
||||||
pageCache := new(Hyperspace)
|
pageCache := new(Hyperspace)
|
||||||
pageCache.topicList.Store([]byte(""))
|
pageCache.topicList.Store([]byte(""))
|
||||||
|
pageCache.gzipTopicList.Store([]byte(""))
|
||||||
|
pageCache.lastTopicListUpdate.Store(int64(0))
|
||||||
return pageCache
|
return pageCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +92,7 @@ func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
hyperspace.gzipTopicList.Store(gbuf)
|
hyperspace.gzipTopicList.Store(gbuf)
|
||||||
|
hyperspace.lastTopicListUpdate.Store(time.Now().Unix())
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -128,12 +135,29 @@ func jumpHdrive(args ...interface{}) (skip bool, rerr c.RouteError) {
|
||||||
//c.DebugLog
|
//c.DebugLog
|
||||||
c.DebugLog("Successful jump")
|
c.DebugLog("Successful jump")
|
||||||
|
|
||||||
|
var etag string
|
||||||
|
lastUpdate := hyperspace.lastTopicListUpdate.Load().(int64)
|
||||||
|
c.DebugLog("lastUpdate:",lastUpdate)
|
||||||
|
if ok {
|
||||||
|
iw.Header().Set("X-I","1")
|
||||||
|
etag = "\""+strconv.FormatInt(lastUpdate, 10)+"-g\""
|
||||||
|
} else {
|
||||||
|
etag = "\""+strconv.FormatInt(lastUpdate, 10)+"\""
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastUpdate != 0 {
|
||||||
|
iw.Header().Set("ETag", etag)
|
||||||
|
if match := r.Header.Get("If-None-Match"); match != "" {
|
||||||
|
if strings.Contains(match, etag) {
|
||||||
|
iw.WriteHeader(http.StatusNotModified)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
header := args[3].(*c.Header)
|
header := args[3].(*c.Header)
|
||||||
routes.FootHeaders(w, header)
|
routes.FootHeaders(w, header)
|
||||||
iw.Write(tList)
|
iw.Write(tList)
|
||||||
if ok {
|
|
||||||
w.Header().Set("X-I","1")
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
|
@ -185,7 +185,7 @@ function initPhrases(loggedIn, panel = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchPhrases(plist) {
|
function fetchPhrases(plist) {
|
||||||
fetch("/api/phrases/?query="+plist)
|
fetch("/api/phrases/?query="+plist, {cache: "no-cache"})
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
console.log("loaded phrase endpoint data");
|
console.log("loaded phrase endpoint data");
|
||||||
|
|
58
routes.go
58
routes.go
|
@ -191,24 +191,32 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
|
||||||
return c.PreErrorJS("You haven't requested any phrases", w, r)
|
return c.PreErrorJS("You haven't requested any phrases", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var etag string
|
||||||
|
_, ok := w.(c.GzipResponseWriter)
|
||||||
|
if ok {
|
||||||
|
etag = "\""+strconv.FormatInt(c.StartTime.Unix(), 10)+"-g\""
|
||||||
|
} else {
|
||||||
|
etag = "\""+strconv.FormatInt(c.StartTime.Unix(), 10)+"\""
|
||||||
|
}
|
||||||
|
|
||||||
var plist map[string]string
|
var plist map[string]string
|
||||||
// A little optimisation to avoid copying entries from one map to the other, if we don't have to mutate it
|
var posLoop = func(positive string) c.RouteError {
|
||||||
// TODO: Reduce the amount of duplication here
|
|
||||||
if len(positives) > 1 {
|
|
||||||
plist = make(map[string]string)
|
|
||||||
for _, positive := range positives {
|
|
||||||
// ! Constrain it to a subset of phrases for now
|
// ! Constrain it to a subset of phrases for now
|
||||||
var ok = false
|
|
||||||
for _, item := range phraseWhitelist {
|
for _, item := range phraseWhitelist {
|
||||||
if strings.HasPrefix(positive, item) {
|
if strings.HasPrefix(positive, item) {
|
||||||
// TODO: Break this down into smaller security boundaries based on control panel sections?
|
// TODO: Break this down into smaller security boundaries based on control panel sections?
|
||||||
if strings.HasPrefix(positive,"panel") {
|
if strings.HasPrefix(positive,"panel") {
|
||||||
if user.IsSuperMod {
|
|
||||||
ok = true
|
|
||||||
w.Header().Set("Cache-Control", "private")
|
w.Header().Set("Cache-Control", "private")
|
||||||
}
|
ok = user.IsSuperMod
|
||||||
} else {
|
} else {
|
||||||
ok = true
|
ok = true
|
||||||
|
w.Header().Set("ETag", etag)
|
||||||
|
if match := r.Header.Get("If-None-Match"); match != "" {
|
||||||
|
if strings.Contains(match, etag) {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -216,7 +224,17 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
|
||||||
if !ok {
|
if !ok {
|
||||||
return c.PreErrorJS("Outside of phrase prefix whitelist", w, r)
|
return c.PreErrorJS("Outside of phrase prefix whitelist", w, r)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A little optimisation to avoid copying entries from one map to the other, if we don't have to mutate it
|
||||||
|
if len(positives) > 1 {
|
||||||
|
plist = make(map[string]string)
|
||||||
|
for _, positive := range positives {
|
||||||
|
rerr := posLoop(positive)
|
||||||
|
if rerr != nil {
|
||||||
|
return rerr
|
||||||
|
}
|
||||||
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive)
|
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive)
|
||||||
if !ok {
|
if !ok {
|
||||||
return c.PreErrorJS("No such prefix", w, r)
|
return c.PreErrorJS("No such prefix", w, r)
|
||||||
|
@ -226,26 +244,10 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ! Constrain it to a subset of phrases for now
|
rerr := posLoop(positives[0])
|
||||||
var ok = false
|
if rerr != nil {
|
||||||
for _, item := range phraseWhitelist {
|
return rerr
|
||||||
if strings.HasPrefix(positives[0], item) {
|
|
||||||
// TODO: Break this down into smaller security boundaries based on control panel sections?
|
|
||||||
if strings.HasPrefix(positives[0],"panel") {
|
|
||||||
if user.IsSuperMod {
|
|
||||||
ok = true
|
|
||||||
w.Header().Set("Cache-Control", "private")
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return c.PreErrorJS("Outside of phrase prefix whitelist", w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0])
|
pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0])
|
||||||
if !ok {
|
if !ok {
|
||||||
return c.PreErrorJS("No such prefix", w, r)
|
return c.PreErrorJS("No such prefix", w, r)
|
||||||
|
|
Loading…
Reference in New Issue