github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/routes/misc.go (about) 1 package routes 2 3 import ( 4 "bytes" 5 "database/sql" 6 "io" 7 "net/http" 8 "strconv" 9 "strings" 10 "time" 11 12 //"fmt" 13 14 c "github.com/Azareal/Gosora/common" 15 "github.com/Azareal/Gosora/common/phrases" 16 ) 17 18 var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(c.Day)) // TODO: Make this a c.Config value 19 var cacheControlMaxAgeWeek = "max-age=" + strconv.Itoa(int(c.Week)) // TODO: Make this a c.Config value 20 21 // GET functions 22 func StaticFile(w http.ResponseWriter, r *http.Request) { 23 file, ok := c.StaticFiles.Get(r.URL.Path) 24 if !ok { 25 //c.DebugLogf("Failed to find '%s'", r.URL.Path) // TODO: Use MicroNotFound? Might be better than the unneccessary overhead of sprintf 26 w.WriteHeader(http.StatusNotFound) 27 return 28 } 29 h := w.Header() 30 31 if file.Length > 300 { 32 if h.Get("Range") != "" { 33 h.Set("Vary", "Accept-Encoding") 34 if len(file.Sha256) != 0 { 35 h.Set("Cache-Control", cacheControlMaxAgeWeek) 36 } else { 37 h.Set("Cache-Control", cacheControlMaxAge) //Cache-Control: max-age=31536000 38 } 39 ae := r.Header.Get("Accept-Encoding") 40 41 if file.BrLength > 300 && strings.Contains(ae, "br") { 42 h.Set("Content-Encoding", "br") 43 h.Set("Content-Length", file.StrBrLength) 44 http.ServeContent(w, r, r.URL.Path, file.Info.ModTime(), bytes.NewReader(file.BrData)) 45 return 46 } 47 48 if file.GzipLength > 300 && strings.Contains(ae, "gzip") { 49 h.Set("Content-Encoding", "gzip") 50 h.Set("Content-Length", file.StrGzipLength) 51 http.ServeContent(w, r, r.URL.Path, file.Info.ModTime(), bytes.NewReader(file.GzipData)) 52 return 53 } 54 55 h.Set("Content-Length", file.StrLength) 56 http.ServeContent(w, r, r.URL.Path, file.Info.ModTime(), bytes.NewReader(file.Data)) 57 return 58 } 59 } 60 61 // Surely, there's a more efficient way of doing this? 62 t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")) 63 if err == nil && file.Info.ModTime().Before(t.Add(1*time.Second)) { 64 w.WriteHeader(http.StatusNotModified) 65 return 66 } 67 h.Set("Last-Modified", file.FormattedModTime) 68 h.Set("Content-Type", file.Mimetype) 69 if len(file.Sha256) != 0 { 70 h.Set("Cache-Control", cacheControlMaxAgeWeek) 71 } else { 72 h.Set("Cache-Control", cacheControlMaxAge) //Cache-Control: max-age=31536000 73 } 74 h.Set("Vary", "Accept-Encoding") 75 76 if file.BrLength > 0 && strings.Contains(r.Header.Get("Accept-Encoding"), "br") { 77 h.Set("Content-Encoding", "br") 78 h.Set("Content-Length", file.StrBrLength) 79 io.Copy(w, bytes.NewReader(file.BrData)) // Use w.Write instead? 80 } else if file.GzipLength > 0 && strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { 81 h.Set("Content-Encoding", "gzip") 82 h.Set("Content-Length", file.StrGzipLength) 83 io.Copy(w, bytes.NewReader(file.GzipData)) // Use w.Write instead? 84 } else { 85 h.Set("Content-Length", file.StrLength) 86 io.Copy(w, bytes.NewReader(file.Data)) 87 } 88 // Other options instead of io.Copy: io.CopyN(), w.Write(), http.ServeContent() 89 } 90 91 func Overview(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header) c.RouteError { 92 h.Title = phrases.GetTitlePhrase("overview") 93 h.Zone = "overview" 94 return renderTemplate("overview", w, r, h, c.Page{h, tList, nil}) 95 } 96 97 func CustomPage(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, name string) c.RouteError { 98 h.Zone = "custom_page" 99 name = c.SanitiseSingleLine(name) 100 page, err := c.Pages.GetByName(name) 101 if err == nil { 102 h.Title = page.Title 103 return renderTemplate("custom_page", w, r, h, c.CustomPagePage{h, page}) 104 } else if err != sql.ErrNoRows { 105 return c.InternalError(err, w, r) 106 } 107 h.Title = phrases.GetTitlePhrase("page") 108 109 // TODO: Pass the page name to the pre-render hook? 110 err = renderTemplate3("page_"+name, "tmpl_page", w, r, h, c.Page{h, tList, nil}) 111 if err == c.ErrBadDefaultTemplate { 112 return c.NotFound(w, r, h) 113 } else if err != nil { 114 return c.InternalError(err, w, r) 115 } 116 return nil 117 } 118 119 // TODO: Set the cookie domain 120 func ChangeTheme(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { 121 //headerLite, _ := SimpleUserCheck(w, r, u) 122 // TODO: Rename js to something else, just in case we rewrite the JS side in WebAssembly? 123 js := r.PostFormValue("js") == "1" 124 newTheme := c.SanitiseSingleLine(r.PostFormValue("theme")) 125 //fmt.Printf("newTheme: %+v\n", newTheme) 126 127 theme, ok := c.Themes[newTheme] 128 if !ok || theme.HideFromThemes { 129 return c.LocalErrorJSQ("That theme doesn't exist", w, r, u, js) 130 } 131 132 cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: int(c.Year)} 133 http.SetCookie(w, &cookie) 134 return actionSuccess(w, r, "/", js) 135 }