Refactored the error handlers.
The static files for themes are now automatically reloaded without having to restart Gosora. Added the Get and Set methods to the SFileList type. Static files can now be updated during execution in a thread-safe way. Removed Riot as a dependency. Began work on making Cosora responsive like the other themes.
This commit is contained in:
parent
0e9cebfa47
commit
9eae8da180
|
@ -86,7 +86,7 @@ Several important features for saving memory in the templates system may have to
|
|||
|
||||
# Advanced Installation
|
||||
|
||||
An example of running the commands directly on Windows.
|
||||
An example of running the commands directly on Windows. We're looking into reducing the number of commands you need to type, for instance, you could invoke the update-deps batch or shell files to install / update all of the dependencies instead of typing each `get get -u`
|
||||
|
||||
Linux is similar, however you might need to use cd and mv a bit more like in the shell files due to the differences in go build across platforms. Additionally, Linux doesn't require `StackExchange/wmi` or ``/x/sys/windows`
|
||||
|
||||
|
@ -168,7 +168,7 @@ We're looking for ways to clean-up the plugin system so that all of them (except
|
|||
|
||||
More images in the /images/ folder. Beware though, some of them are *really* outdated.
|
||||
|
||||
# Dependencies (a few of these like Riot aren't currently in use, but we anticipate that we'll need some sort of search engine library in the very immediate future)
|
||||
# Dependencies (a few of these like Rez aren't currently in use, but we anticipate that we'll need some sort of search engine library in the very immediate future)
|
||||
|
||||
* Go 1.9
|
||||
|
||||
|
@ -194,8 +194,6 @@ More images in the /images/ folder. Beware though, some of them are *really* out
|
|||
|
||||
* ithub.com/denisenkom/go-mssqldb For interfacing with MSSQL. You will be able to pick this instead of MSSQL soon.
|
||||
|
||||
* github.com/go-ego/riot A search engine library.
|
||||
|
||||
* github.com/bamiaux/rez An image resizer (e.g. for spitting out thumbnails)
|
||||
|
||||
* github.com/fsnotify/fsnotify A library for watching events on the file system.
|
||||
|
|
119
common/errors.go
119
common/errors.go
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import "log"
|
||||
|
||||
import "sync"
|
||||
import "net/http"
|
||||
import "runtime/debug"
|
||||
|
@ -32,10 +31,6 @@ type RouteErrorImpl struct {
|
|||
handled bool
|
||||
}
|
||||
|
||||
/*func NewRouteError(msg string, system bool, json bool) RouteError {
|
||||
return &RouteErrorImpl{msg, system, json, false}
|
||||
}*/
|
||||
|
||||
func (err *RouteErrorImpl) Type() string {
|
||||
// System errors may contain sensitive information we don't want the user to see
|
||||
if err.system {
|
||||
|
@ -80,21 +75,9 @@ func LogWarning(err error) {
|
|||
// InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong
|
||||
// ? - Add a user parameter?
|
||||
func InternalError(err error, w http.ResponseWriter, r *http.Request) RouteError {
|
||||
log.Print(err)
|
||||
debug.PrintStack()
|
||||
|
||||
// TODO: Centralise the user struct somewhere else
|
||||
user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||
pi := Page{"Internal Server Error", user, DefaultHeaderVar(), tList, "A problem has occurred in the system."}
|
||||
err = Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
errorBufferMutex.Lock()
|
||||
defer errorBufferMutex.Unlock()
|
||||
errorBuffer = append(errorBuffer, err)
|
||||
log.Fatal("")
|
||||
pi := Page{"Internal Server Error", GuestUser, DefaultHeaderVar(), tList, "A problem has occurred in the system."}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
LogError(err)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -112,26 +95,14 @@ func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, isJs bo
|
|||
func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) RouteError {
|
||||
w.WriteHeader(500)
|
||||
_, _ = w.Write([]byte(`{"errmsg":"A problem has occurred in the system."}`))
|
||||
errorBufferMutex.Lock()
|
||||
defer errorBufferMutex.Unlock()
|
||||
errorBuffer = append(errorBuffer, err)
|
||||
log.Fatal(err)
|
||||
LogError(err)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) RouteError {
|
||||
w.WriteHeader(500)
|
||||
user := User{ID: 0, Group: 6, Perms: GuestPerms}
|
||||
pi := Page{"Error", user, DefaultHeaderVar(), tList, errmsg}
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
pi := Page{"Error", GuestUser, DefaultHeaderVar(), tList, errmsg}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -152,15 +123,7 @@ func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs boo
|
|||
func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(500)
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, errmsg}
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -182,16 +145,7 @@ func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) RouteEr
|
|||
func NoPermissions(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(403)
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You don't have permission to do that."}
|
||||
// TODO: What to do about this hook?
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -212,15 +166,7 @@ func NoPermissionsJS(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
|||
func Banned(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(403)
|
||||
pi := Page{"Banned", user, DefaultHeaderVar(), tList, "You have been banned from this site."}
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -252,15 +198,7 @@ func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bo
|
|||
func LoginRequired(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(401)
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You need to login to do that."}
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -292,30 +230,14 @@ func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
// ? - Add a JSQ and JS version of this?
|
||||
// ? - Add a user parameter?
|
||||
func NotFound(w http.ResponseWriter, r *http.Request) RouteError {
|
||||
w.WriteHeader(404)
|
||||
// TODO: Centralise the user struct somewhere else
|
||||
user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||
pi := Page{"Not Found", user, DefaultHeaderVar(), tList, "The requested page doesn't exist."}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return HandledRouteError()
|
||||
return CustomError("The requested page doesn't exist.", 404, "Not Found", w, r, GuestUser)
|
||||
}
|
||||
|
||||
// CustomError lets us make custom error types which aren't covered by the generic functions above
|
||||
func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(errcode)
|
||||
pi := Page{errtitle, user, DefaultHeaderVar(), tList, errmsg}
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
|
@ -324,12 +246,25 @@ func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.Response
|
|||
if !isJs {
|
||||
return CustomError(errmsg, errcode, errtitle, w, r, user)
|
||||
}
|
||||
return CustomErrorJS(errmsg, errcode, errtitle, w, r, user)
|
||||
return CustomErrorJS(errmsg, errcode, w, r, user)
|
||||
}
|
||||
|
||||
// CustomErrorJS is the pure JSON version of CustomError
|
||||
func CustomErrorJS(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func CustomErrorJS(errmsg string, errcode int, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(errcode)
|
||||
_, _ = w.Write([]byte(`{"errmsg":"` + errmsg + `"}`))
|
||||
return HandledRouteError()
|
||||
}
|
||||
|
||||
func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi Page) {
|
||||
// TODO: What to do about this hook?
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &pi.CurrentUser, &pi) {
|
||||
return
|
||||
}
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package common
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"mime"
|
||||
"strings"
|
||||
"sync"
|
||||
//"errors"
|
||||
"compress/gzip"
|
||||
"io/ioutil"
|
||||
|
@ -16,6 +16,7 @@ import (
|
|||
type SFileList map[string]SFile
|
||||
|
||||
var StaticFiles SFileList = make(map[string]SFile)
|
||||
var staticFileMutex sync.RWMutex
|
||||
|
||||
type SFile struct {
|
||||
Data []byte
|
||||
|
@ -48,11 +49,9 @@ func (list SFileList) Init() error {
|
|||
var ext = filepath.Ext("/public/" + path)
|
||||
gzipData := compressBytesGzip(data)
|
||||
|
||||
list["/static/"+path] = SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
list.Set("/static/"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||
|
||||
if Dev.DebugMode {
|
||||
log.Print("Added the '" + path + "' static file.")
|
||||
}
|
||||
debugLogf("Added the '%s' static file.", path)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -75,14 +74,25 @@ func (list SFileList) Add(path string, prefix string) error {
|
|||
path = strings.TrimPrefix(path, prefix)
|
||||
gzipData := compressBytesGzip(data)
|
||||
|
||||
list["/static"+path] = SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
list.Set("/static"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||
|
||||
if Dev.DebugMode {
|
||||
log.Printf("Added the '%s' static file", path)
|
||||
}
|
||||
debugLogf("Added the '%s' static file", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list SFileList) Get(name string) (file SFile, exists bool) {
|
||||
staticFileMutex.RLock()
|
||||
defer staticFileMutex.RUnlock()
|
||||
file, exists = list[name]
|
||||
return file, exists
|
||||
}
|
||||
|
||||
func (list SFileList) Set(name string, data SFile) {
|
||||
staticFileMutex.Lock()
|
||||
defer staticFileMutex.Unlock()
|
||||
list[name] = data
|
||||
}
|
||||
|
||||
func compressBytesGzip(in []byte) []byte {
|
||||
var buff bytes.Buffer
|
||||
gz := gzip.NewWriter(&buff)
|
||||
|
|
|
@ -14,8 +14,8 @@ type HeaderVars struct {
|
|||
Widgets PageWidgets
|
||||
Site *site
|
||||
Settings SettingMap
|
||||
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed?
|
||||
Theme Theme
|
||||
Themes map[string]*Theme // TODO: Use a slice containing every theme instead of the main map for speed?
|
||||
Theme *Theme
|
||||
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
||||
Zone string
|
||||
Writer http.ResponseWriter
|
||||
|
@ -151,8 +151,8 @@ type PanelThemesPage struct {
|
|||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
PrimaryThemes []Theme
|
||||
VariantThemes []Theme
|
||||
PrimaryThemes []*Theme
|
||||
VariantThemes []*Theme
|
||||
}
|
||||
|
||||
type PanelUserPage struct {
|
||||
|
|
|
@ -97,7 +97,7 @@ func cascadeForumPerms(fperms *ForumPerms, user *User) {
|
|||
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
||||
// TODO: Do a panel specific theme?
|
||||
func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, rerr RouteError) {
|
||||
var theme Theme
|
||||
var theme = &Theme{Name: ""}
|
||||
|
||||
cookie, err := r.Cookie("current_theme")
|
||||
if err == nil {
|
||||
|
@ -116,6 +116,7 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
|||
Themes: Themes,
|
||||
Theme: theme,
|
||||
Zone: "panel",
|
||||
Writer: w,
|
||||
}
|
||||
// TODO: We should probably initialise headerVars.ExtData
|
||||
|
||||
|
@ -189,7 +190,7 @@ func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
|||
|
||||
// TODO: Add the ability for admins to restrict certain themes to certain groups?
|
||||
func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, rerr RouteError) {
|
||||
var theme Theme
|
||||
var theme = &Theme{Name: ""}
|
||||
|
||||
cookie, err := r.Cookie("current_theme")
|
||||
if err == nil {
|
||||
|
@ -208,6 +209,7 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *
|
|||
Themes: Themes,
|
||||
Theme: theme,
|
||||
Zone: "frontend",
|
||||
Writer: w,
|
||||
}
|
||||
|
||||
if user.IsBanned {
|
||||
|
|
|
@ -22,9 +22,9 @@ import (
|
|||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
type ThemeList map[string]Theme // ? Use pointers instead?
|
||||
type ThemeList map[string]*Theme
|
||||
|
||||
var Themes ThemeList = make(map[string]Theme)
|
||||
var Themes ThemeList = make(map[string]*Theme)
|
||||
var DefaultThemeBox atomic.Value
|
||||
var ChangeDefaultThemeMutex sync.Mutex
|
||||
|
||||
|
@ -45,7 +45,6 @@ type Theme struct {
|
|||
Tag string
|
||||
URL string
|
||||
Docks []string // Allowed Values: leftSidebar, rightSidebar, footer
|
||||
AboutSegment bool // ? - Should this be a theme var instead?
|
||||
Settings map[string]ThemeSetting
|
||||
Templates []TemplateMapping
|
||||
TemplatesMap map[string]string
|
||||
|
@ -117,7 +116,7 @@ func (themes ThemeList) LoadActiveStatus() error {
|
|||
log.Printf("Loading the default theme '%s'", theme.Name)
|
||||
theme.Active = true
|
||||
DefaultThemeBox.Store(theme.Name)
|
||||
MapThemeTemplates(theme)
|
||||
theme.MapTemplates()
|
||||
} else {
|
||||
log.Printf("Loading the theme '%s'", theme.Name)
|
||||
theme.Active = false
|
||||
|
@ -147,8 +146,8 @@ func InitThemes() error {
|
|||
return err
|
||||
}
|
||||
|
||||
var theme Theme
|
||||
err = json.Unmarshal(themeFile, &theme)
|
||||
var theme = &Theme{Name: ""}
|
||||
err = json.Unmarshal(themeFile, theme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -185,21 +184,25 @@ func InitThemes() error {
|
|||
}
|
||||
}
|
||||
|
||||
theme.ResourceTemplates = template.New("")
|
||||
template.Must(theme.ResourceTemplates.ParseGlob("./themes/" + theme.Name + "/public/*.css"))
|
||||
|
||||
// It should be safe for us to load the files for all the themes in memory, as-long as the admin hasn't setup a ridiculous number of themes
|
||||
err = AddThemeStaticFiles(theme)
|
||||
err = theme.LoadStaticFiles()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Themes[theme.Name] = theme
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddThemeStaticFiles(theme Theme) error {
|
||||
// TODO: It might be unsafe to call the template parsing functions with fsnotify, do something more concurrent
|
||||
func (theme *Theme) LoadStaticFiles() error {
|
||||
theme.ResourceTemplates = template.New("")
|
||||
template.Must(theme.ResourceTemplates.ParseGlob("./themes/" + theme.Name + "/public/*.css"))
|
||||
|
||||
// It should be safe for us to load the files for all the themes in memory, as-long as the admin hasn't setup a ridiculous number of themes
|
||||
return theme.AddThemeStaticFiles()
|
||||
}
|
||||
|
||||
func (theme *Theme) AddThemeStaticFiles() error {
|
||||
// TODO: Use a function instead of a closure to make this more testable? What about a function call inside the closure to take the theme variable into account?
|
||||
return filepath.Walk("./themes/"+theme.Name+"/public", func(path string, f os.FileInfo, err error) error {
|
||||
debugLog("Attempting to add static file '" + path + "' for default theme '" + theme.Name + "'")
|
||||
|
@ -230,14 +233,14 @@ func AddThemeStaticFiles(theme Theme) error {
|
|||
|
||||
path = strings.TrimPrefix(path, "themes/"+theme.Name+"/public")
|
||||
gzipData := compressBytesGzip(data)
|
||||
StaticFiles["/static/"+theme.Name+path] = SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
StaticFiles.Set("/static/"+theme.Name+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||
|
||||
debugLog("Added the '/" + theme.Name + path + "' static file for theme " + theme.Name + ".")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func MapThemeTemplates(theme Theme) {
|
||||
func (theme *Theme) MapTemplates() {
|
||||
if theme.Templates != nil {
|
||||
for _, themeTmpl := range theme.Templates {
|
||||
if themeTmpl.Name == "" {
|
||||
|
|
|
@ -22,9 +22,6 @@ go get -u gopkg.in/sourcemap.v1
|
|||
echo "Installing OttoJS"
|
||||
go get -u github.com/robertkrimen/otto
|
||||
|
||||
echo "Installing the Riot Search Engine"
|
||||
go get -u github.com/robertkrimen/otto
|
||||
|
||||
echo "Installing the Rez Image Resizer"
|
||||
go get -u github.com/bamiaux/rez
|
||||
|
||||
|
|
|
@ -71,13 +71,6 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Installing the Riot Search Engine
|
||||
go get -u github.com/go-ego/riot
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Installing the Rez Image Resizer
|
||||
go get -u github.com/bamiaux/rez
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
65
main.go
65
main.go
|
@ -13,10 +13,12 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
//"runtime/pprof"
|
||||
"./common"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
var version = common.Version{Major: 0, Minor: 1, Patch: 0, Tag: "dev"}
|
||||
|
@ -55,13 +57,11 @@ func afterDBInit() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Initialising the widgets")
|
||||
err = common.InitWidgets()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Initialising the authentication system")
|
||||
common.Auth, err = common.NewDefaultAuth()
|
||||
if err != nil {
|
||||
|
@ -72,12 +72,10 @@ func afterDBInit() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
common.ModLogs, err = common.NewModLogStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
common.AdminLogs, err = common.NewAdminLogStore()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -158,6 +156,65 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
log.Println("event:", event)
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
log.Println("modified file:", event.Name)
|
||||
var pathBits = strings.Split(event.Name, "\\")
|
||||
if len(pathBits) > 0 {
|
||||
if pathBits[0] == "themes" {
|
||||
var themeName string
|
||||
if len(pathBits) >= 1 {
|
||||
themeName = pathBits[1]
|
||||
}
|
||||
|
||||
if len(pathBits) >= 2 && pathBits[2] == "public" {
|
||||
// TODO: Handle new themes freshly plopped into the folder?
|
||||
theme, ok := common.Themes[themeName]
|
||||
if ok {
|
||||
err = theme.LoadStaticFiles()
|
||||
if err != nil {
|
||||
common.LogError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
log.Println("new file:", event.Name)
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
log.Println("error:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO: Keep tabs on the theme stuff, and the langpacks
|
||||
err = watcher.Add("./public")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = watcher.Add("./templates")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, theme := range common.Themes {
|
||||
err = watcher.Add("./themes/" + theme.Name + "/public")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Run this goroutine once a second
|
||||
secondTicker := time.NewTicker(1 * time.Second)
|
||||
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
|
||||
|
|
|
@ -42,7 +42,6 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User,
|
|||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
headerVars.Zone = "create_topic"
|
||||
headerVars.Writer = w
|
||||
|
||||
// Lock this to the forum being linked?
|
||||
// Should we always put it in strictmode when it's linked from another forum? Well, the user might end up changing their mind on what forum they want to post in and it would be a hassle, if they had to switch pages, even if it is a single click for many (exc. mobile)
|
||||
|
|
|
@ -1473,7 +1473,7 @@ func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
var pThemeList, vThemeList []common.Theme
|
||||
var pThemeList, vThemeList []*common.Theme
|
||||
for _, theme := range common.Themes {
|
||||
if theme.HideFromThemes {
|
||||
continue
|
||||
|
@ -1557,7 +1557,7 @@ func routePanelThemesSetDefault(w http.ResponseWriter, r *http.Request, user com
|
|||
|
||||
common.DefaultThemeBox.Store(uname)
|
||||
common.ResetTemplateOverrides()
|
||||
common.MapThemeTemplates(theme)
|
||||
theme.MapTemplates()
|
||||
common.ChangeDefaultThemeMutex.Unlock()
|
||||
|
||||
http.Redirect(w, r, "/panel/themes/", http.StatusSeeOther)
|
||||
|
|
22
routes.go
22
routes.go
|
@ -42,8 +42,7 @@ func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
// GET functions
|
||||
func routeStatic(w http.ResponseWriter, r *http.Request) {
|
||||
//log.Print("Outputting static file '" + r.URL.Path + "'")
|
||||
file, ok := common.StaticFiles[r.URL.Path]
|
||||
file, ok := common.StaticFiles.Get(r.URL.Path)
|
||||
if !ok {
|
||||
if common.Dev.DebugMode {
|
||||
log.Printf("Failed to find '%s'", r.URL.Path)
|
||||
|
@ -61,11 +60,8 @@ func routeStatic(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
h.Set("Last-Modified", file.FormattedModTime)
|
||||
h.Set("Content-Type", file.Mimetype)
|
||||
//Cache-Control: max-age=31536000
|
||||
h.Set("Cache-Control", cacheControlMaxAge)
|
||||
h.Set("Cache-Control", cacheControlMaxAge) //Cache-Control: max-age=31536000
|
||||
h.Set("Vary", "Accept-Encoding")
|
||||
//http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file)
|
||||
//w.Write(file.Data)
|
||||
if strings.Contains(h.Get("Accept-Encoding"), "gzip") {
|
||||
h.Set("Content-Encoding", "gzip")
|
||||
h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10))
|
||||
|
@ -74,15 +70,9 @@ func routeStatic(w http.ResponseWriter, r *http.Request) {
|
|||
h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time?
|
||||
io.Copy(w, bytes.NewReader(file.Data))
|
||||
}
|
||||
//io.CopyN(w, bytes.NewReader(file.Data), staticFiles[r.URL.Path].Length)
|
||||
// Other options instead of io.Copy: io.CopyN(), w.Write(), http.ServeContent()
|
||||
}
|
||||
|
||||
// Deprecated: Test route for stopping the server during a performance analysis
|
||||
/*func routeExit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError{
|
||||
db.Close()
|
||||
os.Exit(0)
|
||||
}*/
|
||||
|
||||
// TODO: Make this a static file somehow? Is it possible for us to put this file somewhere else?
|
||||
// TODO: Add a sitemap
|
||||
// TODO: Add an API so that plugins can register disallowed areas. E.g. /guilds/join for plugin_guilds
|
||||
|
@ -102,7 +92,6 @@ func routeOverview(w http.ResponseWriter, r *http.Request, user common.User) com
|
|||
return ferr
|
||||
}
|
||||
headerVars.Zone = "overview"
|
||||
headerVars.Writer = w
|
||||
|
||||
pi := common.Page{common.GetTitlePhrase("overview"), user, headerVars, tList, nil}
|
||||
if common.PreRenderHooks["pre_render_overview"] != nil {
|
||||
|
@ -129,7 +118,6 @@ func routeCustomPage(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
return common.NotFound(w, r)
|
||||
}
|
||||
headerVars.Zone = "custom_page"
|
||||
headerVars.Writer = w
|
||||
|
||||
pi := common.Page{common.GetTitlePhrase("page"), user, headerVars, tList, nil}
|
||||
if common.PreRenderHooks["pre_render_custom_page"] != nil {
|
||||
|
@ -151,7 +139,6 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
return ferr
|
||||
}
|
||||
headerVars.Zone = "topics"
|
||||
headerVars.Writer = w
|
||||
|
||||
// TODO: Add a function for the qlist stuff
|
||||
var qlist string
|
||||
|
@ -337,7 +324,6 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
|||
return common.InternalError(err, w, r)
|
||||
}
|
||||
headerVars.Zone = "view_forum"
|
||||
headerVars.Writer = w
|
||||
|
||||
// Calculate the offset
|
||||
var offset int
|
||||
|
@ -424,7 +410,6 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
return ferr
|
||||
}
|
||||
headerVars.Zone = "forums"
|
||||
headerVars.Writer = w
|
||||
|
||||
var err error
|
||||
var forumList []common.Forum
|
||||
|
@ -514,7 +499,6 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
|
|||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
headerVars.Zone = "view_topic"
|
||||
headerVars.Writer = w
|
||||
|
||||
topic.ContentHTML = common.ParseMessage(topic.Content, topic.ParentID, "forums")
|
||||
topic.ContentLines = strings.Count(topic.Content, "\n")
|
||||
|
|
|
@ -495,6 +495,12 @@ select, input, textarea, button {
|
|||
background-color: var(--element-background-color);
|
||||
padding: 12px;
|
||||
}
|
||||
/* TODO: Refactor bgavatars to make the avatars rounded */
|
||||
.rowlist.bgavatars .rowitem {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 40px;
|
||||
padding-left: 46px;
|
||||
}
|
||||
|
||||
.topic_list .rowtopic {
|
||||
font-size: 17px;
|
||||
|
@ -960,3 +966,40 @@ select, input, textarea, button {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
@media(max-width: 520px) {
|
||||
.topic_list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.topic_list .topic_row {
|
||||
display: block;
|
||||
width: calc(50% - 6px);
|
||||
float: left;
|
||||
}
|
||||
.topic_list .topic_row:nth-child(odd) {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.topic_left {
|
||||
margin-bottom: 0px;
|
||||
border-bottom: none;
|
||||
border-right: 1px solid var(--element-border-color);
|
||||
}
|
||||
.topic_left .parent_forum {
|
||||
display: none;
|
||||
}
|
||||
.topic_right.rowitem {
|
||||
border-top: none;
|
||||
border-left: 1px solid var(--element-border-color);
|
||||
background-color: hsl(0,0%,90%);
|
||||
}
|
||||
.topic_right br, .topic_right img {
|
||||
display: none;
|
||||
}
|
||||
.topic_right.topic_sticky {
|
||||
border-bottom: 2px solid var(--element-border-color);
|
||||
}
|
||||
.topic_right > span {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
"URL": "github.com/Azareal/Gosora",
|
||||
"Tag": "WIP",
|
||||
"Docks":["rightSidebar","footer"],
|
||||
"AboutSegment":true,
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
|
|
|
@ -22,9 +22,6 @@ go get -u gopkg.in/sourcemap.v1
|
|||
echo "Updating OttoJS"
|
||||
go get -u github.com/robertkrimen/otto
|
||||
|
||||
echo "Updating the Riot Search Engine"
|
||||
go get -u github.com/go-ego/riot
|
||||
|
||||
echo "Updating the Rez Image Resizer"
|
||||
go get -u github.com/bamiaux/rez
|
||||
|
||||
|
|
|
@ -68,13 +68,6 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Updating the Riot Search Engine
|
||||
go get -u github.com/go-ego/riot
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Updating the Rez Image Resizer
|
||||
go get -u github.com/bamiaux/rez
|
||||
if %errorlevel% neq 0 (
|
||||
|
|
Loading…
Reference in New Issue