diff --git a/cache.go b/cache.go index d716bd93..71b1ab45 100644 --- a/cache.go +++ b/cache.go @@ -11,9 +11,11 @@ var users UserStore var topics TopicStore type DataStore interface { + Load(id int) error Get(id int) (interface{}, error) GetUnsafe(id int) (interface{}, error) CascadeGet(id int) (interface{}, error) + Set(item interface{}) error Add(item interface{}) error AddUnsafe(item interface{}) error Remove(id int) error diff --git a/files.go b/files.go index 38ac39fd..0071419b 100644 --- a/files.go +++ b/files.go @@ -4,9 +4,9 @@ import "log" import "bytes" import "strings" import "mime" -import "errors" +//import "errors" import "os" -import "io" +//import "io" import "io/ioutil" import "path/filepath" import "net/http" @@ -23,7 +23,7 @@ type SFile struct FormattedModTime string } -func (r SFile) Read(b []byte) (n int, err error) { +/*func (r SFile) Read(b []byte) (n int, err error) { n = 0 if r.Pos > r.Length { return n, io.EOF @@ -58,7 +58,7 @@ func (r SFile) Seek(offset int64, whence int) (int64, error) { return 0, errors.New("invalid whence") } return r.Pos, nil -} +}*/ func add_static_file(path string, prefix string) error { data, err := ioutil.ReadFile(path) diff --git a/general_test.go b/general_test.go index bd85ba06..39eeb47a 100644 --- a/general_test.go +++ b/general_test.go @@ -1,5 +1,6 @@ package main import "os" +import "fmt" import "log" import "bytes" import "strconv" @@ -8,7 +9,6 @@ import "testing" import "net/http" import "net/http/httptest" import "io/ioutil" -import "html/template" import "database/sql" import _ "github.com/go-sql-driver/mysql" //import "github.com/husobee/vestigo" @@ -23,9 +23,25 @@ func gloinit() { //discard := ioutil.Discard //log.SetOutput(discard) + init_themes() var err error init_database(err) + init_templates() db.SetMaxOpenConns(64) + err = init_errors() + if err != nil { + log.Fatal(err) + } + + if cache_topicuser == CACHE_STATIC { + users = NewStaticUserStore(user_cache_capacity) + topics = NewStaticTopicStore(topic_cache_capacity) + } else { + users = NewSqlUserStore() + topics = NewSqlTopicStore() + } + + init_static_files() external_sites["YT"] = "https://www.youtube.com/" hooks["trow_assign"] = nil hooks["rrow_assign"] = nil @@ -33,26 +49,30 @@ func gloinit() { gloinited = true } -func BenchmarkTopicTemplate(b *testing.B) { +func init() { + gloinit() +} + +func BenchmarkTopicTemplateSerial(b *testing.B) { b.ReportAllocs() user := User{0,"Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","","",0,0,"127.0.0.1"} admin := User{1,"Admin","admin@localhost",0,true,true,true,true,true,false,AllPerms,"",false,"","","","","",-1,58,"127.0.0.1"} noticeList := []string{"test"} - topic := TopicUser{Title: "Lol",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"} + topic := TopicUser{Title: "Lol",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"} var replyList []Reply - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) + replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) tpage := TopicPage{"Topic Blah",user,noticeList,replyList,topic,1,1,false} tpage2 := TopicPage{"Topic Blah",admin,noticeList,replyList,topic,1,1,false} @@ -113,24 +133,24 @@ func BenchmarkTopicTemplate(b *testing.B) { defer pprof.StopCPUProfile()*/ } -func BenchmarkTopicsTemplate(b *testing.B) { +func BenchmarkTopicsTemplateSerial(b *testing.B) { b.ReportAllocs() user := User{0,"Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","","",0,0,"127.0.0.1"} admin := User{1,"Admin","admin@localhost",0,true,true,true,true,true,false,AllPerms,"",false,"","","","","",-1,58,"127.0.0.1"} noticeList := []string{"test"} - var topicList []TopicUser - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) - topicList = append(topicList, TopicUser{Title: "Hey everyone!",Content: template.HTML("Hey everyone!"),CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + var topicList []TopicsRow + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) + topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}) w := ioutil.Discard tpage := TopicsPage{"Topic Blah",user,noticeList,topicList,nil} @@ -163,23 +183,6 @@ func BenchmarkStaticRouteParallel(b *testing.B) { if !gloinited { gloinit() } - - b.RunParallel(func(pb *testing.PB) { - static_w := httptest.NewRecorder() - static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil)) - static_handler := http.HandlerFunc(route_static) - for pb.Next() { - static_w.Body.Reset() - static_handler.ServeHTTP(static_w,static_req) - } - }) -} - -/*func BenchmarkStaticRouteParallelWithPlugins(b *testing.B) { - b.ReportAllocs() - if !gloinited { - gloinit() - } if !plugins_inited { init_plugins() } @@ -189,11 +192,16 @@ func BenchmarkStaticRouteParallel(b *testing.B) { static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil)) static_handler := http.HandlerFunc(route_static) for pb.Next() { + //static_w.Code = 200 static_w.Body.Reset() static_handler.ServeHTTP(static_w,static_req) + //if static_w.Code != 200 { + // fmt.Println(static_w.Body) + // panic("HTTP Error!") + //} } }) -}*/ +} func BenchmarkTopicAdminRouteParallel(b *testing.B) { b.ReportAllocs() @@ -202,9 +210,15 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { - admin_uid_cookie := http.Cookie{Name: "uid",Value: "1",Path: "/",MaxAge: year} - // TO-DO: Stop hard-coding this value. Seriously. - admin_session_cookie := http.Cookie{Name: "session",Value: "TKBh5Z-qEQhWDBnV6_XVmOhKAowMYPhHeRlrQjjbNc0QRrRiglvWOYFDc1AaMXQIywvEsyA2AOBRYUrZ5kvnGhThY1GhOW6FSJADnRWm_bI=",Path: "/",MaxAge: year} + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year} topic_w := httptest.NewRecorder() topic_req := httptest.NewRequest("get","/topic/1",bytes.NewReader(nil)) @@ -214,7 +228,7 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) { topic_handler := http.HandlerFunc(route_topic_id) for pb.Next() { - //topic_w.Body.Reset() + topic_w.Body.Reset() topic_handler.ServeHTTP(topic_w,topic_req_admin) } }) @@ -245,9 +259,15 @@ func BenchmarkForumsAdminRouteParallel(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { - admin_uid_cookie := http.Cookie{Name: "uid",Value: "1",Path: "/",MaxAge: year} - // TO-DO: Stop hard-coding this value. Seriously. - admin_session_cookie := http.Cookie{Name: "session",Value: "TKBh5Z-qEQhWDBnV6_XVmOhKAowMYPhHeRlrQjjbNc0QRrRiglvWOYFDc1AaMXQIywvEsyA2AOBRYUrZ5kvnGhThY1GhOW6FSJADnRWm_bI=",Path: "/",MaxAge: year} + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year} forums_w := httptest.NewRecorder() forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil)) @@ -270,9 +290,15 @@ func BenchmarkForumsAdminRouteParallelProf(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { - admin_uid_cookie := http.Cookie{Name: "uid",Value: "1",Path: "/",MaxAge: year} - // TO-DO: Stop hard-coding this value. Seriously. - admin_session_cookie := http.Cookie{Name: "session",Value: "TKBh5Z-qEQhWDBnV6_XVmOhKAowMYPhHeRlrQjjbNc0QRrRiglvWOYFDc1AaMXQIywvEsyA2AOBRYUrZ5kvnGhThY1GhOW6FSJADnRWm_bI=",Path: "/",MaxAge: year} + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path: "/",MaxAge: year} forums_w := httptest.NewRecorder() forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil)) @@ -313,10 +339,16 @@ func BenchmarkForumsGuestRouteParallel(b *testing.B) { func BenchmarkRoutesSerial(b *testing.B) { b.ReportAllocs() + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } - admin_uid_cookie := http.Cookie{Name: "uid",Value: "1",Path: "/",MaxAge: year} - // TO-DO: Stop hard-coding this value. Seriously. - admin_session_cookie := http.Cookie{Name: "session",Value: "TKBh5Z-qEQhWDBnV6_XVmOhKAowMYPhHeRlrQjjbNc0QRrRiglvWOYFDc1AaMXQIywvEsyA2AOBRYUrZ5kvnGhThY1GhOW6FSJADnRWm_bI=",Path: "/",MaxAge: year} + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path: "/",MaxAge: year} if plugins_inited { b.Log("Plugins have already been initialised, they can't be deinitialised so these tests will run with plugins on") @@ -367,8 +399,13 @@ func BenchmarkRoutesSerial(b *testing.B) { b.Run("static_recorder", func(b *testing.B) { for i := 0; i < b.N; i++ { + //static_w.Code = 200 static_w.Body.Reset() static_handler.ServeHTTP(static_w,static_req) + //if static_w.Code != 200 { + // fmt.Println(static_w.Body) + // panic("HTTP Error!") + //} } }) @@ -542,12 +579,9 @@ func BenchmarkQueryTopicParallel(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { - topic := TopicUser{Css: no_css_tmpl} - var content string - var is_super_admin bool - var group int + tu := TopicUser{Css: no_css_tmpl} for pb.Next() { - err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, users.name, users.avatar, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, topics.ipaddress from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.CreatedByName, &topic.Avatar, &is_super_admin, &group, &topic.URLPrefix, &topic.URLName, &topic.Level, &topic.IpAddress) + err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.Is_Closed, &tu.Sticky, &tu.ParentID, &tu.IpAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) if err == sql.ErrNoRows { log.Fatal("No rows found!") return @@ -566,12 +600,9 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { - topic := TopicUser{Css: no_css_tmpl} - var content string - var is_super_admin bool - var group int + tu := TopicUser{Css: no_css_tmpl} for pb.Next() { - err := get_topic_user_stmt.QueryRow(1).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.CreatedByName, &topic.Avatar, &is_super_admin, &group, &topic.URLPrefix, &topic.URLName, &topic.Level, &topic.IpAddress) + err := get_topic_user_stmt.QueryRow(1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.Is_Closed, &tu.Sticky, &tu.ParentID, &tu.IpAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) if err == sql.ErrNoRows { log.Fatal("No rows found!") return @@ -585,13 +616,10 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) { func BenchmarkQueriesSerial(b *testing.B) { b.ReportAllocs() - topic := TopicUser{Css: no_css_tmpl} - var content string - var is_super_admin bool - var group int + tu := TopicUser{Css: no_css_tmpl} b.Run("topic", func(b *testing.B) { for i := 0; i < b.N; i++ { - err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, users.name, users.avatar, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, topics.ipaddress from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.CreatedByName, &topic.Avatar, &is_super_admin, &group, &topic.URLPrefix, &topic.URLName, &topic.Level, &topic.IpAddress) + err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.Is_Closed, &tu.Sticky, &tu.ParentID, &tu.IpAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) if err == sql.ErrNoRows { log.Fatal("No rows found!") return @@ -617,7 +645,10 @@ func BenchmarkQueriesSerial(b *testing.B) { defer rows.Close() } }) + replyItem := Reply{Css: no_css_tmpl} + var is_super_admin bool + var group int b.Run("topic_replies_scan", func(b *testing.B) { for i := 0; i < b.N; i++ { rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1) @@ -648,7 +679,7 @@ func addEmptyRoutesToMux(routes []string, serveMux *http.ServeMux) { } } -func BenchmarkDefaultGoRouter(b *testing.B) { +func BenchmarkDefaultGoRouterSerial(b *testing.B) { w := httptest.NewRecorder() req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil)) routes := make([]string, 0) @@ -805,176 +836,13 @@ func BenchmarkDefaultGoRouter(b *testing.B) { }) } -/*func addEmptyRoutesToVestigo(routes []string, router *vestigo.Router) { - for _, route := range routes { - router.HandleFunc(route, func(_ http.ResponseWriter,_ *http.Request){}) - } -} - -func BenchmarkVestigoRouter(b *testing.B) { - w := httptest.NewRecorder() - req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil)) - routes := make([]string, 0) - - routes = append(routes,"/test/") - router := vestigo.NewRouter() - router.HandleFunc("/test/", func(_ http.ResponseWriter,_ *http.Request){}) - b.Run("one-route", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - routes = append(routes,"/topic/") - routes = append(routes,"/forums/") - routes = append(routes,"/forum/") - routes = append(routes,"/panel/") - router = vestigo.NewRouter() - addEmptyRoutesToVestigo(routes, router) - b.Run("five-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/panel/plugins/") - routes = append(routes,"/panel/groups/") - routes = append(routes,"/panel/settings/") - routes = append(routes,"/panel/users/") - routes = append(routes,"/panel/forums/") - addEmptyRoutesToVestigo(routes, router) - b.Run("ten-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/panel/forums/create/submit/") - routes = append(routes,"/panel/forums/delete/") - routes = append(routes,"/users/ban/") - routes = append(routes,"/panel/users/edit/") - routes = append(routes,"/panel/forums/create/") - routes = append(routes,"/users/unban/") - routes = append(routes,"/pages/") - routes = append(routes,"/users/activate/") - routes = append(routes,"/panel/forums/edit/submit/") - routes = append(routes,"/panel/plugins/activate/") - addEmptyRoutesToVestigo(routes, router) - b.Run("twenty-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/panel/plugins/deactivate/") - routes = append(routes,"/panel/plugins/install/") - routes = append(routes,"/panel/plugins/uninstall/") - routes = append(routes,"/panel/templates/") - routes = append(routes,"/panel/templates/edit/") - routes = append(routes,"/panel/templates/create/") - routes = append(routes,"/panel/templates/delete/") - routes = append(routes,"/panel/templates/edit/submit/") - routes = append(routes,"/panel/themes/") - routes = append(routes,"/panel/themes/edit/") - addEmptyRoutesToVestigo(routes, router) - b.Run("thirty-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/panel/themes/create/") - routes = append(routes,"/panel/themes/delete/") - routes = append(routes,"/panel/themes/delete/submit/") - routes = append(routes,"/panel/templates/create/submit/") - routes = append(routes,"/panel/templates/delete/submit/") - routes = append(routes,"/panel/widgets/") - routes = append(routes,"/panel/widgets/edit/") - routes = append(routes,"/panel/widgets/activate/") - routes = append(routes,"/panel/widgets/deactivate/") - routes = append(routes,"/panel/magical/wombat/path") - addEmptyRoutesToVestigo(routes, router) - b.Run("forty-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/report/") - routes = append(routes,"/report/submit/") - routes = append(routes,"/topic/create/submit/") - routes = append(routes,"/topics/create/") - routes = append(routes,"/overview/") - routes = append(routes,"/uploads/") - routes = append(routes,"/static/") - routes = append(routes,"/reply/edit/submit/") - routes = append(routes,"/reply/delete/submit/") - routes = append(routes,"/topic/edit/submit/") - addEmptyRoutesToVestigo(routes, router) - b.Run("fifty-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/topic/delete/submit/") - routes = append(routes,"/topic/stick/submit/") - routes = append(routes,"/topic/unstick/submit/") - routes = append(routes,"/accounts/login/") - routes = append(routes,"/accounts/create/") - routes = append(routes,"/accounts/logout/") - routes = append(routes,"/accounts/login/submit/") - routes = append(routes,"/accounts/create/submit/") - routes = append(routes,"/user/edit/critical/") - routes = append(routes,"/user/edit/critical/submit/") - addEmptyRoutesToVestigo(routes, router) - b.Run("sixty-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) - - router = vestigo.NewRouter() - routes = append(routes,"/user/edit/avatar/") - routes = append(routes,"/user/edit/avatar/submit/") - routes = append(routes,"/user/edit/username/") - routes = append(routes,"/user/edit/username/submit/") - routes = append(routes,"/profile/reply/create/") - routes = append(routes,"/profile/reply/edit/submit/") - routes = append(routes,"/profile/reply/delete/submit/") - routes = append(routes,"/arcane/tower/") - routes = append(routes,"/magical/kingdom/") - routes = append(routes,"/insert/name/here/") - addEmptyRoutesToVestigo(routes, router) - b.Run("seventy-routes", func(b *testing.B) { - for i := 0; i < b.N; i++ { - req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil)) - router.ServeHTTP(w,req) - } - }) -}*/ - func addEmptyRoutesToCustom(routes []string, router *Router) { for _, route := range routes { router.HandleFunc(route, func(_ http.ResponseWriter,_ *http.Request){}) } } -func BenchmarkCustomRouter(b *testing.B) { +func BenchmarkCustomRouterSerial(b *testing.B) { w := httptest.NewRecorder() req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil)) routes := make([]string, 0) @@ -1131,7 +999,7 @@ func BenchmarkCustomRouter(b *testing.B) { }) } -func BenchmarkParser(b *testing.B) { +func BenchmarkParserSerial(b *testing.B) { b.ReportAllocs() b.Run("empty_post", func(b *testing.B) { for i := 0; i < b.N; i++ { @@ -1165,7 +1033,7 @@ func BenchmarkParser(b *testing.B) { }) } -func BenchmarkBBCodePluginWithRegexp(b *testing.B) { +func BenchmarkBBCodePluginWithRegexpSerial(b *testing.B) { b.ReportAllocs() b.Run("empty_post", func(b *testing.B) { for i := 0; i < b.N; i++ { @@ -1214,7 +1082,7 @@ func BenchmarkBBCodePluginWithRegexp(b *testing.B) { }) } -func BenchmarkBBCodePluginWithoutCodeTag(b *testing.B) { +func BenchmarkBBCodePluginWithoutCodeTagSerial(b *testing.B) { b.ReportAllocs() b.Run("empty_post", func(b *testing.B) { for i := 0; i < b.N; i++ { @@ -1263,7 +1131,7 @@ func BenchmarkBBCodePluginWithoutCodeTag(b *testing.B) { }) } -func BenchmarkBBCodePluginWithFullParser(b *testing.B) { +func BenchmarkBBCodePluginWithFullParserSerial(b *testing.B) { b.ReportAllocs() b.Run("empty_post", func(b *testing.B) { for i := 0; i < b.N; i++ { @@ -1320,6 +1188,186 @@ func TestLevels(t *testing.T) { } } +func TestStaticRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + static_w := httptest.NewRecorder() + static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil)) + static_handler := http.HandlerFunc(route_static) + + static_handler.ServeHTTP(static_w,static_req) + if static_w.Code != 200 { + fmt.Println(static_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the static route!") +} + +func TestTopicAdminRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } + + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year} + + topic_w := httptest.NewRecorder() + topic_req := httptest.NewRequest("get","/topic/1",bytes.NewReader(nil)) + topic_req_admin := topic_req + topic_req_admin.AddCookie(&admin_uid_cookie) + topic_req_admin.AddCookie(&admin_session_cookie) + topic_handler := http.HandlerFunc(route_topic_id) + + topic_handler.ServeHTTP(topic_w,topic_req_admin) + if topic_w.Code != 200 { + fmt.Println(topic_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the topic-admin route!") +} + +func TestTopicGuestRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + topic_w := httptest.NewRecorder() + topic_req := httptest.NewRequest("get","/topic/1",bytes.NewReader(nil)) + topic_handler := http.HandlerFunc(route_topic_id) + + topic_handler.ServeHTTP(topic_w,topic_req) + if topic_w.Code != 200 { + fmt.Println(topic_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the topic-guest route!") +} + +func TestForumsAdminRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year} + + forums_w := httptest.NewRecorder() + forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil)) + forums_req_admin := forums_req + forums_req_admin.AddCookie(&admin_uid_cookie) + forums_req_admin.AddCookie(&admin_session_cookie) + forums_handler := http.HandlerFunc(route_forums) + + forums_handler.ServeHTTP(forums_w,forums_req_admin) + if forums_w.Code != 200 { + fmt.Println(forums_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the forums-admin route!") +} + +func TestForumsGuestRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + forums_w := httptest.NewRecorder() + forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil)) + forums_handler := http.HandlerFunc(route_forums) + + forums_handler.ServeHTTP(forums_w,forums_req) + if forums_w.Code != 200 { + fmt.Println(forums_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the forums-guest route!") +} + +func TestForumAdminRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + admin, err := users.CascadeGet(1) + if err != nil { + panic(err) + } + if !admin.Is_Admin { + panic("UID1 is not an admin") + } + admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year} + admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year} + + forum_w := httptest.NewRecorder() + forum_req := httptest.NewRequest("get","/forum/2",bytes.NewReader(nil)) + forum_req_admin := forum_req + forum_req_admin.AddCookie(&admin_uid_cookie) + forum_req_admin.AddCookie(&admin_session_cookie) + forum_handler := http.HandlerFunc(route_forum) + + forum_handler.ServeHTTP(forum_w,forum_req_admin) + if forum_w.Code != 200 { + fmt.Println(forum_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the forum-admin route!") +} + +func TestForumGuestRoute(t *testing.T) { + if !gloinited { + gloinit() + } + if !plugins_inited { + init_plugins() + } + + forum_w := httptest.NewRecorder() + forum_req := httptest.NewRequest("get","/forum/2",bytes.NewReader(nil)) + forum_handler := http.HandlerFunc(route_forum) + + forum_handler.ServeHTTP(forum_w,forum_req) + if forum_w.Code != 200 { + fmt.Println(forum_w.Body) + panic("HTTP Error!") + } + fmt.Println("No problems found in the forum-guest route!") +} + /*func TestRoute(t *testing.T) { }*/ \ No newline at end of file diff --git a/images/postcache-tests-and-bench.PNG b/images/postcache-tests-and-bench.PNG new file mode 100644 index 00000000..f89a93eb Binary files /dev/null and b/images/postcache-tests-and-bench.PNG differ diff --git a/main.go b/main.go index 87896959..d1da4da6 100644 --- a/main.go +++ b/main.go @@ -123,6 +123,29 @@ func init_templates() { template.Must(templates.ParseGlob("pages/*")) } +func init_static_files() { + log.Print("Loading the static files.") + err := filepath.Walk("./public", func(path string, f os.FileInfo, err error) error { + if f.IsDir() { + return nil + } + + path = strings.Replace(path,"\\","/",-1) + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + path = strings.TrimPrefix(path,"public/") + log.Print("Added the '" + path + "' static file.") + static_files["/static/" + path] = SFile{data,compress_bytes_gzip(data),0,int64(len(data)),mime.TypeByExtension(filepath.Ext("/public/" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)} + return nil + }) + if err != nil { + log.Fatal(err) + } +} + func main(){ //if profiling { // f, err := os.Create("startup_cpu.prof") @@ -151,27 +174,7 @@ func main(){ topics = NewSqlTopicStore() } - log.Print("Loading the static files.") - err = filepath.Walk("./public", func(path string, f os.FileInfo, err error) error { - if f.IsDir() { - return nil - } - - path = strings.Replace(path,"\\","/",-1) - data, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - path = strings.TrimPrefix(path,"public/") - log.Print("Added the '" + path + "' static file.") - static_files["/static/" + path] = SFile{data,compress_bytes_gzip(data),0,int64(len(data)),mime.TypeByExtension(filepath.Ext("/public/" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)} - return nil - }) - if err != nil { - log.Fatal(err) - } - + init_static_files() external_sites["YT"] = "https://www.youtube.com/" hooks["trow_assign"] = nil hooks["rrow_assign"] = nil diff --git a/mod_routes.go b/mod_routes.go index d6feb1f4..93518f6f 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -57,6 +57,12 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) { return } + err = topics.Load(tid) + if err != nil { + LocalError("This topic no longer exists!",w,r,user) + return + } + if is_js == "0" { http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther) } else { @@ -114,6 +120,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) { } forums[fid].TopicCount -= 1 + topics.Remove(tid) } func route_stick_topic(w http.ResponseWriter, r *http.Request) { @@ -123,8 +130,7 @@ func route_stick_topic(w http.ResponseWriter, r *http.Request) { return } - var fid int - err = db.QueryRow("select parentID from topics where tid = ?", tid).Scan(&fid) + topic, err := topics.CascadeGet(tid) if err == sql.ErrNoRows { PreError("The topic you tried to pin doesn't exist.",w,r) return @@ -133,7 +139,7 @@ func route_stick_topic(w http.ResponseWriter, r *http.Request) { return } - user, ok := SimpleForumSessionCheck(w,r,fid) + user, ok := SimpleForumSessionCheck(w,r,topic.ParentID) if !ok { return } @@ -147,6 +153,8 @@ func route_stick_topic(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } + + topic.Sticky = true http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther) } @@ -157,8 +165,7 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request) { return } - var fid int - err = db.QueryRow("select parentID from topics where tid = ?", tid).Scan(&fid) + topic, err := topics.CascadeGet(tid) if err == sql.ErrNoRows { PreError("The topic you tried to unpin doesn't exist.",w,r) return @@ -167,7 +174,7 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request) { return } - user, ok := SimpleForumSessionCheck(w,r,fid) + user, ok := SimpleForumSessionCheck(w,r,topic.ParentID) if !ok { return } @@ -181,6 +188,8 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } + + topic.Sticky = false http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther) } @@ -312,6 +321,12 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request) { if err != nil { InternalErrorJSQ(err,w,r,is_js) } + + err = topics.Load(tid) + if err != nil { + LocalError("This topic no longer exists!",w,r,user) + return + } } func route_profile_reply_edit_submit(w http.ResponseWriter, r *http.Request) { @@ -374,7 +389,6 @@ func route_profile_reply_delete_submit(w http.ResponseWriter, r *http.Request) { LocalError("Bad Form",w,r,user) return } - is_js := r.PostFormValue("is_js") if is_js == "" { is_js = "0" @@ -387,7 +401,7 @@ func route_profile_reply_delete_submit(w http.ResponseWriter, r *http.Request) { } var uid int - err = db.QueryRow("SELECT uid from users_replies where rid = ?", rid).Scan(&uid) + err = db.QueryRow("select uid from users_replies where rid = ?", rid).Scan(&uid) if err == sql.ErrNoRows { LocalErrorJSQ("The reply you tried to delete doesn't exist.",w,r,user,is_js) return @@ -502,6 +516,12 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } + + err = users.Load(uid) + if err != nil { + LocalError("This user no longer exists!",w,r,user) + return + } http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther) } @@ -546,6 +566,12 @@ func route_unban(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } + + err = users.Load(uid) + if err != nil { + LocalError("This user no longer exists!",w,r,user) + return + } http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther) } @@ -595,6 +621,12 @@ func route_activate(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } + + err = users.Load(uid) + if err != nil { + LocalError("This user no longer exists!",w,r,user) + return + } http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther) } @@ -973,7 +1005,7 @@ func route_panel_setting_edit(w http.ResponseWriter, r *http.Request) { sname := r.URL.Path[len("/panel/settings/edit/submit/"):] scontent := r.PostFormValue("setting-value") - err = db.QueryRow("SELECT name, type, constraints from settings where name = ?", sname).Scan(&sname, &stype, &sconstraints) + err = db.QueryRow("select name, type, constraints from settings where name = ?", sname).Scan(&sname, &stype, &sconstraints) if err == sql.ErrNoRows { LocalError("The setting you want to edit doesn't exist.",w,r,user) return @@ -1208,15 +1240,13 @@ func route_panel_users_edit(w http.ResponseWriter, r *http.Request){ return } - var err error - targetUser := User{ID: 0,} - targetUser.ID, err = strconv.Atoi(r.URL.Path[len("/panel/users/edit/"):]) + uid, err := strconv.Atoi(r.URL.Path[len("/panel/users/edit/"):]) if err != nil { LocalError("The provided User ID is not a valid number.",w,r,user) return } - err = db.QueryRow("select `name`,`email`,`group` from `users` where `uid` = ?", targetUser.ID).Scan(&targetUser.Name,&targetUser.Email,&targetUser.Group) + targetUser, err := users.Get(uid) if err == sql.ErrNoRows { LocalError("The user you're trying to edit doesn't exist.",w,r,user) return @@ -1225,8 +1255,6 @@ func route_panel_users_edit(w http.ResponseWriter, r *http.Request){ return } - targetUser.Is_Admin = targetUser.Is_Super_Admin || groups[targetUser.Group].Is_Admin - targetUser.Is_Super_Mod = groups[targetUser.Group].Is_Mod || targetUser.Is_Admin if targetUser.Is_Admin && !user.Is_Admin { LocalError("Only administrators can edit the account of an administrator.",w,r,user) return @@ -1264,15 +1292,13 @@ func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request){ return } - var err error - targetUser := User{ID: 0,} - targetUser.ID, err = strconv.Atoi(r.URL.Path[len("/panel/users/edit/submit/"):]) + tid, err := strconv.Atoi(r.URL.Path[len("/panel/users/edit/submit/"):]) if err != nil { LocalError("The provided User ID is not a valid number.",w,r,user) return } - err = db.QueryRow("select `name`,`email`,`group` from `users` where `uid` = ?", targetUser.ID).Scan(&targetUser.Name, &targetUser.Email, &targetUser.Group) + targetUser, err := users.Get(tid) if err == sql.ErrNoRows { LocalError("The user you're trying to edit doesn't exist.",w,r,user) return @@ -1281,8 +1307,6 @@ func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request){ return } - targetUser.Is_Admin = targetUser.Is_Super_Admin || groups[targetUser.Group].Is_Admin - targetUser.Is_Super_Mod = groups[targetUser.Group].Is_Mod || targetUser.Is_Admin if targetUser.Is_Admin && !user.Is_Admin { LocalError("Only administrators can edit the account of an administrator.",w,r,user) return @@ -1340,6 +1364,12 @@ func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request){ SetPassword(targetUser.ID, newpassword) } + err = users.Load(targetUser.ID) + if err != nil { + LocalError("This user no longer exists!",w,r,user) + return + } + http.Redirect(w,r,"/panel/users/edit/" + strconv.Itoa(targetUser.ID),http.StatusSeeOther) } diff --git a/mysql.go b/mysql.go index 17bb2b45..a0ab9e60 100644 --- a/mysql.go +++ b/mysql.go @@ -9,6 +9,8 @@ import "strconv" import "encoding/json" var db *sql.DB +var db_version string + var get_user_stmt *sql.Stmt var get_full_user_stmt *sql.Stmt var get_topic_list_stmt *sql.Stmt @@ -92,6 +94,9 @@ func init_database(err error) { log.Fatal(err) } + // Getting the database version.. + db.QueryRow("SELECT VERSION()").Scan(&db_version) + /*log.Print("Preparing get_session statement.") get_session_stmt, err = db.Prepare("select `uid`,`name`,`group`,`is_super_admin`,`session`,`email`,`avatar`,`message`,`url_prefix`,`url_name`,`level`,`score`,`last_ip` from `users` where `uid` = ? and `session` = ? AND `session` <> ''") if err != nil { diff --git a/old-images/Bench-13-01-2017-doubled-gosoras-speed.PNG b/old-images/Bench-13-01-2017-doubled-gosoras-speed.PNG new file mode 100644 index 00000000..9001831c Binary files /dev/null and b/old-images/Bench-13-01-2017-doubled-gosoras-speed.PNG differ diff --git a/images/bench_parser_with_regexp.PNG b/old-images/bench_parser_with_regexp.PNG similarity index 100% rename from images/bench_parser_with_regexp.PNG rename to old-images/bench_parser_with_regexp.PNG diff --git a/images/bench_round1.PNG b/old-images/bench_round1.PNG similarity index 100% rename from images/bench_round1.PNG rename to old-images/bench_round1.PNG diff --git a/images/bench_round1_with_memory_allocs.PNG b/old-images/bench_round1_with_memory_allocs.PNG similarity index 100% rename from images/bench_round1_with_memory_allocs.PNG rename to old-images/bench_round1_with_memory_allocs.PNG diff --git a/images/bench_round1_with_router_bench.PNG b/old-images/bench_round1_with_router_bench.PNG similarity index 100% rename from images/bench_round1_with_router_bench.PNG rename to old-images/bench_round1_with_router_bench.PNG diff --git a/images/bench_round2.PNG b/old-images/bench_round2.PNG similarity index 100% rename from images/bench_round2.PNG rename to old-images/bench_round2.PNG diff --git a/images/bench_round3.PNG b/old-images/bench_round3.PNG similarity index 100% rename from images/bench_round3.PNG rename to old-images/bench_round3.PNG diff --git a/images/bench_round3_staticfileimprovements.PNG b/old-images/bench_round3_staticfileimprovements.PNG similarity index 100% rename from images/bench_round3_staticfileimprovements.PNG rename to old-images/bench_round3_staticfileimprovements.PNG diff --git a/images/bench_round4.PNG b/old-images/bench_round4.PNG similarity index 100% rename from images/bench_round4.PNG rename to old-images/bench_round4.PNG diff --git a/images/bench_topic_route.PNG b/old-images/bench_topic_route.PNG similarity index 100% rename from images/bench_topic_route.PNG rename to old-images/bench_topic_route.PNG diff --git a/images/bench_vestigo_router.PNG b/old-images/bench_vestigo_router.PNG similarity index 100% rename from images/bench_vestigo_router.PNG rename to old-images/bench_vestigo_router.PNG diff --git a/permissions.go b/permissions.go index 6801772d..69360475 100644 --- a/permissions.go +++ b/permissions.go @@ -153,6 +153,8 @@ func init() { ExtData: make(map[string]bool), } + guest_user.Perms = GuestPerms + if debug { fmt.Printf("Guest Perms: ") fmt.Printf("%+v\n", GuestPerms) diff --git a/router.go b/router.go index b48f3472..3690137d 100644 --- a/router.go +++ b/router.go @@ -28,17 +28,13 @@ func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter } func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { - router.mu.RLock() - if req.URL.Path[0] != '/' { - router.mu.RUnlock() w.WriteHeader(405) w.Write([]byte("")) return } - // Do something on the path to turn slashes facing the wrong way "\" into "/" slashes. Like what? Wouldn't that be slow? Might need to move to fasthttp for this - + router.mu.RLock() handle, ok := router.routes[req.URL.Path] if ok { router.mu.RUnlock() diff --git a/routes.go b/routes.go index 342ee083..7c63b3aa 100644 --- a/routes.go +++ b/routes.go @@ -113,7 +113,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){ return } - topicItem := TopicsRow{ID: 0,} + topicItem := TopicsRow{ID: 0} for rows.Next() { err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.ParentID, &topicItem.LikeCount, &topicItem.CreatedByName, &topicItem.Avatar) if err != nil { @@ -316,33 +316,25 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ return } if !user.Perms.ViewTopic { + //fmt.Printf("%+v\n", user.Perms) NoPermissions(w,r,user) return } - topic.ContentLines = strings.Count(topic.Content,"\n") topic.Content = parse_message(topic.Content) + topic.ContentLines = strings.Count(topic.Content,"\n") // We don't want users posting in locked topics... if topic.Is_Closed && !user.Is_Mod { user.Perms.CreateReply = false } - if topic.Avatar != "" { - if topic.Avatar[0] == '.' { - topic.Avatar = "/uploads/avatar_" + strconv.Itoa(topic.CreatedBy) + topic.Avatar - } - } else { - topic.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topic.CreatedBy),1) - } - if groups[topic.Group].Is_Mod || groups[topic.Group].Is_Admin { + if groups[topic.Group].Is_Mod { topic.Css = staff_css_tmpl topic.Level = -1 } - topic.Tag = groups[topic.Group].Tag - - if settings["url_tags"] == false { + /*if settings["url_tags"] == false { topic.URLName = "" } else { topic.URL, ok = external_sites[topic.URLPrefix] @@ -351,7 +343,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ } else { topic.URL = topic.URL + topic.URLName } - } + }*/ // Calculate the offset last_page := int(topic.PostCount / items_per_page) + 1 @@ -415,7 +407,6 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ replyItem.URL = replyItem.URL + replyItem.URLName } }*/ - replyItem.Liked = false if hooks["rrow_assign"] != nil { @@ -465,19 +456,19 @@ func route_profile(w http.ResponseWriter, r *http.Request){ replyList []Reply ) - puser := User{ID: 0,} - puser.ID, err = strconv.Atoi(r.URL.Path[len("/user/"):]) + pid, err := strconv.Atoi(r.URL.Path[len("/user/"):]) if err != nil { - LocalError("The provided TopicID is not a valid number.",w,r,user) + LocalError("The provided User ID is not a valid number.",w,r,user) return } - if puser.ID == user.ID { + var puser *User + if pid == user.ID { user.Is_Mod = true - puser = user + puser = &user } else { // Fetch the user data - err = get_user_stmt.QueryRow(puser.ID).Scan(&puser.Name, &puser.Group, &puser.Is_Super_Admin, &puser.Avatar, &puser.Message, &puser.URLPrefix, &puser.URLName, &puser.Level) + puser, err = users.CascadeGet(pid) if err == sql.ErrNoRows { NotFound(w,r) return @@ -485,24 +476,6 @@ func route_profile(w http.ResponseWriter, r *http.Request){ InternalError(err,w,r) return } - - puser.Is_Admin = puser.Is_Super_Admin || groups[puser.Group].Is_Admin - puser.Is_Super_Mod = puser.Is_Admin || groups[puser.Group].Is_Mod - puser.Is_Mod = puser.Is_Super_Mod - puser.Is_Banned = groups[puser.Group].Is_Banned - if puser.Is_Banned && puser.Is_Super_Mod { - puser.Is_Banned = false - } - } - - puser.Tag = groups[puser.Group].Tag - - if puser.Avatar != "" { - if puser.Avatar[0] == '.' { - puser.Avatar = "/uploads/avatar_" + strconv.Itoa(puser.ID) + puser.Avatar - } - } else { - puser.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(puser.ID),1) } // Get the replies.. @@ -553,7 +526,7 @@ func route_profile(w http.ResponseWriter, r *http.Request){ return } - ppage := ProfilePage{puser.Name + "'s Profile",user,noticeList,replyList,puser,false} + ppage := ProfilePage{puser.Name + "'s Profile",user,noticeList,replyList,*puser,false} if template_profile_handle != nil { template_profile_handle(ppage,w) } else { @@ -922,7 +895,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { - LocalError("Bad Form", w, r, user) + LocalError("Bad Form",w,r,user) return } if r.FormValue("session") != user.Session { @@ -1213,6 +1186,11 @@ func route_account_own_edit_avatar_submit(w http.ResponseWriter, r *http.Request return } user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext + err = users.Load(user.ID) + if err != nil { + LocalError("This user no longer exists!",w,r,user) + return + } noticeList = append(noticeList, "Your avatar was successfully updated") pi := Page{"Edit Avatar",user,noticeList,tList,nil} @@ -1253,7 +1231,13 @@ func route_account_own_edit_username_submit(w http.ResponseWriter, r *http.Reque LocalError("Unable to change the username. Does someone else already have this name?",w,r,user) return } + user.Name = new_username + err = users.Load(user.ID) + if err != nil { + LocalError("Your account doesn't exist!",w,r,user) + return + } noticeList = append(noticeList,"Your username was successfully updated") pi := Page{"Edit Username",user,noticeList,tList,nil} @@ -1305,7 +1289,7 @@ func route_account_own_edit_email(w http.ResponseWriter, r *http.Request) { if !enable_emails { noticeList = append(noticeList, "The email system has been turned off. All features involving sending emails have been disabled.") } - pi := Page{"Email Manager",user,noticeList,emailList,0} + pi := Page{"Email Manager",user,noticeList,emailList,nil} templates.ExecuteTemplate(w,"account-own-edit-email.html", pi) } @@ -1376,7 +1360,7 @@ func route_account_own_edit_email_token_submit(w http.ResponseWriter, r *http.Re noticeList = append(noticeList,"The email system has been turned off. All features involving sending emails have been disabled.") } noticeList = append(noticeList,"Your email was successfully verified") - pi := Page{"Email Manager",user,noticeList,emailList,0} + pi := Page{"Email Manager",user,noticeList,emailList,nil} templates.ExecuteTemplate(w,"account-own-edit-email.html", pi) } @@ -1395,6 +1379,12 @@ func route_logout(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } + + err = users.Load(user.ID) + if err != nil { + LocalError("Your account doesn't exist!",w,r,user) + return + } http.Redirect(w,r, "/", http.StatusSeeOther) } @@ -1408,7 +1398,7 @@ func route_login(w http.ResponseWriter, r *http.Request) { return } pi := Page{"Login",user,noticeList,tList,nil} - templates.ExecuteTemplate(w,"login.html", pi) + templates.ExecuteTemplate(w,"login.html",pi) } func route_login_submit(w http.ResponseWriter, r *http.Request) { diff --git a/template_forum.go b/template_forum.go index 55a5e609..a9f00d89 100644 --- a/template_forum.go +++ b/template_forum.go @@ -1,7 +1,7 @@ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main -import "io" import "strconv" +import "io" func init() { template_forum_handle = template_forum diff --git a/template_forums.go b/template_forums.go index d912d703..4f0e2372 100644 --- a/template_forums.go +++ b/template_forums.go @@ -1,7 +1,7 @@ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main -import "strconv" import "io" +import "strconv" func init() { template_forums_handle = template_forums diff --git a/template_topic.go b/template_topic.go index 81b8b7a6..9acc93b7 100644 --- a/template_topic.go +++ b/template_topic.go @@ -1,7 +1,7 @@ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main -import "strconv" import "io" +import "strconv" func init() { template_topic_handle = template_topic diff --git a/templates.go b/templates.go index a5d68774..7e61540b 100644 --- a/templates.go +++ b/templates.go @@ -871,6 +871,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle } func (c *CTemplateSet) compile_command(*parse.CommandNode) (out string) { + panic("Uh oh! Something went wrong!") return "" } diff --git a/templates_old.go b/templates_old.go deleted file mode 100644 index 50bdbd0a..00000000 --- a/templates_old.go +++ /dev/null @@ -1,657 +0,0 @@ -package main -import "log" -import "fmt" -import "strings" -import "reflect" -import "path/filepath" -import "io/ioutil" -import "text/template/parse" - -/* Keeping this so I can benchmark against it */ - -type CTemplateSet2 struct -{ - tlist map[string]*parse.Tree - dir string - funcMap map[string]interface{} - importMap map[string]string - varList map[string]VarItem - localVars map[string]map[string]VarItemReflect - //tempVars map[string]string - doImports bool - expectsInt interface{} -} - -func (c *CTemplateSet2) compile_template_2(name string, dir string, expects string, expectsInt interface{}, varList map[string]VarItem) (out string) { - c.dir = dir - c.doImports = true - c.funcMap = make(map[string]interface{}) - c.funcMap["and"] = "&&" - c.funcMap["not"] = "!" - c.funcMap["or"] = "||" - c.funcMap["eq"] = true - c.funcMap["ge"] = true - c.funcMap["gt"] = true - c.funcMap["le"] = true - c.funcMap["lt"] = true - c.funcMap["ne"] = true - c.importMap = make(map[string]string) - c.importMap["strconv"] = "strconv" - c.varList = varList - c.expectsInt = expectsInt - holdreflect := reflect.ValueOf(expectsInt) - - res, err := ioutil.ReadFile(dir + name) - if err != nil { - log.Fatal(err) - } - content := string(res) - - tree := parse.New(name, c.funcMap) - var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree) - tree, err = tree.Parse(content,"{{","}}", treeSet, c.funcMap) - if err != nil { - log.Fatal(err) - } - - if debug { - fmt.Println(name) - } - - out = "" - fname := strings.TrimSuffix(name, filepath.Ext(name)) - c.tlist = make(map[string]*parse.Tree) - c.tlist[fname] = tree - varholder := "tmpl_" + fname + "_vars" - - if debug { - fmt.Println(c.tlist) - } - - c.localVars = make(map[string]map[string]VarItemReflect) - c.localVars[fname] = make(map[string]VarItemReflect) - c.localVars[fname]["."] = VarItemReflect{".",varholder,holdreflect} - - subtree := c.tlist[fname] - if debug { - fmt.Println(subtree.Root) - } - - for _, node := range subtree.Root.Nodes { - if debug { - fmt.Println("Node: " + node.String()) - } - out += c.compile_switch(varholder, holdreflect, fname, node) - } - - var importList string - if c.doImports { - for _, item := range c.importMap { - importList += "import \"" + item + "\"\n" - } - } - - var varString string - for _, varItem := range c.varList { - varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n" - } - - out = "package main\n" + importList + "\nfunc init() {\nctemplates[\"" + fname + "\"] = template_" + fname + "\n}\n\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ") (tmpl string) {\n" + varString + out + "return tmpl\n}\n" - if debug { - fmt.Println("Output!") - fmt.Println(out) - } - return out -} - -func (c *CTemplateSet2) compile_switch(varholder string, holdreflect reflect.Value, template_name string, node interface{}) (out string) { - switch node := node.(type) { - case *parse.ActionNode: - if debug { - fmt.Println("Action Node") - } - - if node.Pipe == nil { - break - } - for _, cmd := range node.Pipe.Cmds { - out += c.compile_subswitch(varholder, holdreflect, template_name, cmd) - } - return out - case *parse.IfNode: - if debug { - fmt.Println("If Node: ") - fmt.Println(node.Pipe) - } - - var expr string - for _, cmd := range node.Pipe.Cmds { - if debug { - fmt.Println("If Node Bit: ") - fmt.Println(cmd) - fmt.Println(reflect.ValueOf(cmd).Type().Name()) - } - expr += c.compile_varswitch(varholder, holdreflect, template_name, cmd) - } - - if node.ElseList == nil { - if debug { - fmt.Println("Branch 1") - } - return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "}\n" - } else { - if debug { - fmt.Println("Branch 2") - } - return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "} else {\n" + c.compile_switch(varholder, holdreflect, template_name, node.ElseList) + "}\n" - } - case *parse.ListNode: - if debug { - fmt.Println("List Node") - } - for _, subnode := range node.Nodes { - out += c.compile_switch(varholder, holdreflect, template_name, subnode) - } - return out - case *parse.RangeNode: - if debug { - fmt.Println("Range Node!") - fmt.Println(node.Pipe) - } - - var outVal reflect.Value - for _, cmd := range node.Pipe.Cmds { - if debug { - fmt.Println("Range Bit: ") - fmt.Println(cmd) - } - out, outVal = c.compile_reflectswitch(varholder, holdreflect, template_name, cmd) - } - - if debug { - fmt.Println("Returned: ") - fmt.Println(out) - fmt.Println("Range Kind Switch!") - } - - switch outVal.Kind() { - case reflect.Map: - var item reflect.Value - for _, key := range outVal.MapKeys() { - item = outVal.MapIndex(key) - } - - out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compile_switch("item", item, template_name, node.List) + "}\n}" - case reflect.Invalid: - return "" - } - - if node.ElseList != nil { - out += " else {\n" + c.compile_switch(varholder, holdreflect, template_name, node.ElseList) + "}\n" - } else { - out += "\n" - } - return out - case *parse.TemplateNode: - if debug { - fmt.Println("Template Node") - } - return c.compile_subtemplate(varholder, holdreflect, node) - case *parse.TextNode: - return "tmpl += `" + string(node.Text) + "`\n" - default: - panic("Unknown Node in main switch") - } - return "" -} - -func (c *CTemplateSet2) compile_subswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string) { - firstWord := node.Args[0] - switch n := firstWord.(type) { - case *parse.FieldNode: - if debug { - fmt.Println("Field Node: ") - fmt.Println(n.Ident) - } - - /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ - cur := holdreflect - - var varbit string - if cur.Kind() == reflect.Interface { - cur = cur.Elem() - varbit += ".(" + cur.Type().Name() + ")" - } - - for _, id := range n.Ident { - if debug { - fmt.Println("Data Kind: ") - fmt.Println(cur.Kind().String()) - fmt.Println("Field Bit: ") - fmt.Println(id) - } - - cur = cur.FieldByName(id) - if cur.Kind() == reflect.Interface { - cur = cur.Elem() - /*if cur.Kind() == reflect.String && cur.Type().Name() != "string" { - varbit = "string(" + varbit + "." + id + ")"*/ - //if cur.Kind() == reflect.String && cur.Type().Name() != "string" { - if cur.Type().PkgPath() != "main" { - c.importMap["html/template"] = "html/template" - varbit += "." + id + ".(" + strings.TrimPrefix(cur.Type().PkgPath(),"html/") + "." + cur.Type().Name() + ")" - } else { - varbit += "." + id + ".(" + cur.Type().Name() + ")" - } - } else { - varbit += "." + id - } - - if debug { - fmt.Println("End Cycle") - } - } - out = c.compile_varsub(varholder + varbit, cur) - - for _, varItem := range c.varList { - if strings.HasPrefix(out, varItem.Destination) { - out = strings.Replace(out, varItem.Destination, varItem.Name, 1) - } - } - return out - case *parse.DotNode: - if debug { - fmt.Println("Dot Node") - fmt.Println(node.String()) - } - return c.compile_varsub(varholder, holdreflect) - case *parse.NilNode: - panic("Nil is not a command x.x") - case *parse.VariableNode: - if debug { - fmt.Println("Variable Node") - fmt.Println(n.String()) - fmt.Println(n.Ident) - } - - out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) - return "tmpl += " + out + "\n" - case *parse.StringNode: - return n.Quoted - default: - fmt.Println("Unknown Kind: ") - fmt.Println(reflect.ValueOf(firstWord).Elem().Kind()) - fmt.Println("Unknown Type: ") - fmt.Println(reflect.ValueOf(firstWord).Elem().Type().Name()) - panic("I don't know what node this is") - } - return "" -} - -func (c *CTemplateSet2) compile_varswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string) { - firstWord := node.Args[0] - switch n := firstWord.(type) { - case *parse.FieldNode: - if debug { - fmt.Println("Field Node: ") - fmt.Println(n.Ident) - - for _, id := range n.Ident { - fmt.Println("Field Bit: ") - fmt.Println(id) - } - } - - /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ - return c.compile_boolsub(n.String(), varholder, template_name, holdreflect) - case *parse.ChainNode: - if debug { - fmt.Println("Chain Node: ") - fmt.Println(n.Node) - fmt.Println(node.Args) - } - break - case *parse.IdentifierNode: - if debug { - fmt.Println("Identifier Node: ") - fmt.Println(node) - fmt.Println(node.Args) - } - return c.compile_identswitch(varholder, holdreflect, template_name, node) - case *parse.DotNode: - return varholder - case *parse.VariableNode: - if debug { - fmt.Println("Variable Node") - fmt.Println(n.String()) - fmt.Println(n.Ident) - } - - out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) - return out - case *parse.NilNode: - panic("Nil is not a command x.x") - case *parse.PipeNode: - if debug { - fmt.Println("Pipe Node!") - fmt.Println(n) - } - - /*for _, cmd := range n.Cmds { - if debug { - fmt.Println("Pipe Bit: ") - fmt.Println(cmd) - } - out += c.compile_if_varsub_n(n.String(), varholder, template_name, holdreflect) - }*/ - - if debug { - fmt.Println("Args: ") - fmt.Println(node.Args) - } - - /*argcopy := node.Args[1:] - for _, arg := range argcopy { - if debug { - fmt.Println("Pipe Arg: ") - fmt.Println(arg) - fmt.Println(reflect.ValueOf(arg).Elem().Type().Name()) - fmt.Println(reflect.ValueOf(arg).Kind()) - } - - switch arg.(type) { - case *parse.IdentifierNode: - out += c.compile_identswitch(varholder, holdreflect, template_name, node) - break - case *parse.PipeNode: - break - //out += c.compile_if_varsub_n(a.String(), varholder, template_name, holdreflect) - default: - panic("Unknown Pipe Arg type! Did Mario get stuck in the pipes again?") - } - //out += c.compile_varswitch(arg.String(), holdreflect, template_name, arg) - }*/ - out += c.compile_identswitch(varholder, holdreflect, template_name, node) - - if debug { - fmt.Println("Out: ") - fmt.Println(out) - } - return out - default: - fmt.Println("Unknown Kind: ") - fmt.Println(reflect.ValueOf(firstWord).Elem().Kind()) - fmt.Println("Unknown Type: ") - fmt.Println(reflect.ValueOf(firstWord).Elem().Type().Name()) - panic("I don't know what node this is! Grr...") - } - return "" -} - -func (c *CTemplateSet2) compile_identswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string) { - ArgLoop: - for pos, id := range node.Args { - if debug { - fmt.Println(id) - } - - switch id.String() { - case "not": - out += "!" - case "or": - out += " || " - case "and": - out += " && " - case "le": - out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect) - break ArgLoop - default: - if debug { - fmt.Println("Variable!") - } - out += c.compile_if_varsub_n(id.String(), varholder, template_name, holdreflect) - } - } - return out -} - -func (c *CTemplateSet2) compile_reflectswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string, outVal reflect.Value) { - firstWord := node.Args[0] - switch n := firstWord.(type) { - case *parse.FieldNode: - if debug { - fmt.Println("Field Node: ") - fmt.Println(n.Ident) - - for _, id := range n.Ident { - fmt.Println("Field Bit: ") - fmt.Println(id) - } - } - /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ - return c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) - case *parse.ChainNode: - if debug { - fmt.Println("Chain Node: ") - fmt.Println(n.Node) - fmt.Println(node.Args) - } - return "", outVal - /*case *parse.IdentifierNode: - fmt.Println("Identifier Node: ") - fmt.Println(node) - fmt.Println(node.Args) - - ArgLoop: - for pos, id := range node.Args { - fmt.Println(id) - switch id.String() { - case "not": - out += "!" - case "or": - out += " || " - case "and": - out += " && " - case "le": - out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, holdreflect) - break ArgLoop - default: - fmt.Println("Variable!") - out += c.compile_if_varsub_n(id.String(), varholder, holdreflect) - } - } - return out*/ - case *parse.DotNode: - return varholder, holdreflect - case *parse.NilNode: - panic("Nil is not a command x.x") - default: - //panic("I don't know what node this is") - } - return "", outVal -} - -func (c *CTemplateSet2) compile_if_varsub_n(varname string, varholder string, template_name string, cur reflect.Value) (out string) { - out, _ = c.compile_if_varsub(varname, varholder, template_name, cur) - return out -} - -func (c *CTemplateSet2) compile_if_varsub(varname string, varholder string, template_name string, cur reflect.Value) (out string, val reflect.Value) { - if varname[0] != '.' && varname[0] != '$' { - return varname, cur - } - - bits := strings.Split(varname,".") - if varname[0] == '$' { - var res VarItemReflect - if varname[1] == '.' { - res = c.localVars[template_name]["."] - } else { - res = c.localVars[template_name][strings.TrimPrefix(bits[0],"$")] - } - out += res.Destination - cur = res.Value - - if cur.Kind() == reflect.Interface { - cur = cur.Elem() - } - } else { - if cur.Kind() == reflect.Interface { - cur = cur.Elem() - out += varholder + ".(" + cur.Type().Name() + ")" - } else { - out += varholder - } - } - bits[0] = strings.TrimPrefix(bits[0],"$") - - if debug { - fmt.Println("Cur Kind: ") - fmt.Println(cur.Kind()) - fmt.Println("Cur Type: ") - fmt.Println(cur.Type().Name()) - } - - for _, bit := range bits { - if debug { - fmt.Println("Variable Field!") - fmt.Println(bit) - } - - if bit == "" { - continue - } - - cur = cur.FieldByName(bit) - if cur.Kind() == reflect.Interface { - cur = cur.Elem() - out += "." + bit + ".(" + cur.Type().Name() + ")" - } else { - out += "." + bit - } - - if debug { - fmt.Println("Data Kind: ") - fmt.Println(cur.Kind()) - fmt.Println("Data Type: ") - fmt.Println(cur.Type().Name()) - } - } - - for _, varItem := range c.varList { - if strings.HasPrefix(out, varItem.Destination) { - out = strings.Replace(out, varItem.Destination, varItem.Name, 1) - } - } - return out, cur -} - -func (c *CTemplateSet2) compile_boolsub(varname string, varholder string, template_name string, val reflect.Value) string { - out, val := c.compile_if_varsub(varname, varholder, template_name, val) - switch val.Kind() { - case reflect.Int: - out += " > 0" - case reflect.Bool: - // Do nothing - case reflect.String: - out += " != \"\"" - case reflect.Int64: - out += " > 0" - default: - panic("I don't know what this variable's type is o.o\n") - } - return out -} - -func (c *CTemplateSet2) compile_varsub(varname string, val reflect.Value) string { - for _, varItem := range c.varList { - if strings.HasPrefix(varname, varItem.Destination) { - varname = strings.Replace(varname, varItem.Destination, varItem.Name, 1) - } - } - - if val.Kind() == reflect.Interface { - val = val.Elem() - } - - switch val.Kind() { - case reflect.Int: - return "tmpl += strconv.Itoa(" + varname + ")\n" - case reflect.Bool: - return "if " + varname + " {\ntmpl += \"true\"} else {\ntmpl += \"false\"\n}\n" - case reflect.String: - if val.Type().Name() != "string" && !strings.HasPrefix(varname,"string(") { - return "tmpl += string(" + varname + ")\n" - } else { - return "tmpl += " + varname + "\n" - } - case reflect.Int64: - return "tmpl += strconv.FormatInt(" + varname + ", 10)" - default: - fmt.Println("Unknown Kind: ") - fmt.Println(val.Kind()) - fmt.Println("Unknown Type: ") - fmt.Println(val.Type().Name()) - panic("// I don't know what this variable's type is o.o\n") - } -} - -func (c *CTemplateSet2) compile_subtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) { - if debug { - fmt.Println("Template Node: " + node.Name) - } - - fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name)) - varholder := "tmpl_" + fname + "_vars" - var holdreflect reflect.Value - if node.Pipe != nil { - for _, cmd := range node.Pipe.Cmds { - firstWord := cmd.Args[0] - switch firstWord.(type) { - case *parse.DotNode: - varholder = pvarholder - holdreflect = pholdreflect - break - case *parse.NilNode: - panic("Nil is not a command x.x") - default: - out = "var " + varholder + " := false\n" - out += c.compile_command(cmd) - } - } - } - - res, err := ioutil.ReadFile(c.dir + node.Name) - if err != nil { - log.Fatal(err) - } - content := string(res) - - tree := parse.New(node.Name, c.funcMap) - var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree) - tree, err = tree.Parse(content,"{{","}}", treeSet, c.funcMap) - if err != nil { - log.Fatal(err) - } - - c.tlist[fname] = tree - subtree := c.tlist[fname] - if debug { - fmt.Println(subtree.Root) - } - - c.localVars[fname] = make(map[string]VarItemReflect) - c.localVars[fname]["."] = VarItemReflect{".",varholder,holdreflect} - - for _, node := range subtree.Root.Nodes { - if debug { - fmt.Println("Node: " + node.String()) - } - out += c.compile_switch(varholder, holdreflect, fname, node) - } - return out -} - -func (c *CTemplateSet2) compile_command(*parse.CommandNode) (out string) { - return "" -} \ No newline at end of file diff --git a/topic.go b/topic.go index 162263af..ce37a0e5 100644 --- a/topic.go +++ b/topic.go @@ -80,9 +80,11 @@ type TopicsRow struct } type TopicStore interface { + Load(id int) error Get(id int) (*Topic, error) GetUnsafe(id int) (*Topic, error) CascadeGet(id int) (*Topic, error) + Set(item *Topic) error Add(item *Topic) error AddUnsafe(item *Topic) error Remove(id int) error @@ -137,6 +139,31 @@ func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) { return topic, err } +func (sts *StaticTopicStore) Load(id int) error { + topic := &Topic{ID:id} + err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) + if err == nil { + sts.Set(topic) + } + return err +} + +func (sts *StaticTopicStore) Set(item *Topic) error { + sts.mu.Lock() + item, ok := sts.items[item.ID] + if ok { + sts.items[item.ID] = item + } else if sts.length >= sts.capacity { + sts.mu.Unlock() + return ErrStoreCapacityOverflow + } else { + sts.items[item.ID] = item + } + sts.mu.Unlock() + sts.length++ + return nil +} + func (sts *StaticTopicStore) Add(item *Topic) error { if sts.length >= sts.capacity { return ErrStoreCapacityOverflow @@ -218,7 +245,16 @@ func (sus *SqlTopicStore) CascadeGet(id int) (*Topic, error) { return &topic, err } +func (sus *SqlTopicStore) Load(id int) error { + topic := Topic{ID:id} + err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) + return err +} + // Placeholder methods, the actual queries are done elsewhere +func (sus *SqlTopicStore) Set(item *Topic) error { + return nil +} func (sus *SqlTopicStore) Add(item *Topic) error { return nil } @@ -252,6 +288,7 @@ func get_topicuser(tid int) (TopicUser,error) { if err != nil { return TopicUser{ID:tid}, err } + init_user_perms(user) // We might be better off just passing seperate topic and user structs to the caller? return copy_topic_to_topicuser(topic, user), nil @@ -264,13 +301,8 @@ func get_topicuser(tid int) (TopicUser,error) { if err != nil { return TopicUser{ID:tid}, err } + init_user_perms(user) tu := copy_topic_to_topicuser(topic, user) - //fmt.Printf("%+v\n", topic) - //fmt.Println("") - //fmt.Printf("%+v\n", user) - //fmt.Println("") - //fmt.Printf("%+v\n", tu) - //fmt.Println("") return tu, nil } } @@ -280,7 +312,9 @@ func get_topicuser(tid int) (TopicUser,error) { the_topic := Topic{ID:tu.ID, Title:tu.Title, Content:tu.Content, CreatedBy:tu.CreatedBy, Is_Closed:tu.Is_Closed, Sticky:tu.Sticky, CreatedAt:tu.CreatedAt, LastReplyAt:tu.LastReplyAt, ParentID:tu.ParentID, IpAddress:tu.IpAddress, PostCount:tu.PostCount, LikeCount:tu.LikeCount} //fmt.Printf("%+v\n", the_topic) + tu.Tag = groups[tu.Group].Tag topics.Add(&the_topic) + //err = errors.Error("Loaded data via query") return tu, err } diff --git a/user.go b/user.go index 602fd3f8..1fb3ebf8 100644 --- a/user.go +++ b/user.go @@ -9,6 +9,8 @@ import "golang.org/x/crypto/bcrypt" import "database/sql" import _ "github.com/go-sql-driver/mysql" +var guest_user User = User{ID:0,Group:6,Perms:GuestPerms} + type User struct { ID int @@ -44,9 +46,11 @@ type Email struct } type UserStore interface { + Load(id int) error Get(id int) (*User, error) GetUnsafe(id int) (*User, error) CascadeGet(id int) (*User, error) + Set(item *User) error Add(item *User) error AddUnsafe(item *User) error Remove(id int) error @@ -94,12 +98,58 @@ func (sts *StaticUserStore) CascadeGet(id int) (*User, error) { user = &User{ID:id} err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) + + if user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(user) if err == nil { sts.Add(user) } return user, err } +func (sts *StaticUserStore) Load(id int) error { + user := &User{ID:id} + err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) + if err != nil { + return err + } + + if user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(user) + sts.Set(user) + return nil +} + +func (sts *StaticUserStore) Set(item *User) error { + sts.mu.Lock() + item, ok := sts.items[item.ID] + if ok { + sts.items[item.ID] = item + } else if sts.length >= sts.capacity { + sts.mu.Unlock() + return ErrStoreCapacityOverflow + } else { + sts.items[item.ID] = item + } + sts.mu.Unlock() + sts.length++ + return nil +} + func (sts *StaticUserStore) Add(item *User) error { if sts.length >= sts.capacity { return ErrStoreCapacityOverflow @@ -161,22 +211,62 @@ func NewSqlUserStore() *SqlUserStore { func (sus *SqlUserStore) Get(id int) (*User, error) { user := User{ID:id} err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) + + if user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) return &user, err } func (sus *SqlUserStore) GetUnsafe(id int) (*User, error) { user := User{ID:id} err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) + + if user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) return &user, err } func (sus *SqlUserStore) CascadeGet(id int) (*User, error) { user := User{ID:id} err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) + + if user.Avatar != "" { + if user.Avatar[0] == '.' { + user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + } + } else { + user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) + } + user.Tag = groups[user.Group].Tag + init_user_perms(&user) return &user, err } +func (sus *SqlUserStore) Load(id int) error { + user := &User{ID:id} + // Simplify this into a quick check whether the user exists + err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) + return err +} + // Placeholder methods, the actual queries are done elsewhere +func (sus *SqlUserStore) Set(item *User) error { + return nil +} func (sus *SqlUserStore) Add(item *User) error { return nil } @@ -258,6 +348,8 @@ func ForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user Us } user, success = SimpleSessionCheck(w,r) fperms := groups[user.Group].Forums[fid] + //fmt.Printf("%+v\n", user.Perms) + //fmt.Printf("%+v\n", fperms) if fperms.Overrides && !user.Is_Super_Admin { user.Perms.ViewTopic = fperms.ViewTopic user.Perms.LikeItem = fperms.LikeItem @@ -288,45 +380,28 @@ func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (User,bool) { // Are there any session cookies..? cookie, err := r.Cookie("uid") if err != nil { - return User{ID:0,Group:6,Perms:GuestPerms}, true + return guest_user, true } uid, err := strconv.Atoi(cookie.Value) if err != nil { - return User{ID:0,Group:6,Perms:GuestPerms}, true + return guest_user, true } cookie, err = r.Cookie("session") if err != nil { - return User{ID:0,Group:6,Perms:GuestPerms}, true + return guest_user, true } // Is this session valid..? user, err := users.CascadeGet(uid) if err == sql.ErrNoRows { - user.ID = 0 - user.Session = "" - user.Group = 6 - user.Perms = GuestPerms - return *user, true + return guest_user, true } else if err != nil { InternalError(err,w,r) - return *user, false + return guest_user, false } if user.Session == "" || cookie.Value != user.Session { - user.ID = 0 - user.Session = "" - user.Group = 6 - user.Perms = GuestPerms - return *user, true - } - - user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin - user.Is_Super_Mod = groups[user.Group].Is_Mod || user.Is_Admin - user.Is_Mod = user.Is_Super_Mod - user.Is_Banned = groups[user.Group].Is_Banned - user.Loggedin = !user.Is_Banned || user.Is_Super_Mod - if user.Is_Banned && user.Is_Super_Mod { - user.Is_Banned = false + return guest_user, true } if user.Is_Super_Admin { @@ -335,14 +410,6 @@ func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (User,bool) { user.Perms = groups[user.Group].Perms } - if user.Avatar != "" { - if user.Avatar[0] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar - } - } else { - user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1) - } - host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { PreError("Bad IP",w,r) @@ -448,3 +515,13 @@ func decrease_post_user_stats(wcount int, uid int, topic bool, user User) error _, err = update_user_level_stmt.Exec(getLevel(user.Score - base_score - mod), uid) return err } + +func init_user_perms(user *User) { + user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin + user.Is_Super_Mod = user.Is_Admin || groups[user.Group].Is_Mod + user.Is_Mod = user.Is_Super_Mod + user.Is_Banned = groups[user.Group].Is_Banned + if user.Is_Banned && user.Is_Super_Mod { + user.Is_Banned = false + } +} \ No newline at end of file