diff --git a/common/files.go b/common/files.go index 75b13e8b..57372d8b 100644 --- a/common/files.go +++ b/common/files.go @@ -55,7 +55,7 @@ func (list SFileList) JSTmplInit() error { } path = strings.TrimPrefix(path, "tmpl_client/") - tmplName := strings.TrimSuffix(path, ".go") + tmplName := strings.TrimSuffix(path, ".jgo") shortName := strings.TrimPrefix(tmplName, "template_") var replace = func(data []byte, replaceThis string, withThis string) []byte { @@ -104,10 +104,6 @@ func (list SFileList) JSTmplInit() error { } 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 var each = func(phrase string, handle func(index int)) { diff --git a/common/forum_store.go b/common/forum_store.go index e14780a6..476f0110 100644 --- a/common/forum_store.go +++ b/common/forum_store.go @@ -143,6 +143,7 @@ func (mfs *MemoryForumStore) rebuildView() { }) sort.Sort(SortForum(forumView)) mfs.forumView.Store(forumView) + TopicListThaw.Thaw() } 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.LastTopic = Topics.DirtyGet(forum.LastTopicID) forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID) - TopicListThaw.Thaw() + //TopicListThaw.Thaw() return forum, err } @@ -219,7 +220,6 @@ func (mfs *MemoryForumStore) Reload(id int) error { forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID) mfs.CacheSet(forum) - TopicListThaw.Thaw() return nil } @@ -290,7 +290,6 @@ func (mfs *MemoryForumStore) Delete(id int) error { } _, err := mfs.delete.Exec(id) mfs.CacheDelete(id) - TopicListThaw.Thaw() return err } diff --git a/common/template_init.go b/common/template_init.go index aa45d6a1..6698e9e5 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -495,7 +495,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri if tname != "" { tname = "_" + tname } - err := writeFile(dirPrefix+"template_"+name+tname+".go", content) + err := writeFile(dirPrefix+"template_"+name+tname+".jgo", content) if err != nil { log.Fatal(err) } diff --git a/common/templates/templates.go b/common/templates/templates.go index 69dd2b74..0602e650 100644 --- a/common/templates/templates.go +++ b/common/templates/templates.go @@ -442,20 +442,22 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe fout += "}\n\n" } + if c.lang == "normal" { fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n" fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `) if !ok { 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) if ok { iw = gzw.ResponseWriter } _ = iw ` + } else { + fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars interface{}, w io.Writer) error {\n" } if len(c.langIndexToName) > 0 { diff --git a/experimental/plugin_hyperdrive.go b/experimental/plugin_hyperdrive.go index d3168887..2643ebd9 100644 --- a/experimental/plugin_hyperdrive.go +++ b/experimental/plugin_hyperdrive.go @@ -5,6 +5,9 @@ import ( //"log" "bytes" "errors" + "strings" + "strconv" + "time" "sync/atomic" "net/http" "net/http/httptest" @@ -37,11 +40,14 @@ func deactivateHdrive(plugin *c.Plugin) { type Hyperspace struct { topicList atomic.Value gzipTopicList atomic.Value + lastTopicListUpdate atomic.Value } func newHyperspace() *Hyperspace { pageCache := new(Hyperspace) pageCache.topicList.Store([]byte("")) + pageCache.gzipTopicList.Store([]byte("")) + pageCache.lastTopicListUpdate.Store(int64(0)) return pageCache } @@ -86,6 +92,7 @@ func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { return false, nil } hyperspace.gzipTopicList.Store(gbuf) + hyperspace.lastTopicListUpdate.Store(time.Now().Unix()) return false, nil } @@ -128,12 +135,29 @@ func jumpHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { //c.DebugLog 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) routes.FootHeaders(w, header) iw.Write(tList) - if ok { - w.Header().Set("X-I","1") - } return true, nil } \ No newline at end of file diff --git a/public/init.js b/public/init.js index 7d35cbfe..544b46f1 100644 --- a/public/init.js +++ b/public/init.js @@ -185,7 +185,7 @@ function initPhrases(loggedIn, panel = false) { } function fetchPhrases(plist) { - fetch("/api/phrases/?query="+plist) + fetch("/api/phrases/?query="+plist, {cache: "no-cache"}) .then((resp) => resp.json()) .then((data) => { console.log("loaded phrase endpoint data"); diff --git a/routes.go b/routes.go index dacdb059..56656891 100644 --- a/routes.go +++ b/routes.go @@ -191,32 +191,50 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout 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 posLoop = func(positive string) c.RouteError { + // ! Constrain it to a subset of phrases for now + for _, item := range phraseWhitelist { + if strings.HasPrefix(positive, item) { + // TODO: Break this down into smaller security boundaries based on control panel sections? + if strings.HasPrefix(positive,"panel") { + w.Header().Set("Cache-Control", "private") + ok = user.IsSuperMod + } else { + 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 + } + } + if !ok { + 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 - // 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 - var ok = false - for _, item := range phraseWhitelist { - if strings.HasPrefix(positive, item) { - // TODO: Break this down into smaller security boundaries based on control panel sections? - if strings.HasPrefix(positive,"panel") { - if user.IsSuperMod { - ok = true - w.Header().Set("Cache-Control", "private") - } - } else { - ok = true - } - break - } + rerr := posLoop(positive) + if rerr != nil { + return rerr } - if !ok { - return c.PreErrorJS("Outside of phrase prefix whitelist", w, r) - } - pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive) if !ok { 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 { - // ! Constrain it to a subset of phrases for now - var ok = false - for _, item := range phraseWhitelist { - 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 - } + rerr := posLoop(positives[0]) + if rerr != nil { + return rerr } - if !ok { - return c.PreErrorJS("Outside of phrase prefix whitelist", w, r) - } - pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0]) if !ok { return c.PreErrorJS("No such prefix", w, r)