From a225e3b6adf40784f0c29f9db8f3aaabab954b3e Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Mon, 2 Oct 2017 18:06:35 -0500 Subject: [PATCH] Cleaning up the models, making things a little nicer Resolves issue #23 --- admin_clients.go | 26 ++- admin_endpoints.go | 2 +- admin_games.go | 51 +++-- admin_teams.go | 45 ++-- admin_users.go | 18 +- admin_votes.go | 3 - main.go | 59 ++--- model.go | 119 ++++------ model_clients.go | 194 ++++++++-------- model_games.go | 159 +++----------- model_site.go | 62 ++++++ model_teams.go | 523 +++++++++++++++++++++++++------------------- model_users.go | 46 ++-- model_votes.go | 96 +------- page_session.go | 2 +- public_endpoints.go | 99 +++++---- 16 files changed, 719 insertions(+), 785 deletions(-) create mode 100644 model_site.go diff --git a/admin_clients.go b/admin_clients.go index 419f13c..7e2c2c0 100644 --- a/admin_clients.go +++ b/admin_clients.go @@ -11,19 +11,20 @@ func handleAdminClients(w http.ResponseWriter, req *http.Request, page *pageData vars := mux.Vars(req) page.SubTitle = "Clients" clientId := vars["id"] + client := db.getClient(clientId) clientIp, _, _ := net.SplitHostPort(req.RemoteAddr) if clientId == "" { type clientsPageData struct { Clients []Client } - page.TemplateData = clientsPageData{Clients: dbGetAllClients()} + page.TemplateData = clientsPageData{Clients: db.getAllClients()} page.SubTitle = "Clients" page.show("admin-clients.html", w) } else { switch vars["function"] { case "add": page.SubTitle = "Authenticate Client" - cli := dbGetClient(clientId) + cli := db.getClient(clientId) if cli.IP == "" { cli.IP = clientIp } @@ -38,14 +39,17 @@ func handleAdminClients(w http.ResponseWriter, req *http.Request, page *pageData email := req.FormValue("email") password := req.FormValue("password") clientName := req.FormValue("clientname") + // Authentication isn't required to set a client name if clientName != "" { - dbSetClientName(clientId, clientName) + client.Name = clientName } - dbUpdateClientIP(clientId, clientIp) + client.IP = clientIp + client.save() if page.LoggedIn || doLogin(email, password) == nil { // Received a valid login // Authenticate the client - if dbAuthClient(clientId, clientIp) == nil { + client.Auth = true + if client.save() == nil { page.session.setFlashMessage("Client Authenticated", "success") } else { page.session.setFlashMessage("Client Authentication Failed", "error") @@ -56,17 +60,17 @@ func handleAdminClients(w http.ResponseWriter, req *http.Request, page *pageData } redirect("/", w, req) case "deauth": - dbDeAuthClient(clientId) + client.Auth = false + if client.save() == nil { + page.session.setFlashMessage("Client De-Authenticated", "success") + } else { + page.session.setFlashMessage("Client De-Authentication Failed", "success") + } redirect("/admin/clients", w, req) } } } -func clientIsAuthenticated(cid string, req *http.Request) bool { - return dbClientIsAuth(cid) - //return clientIsServer(req) || dbClientIsAuth(cid) -} - func clientIsServer(req *http.Request) bool { clientIp, _, _ := net.SplitHostPort(req.RemoteAddr) ifaces, err := net.Interfaces() diff --git a/admin_endpoints.go b/admin_endpoints.go index 5bf61a4..048d711 100644 --- a/admin_endpoints.go +++ b/admin_endpoints.go @@ -72,7 +72,7 @@ func handleAdminSetAuthMode(w http.ResponseWriter, req *http.Request, page *page if err != nil { page.session.setFlashMessage("Invalid Authentication Mode: "+vars["id"], "error") } - if dbSetAuthMode(newMode) != nil { + if db.setAuthMode(newMode) != nil { page.session.setFlashMessage("Invalid Authentication Mode: "+vars["id"], "error") } redirect("/admin", w, req) diff --git a/admin_games.go b/admin_games.go index 04026a2..8fc755f 100644 --- a/admin_games.go +++ b/admin_games.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/base64" "errors" - "fmt" "image" "image/gif" "image/jpeg" @@ -25,46 +24,50 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) Teams []Team } gpd := new(gamesPageData) - gpd.Teams = dbGetAllTeams() + gpd.Teams = db.getAllTeams() page.TemplateData = gpd page.SubTitle = "Games" page.show("admin-games.html", w) } else { - switch vars["function"] { - case "save": - name := req.FormValue("gamename") - desc := req.FormValue("gamedesc") - link := req.FormValue("gamelink") - if dbIsValidTeam(teamId) { - if err := dbUpdateTeamGame(teamId, name, link, desc); err != nil { + tm := db.getTeam(teamId) + if tm != nil { + switch vars["function"] { + case "save": + gm := newGame(tm.UUID) + gm.Name = req.FormValue("gamename") + gm.Link = req.FormValue("gamelink") + gm.Description = req.FormValue("gamedesc") + if err := gm.save(); err != nil { page.session.setFlashMessage("Error updating game: "+err.Error(), "error") } else { page.session.setFlashMessage("Team game updated", "success") } - redirect("/admin/teams/"+teamId+"#game", w, req) + redirect("/admin/teams/"+tm.UUID+"#game", w, req) + case "screenshotupload": + if err := saveScreenshots(tm, req); err != nil { + page.session.setFlashMessage("Error updating game: "+err.Error(), "error") + } + redirect("/admin/teams/"+tm.UUID+"#game", w, req) + case "screenshotdelete": + ssid := vars["subid"] + if err := tm.deleteScreenshot(ssid); err != nil { + page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") + } + redirect("/admin/teams/"+tm.UUID+"#game", w, req) } - case "screenshotupload": - if err := saveScreenshots(teamId, req); err != nil { - page.session.setFlashMessage("Error updating game: "+err.Error(), "error") - } - redirect("/admin/teams/"+teamId+"#game", w, req) - case "screenshotdelete": - ssid := vars["subid"] - if err := dbDeleteTeamGameScreenshot(teamId, ssid); err != nil { - page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") - } - redirect("/admin/teams/"+teamId+"#game", w, req) + } else { + page.session.setFlashMessage("Not a valid team id", "error") + redirect("/admin/teams", w, req) } } } -func saveScreenshots(teamId string, req *http.Request) error { +func saveScreenshots(tm *Team, req *http.Request) error { var err error file, hdr, err := req.FormFile("newssfile") if err != nil { return err } - fmt.Println("File Received: " + hdr.Filename) extIdx := strings.LastIndex(hdr.Filename, ".") fltp := "png" if len(hdr.Filename) > extIdx { @@ -90,7 +93,7 @@ func saveScreenshots(teamId string, req *http.Request) error { } thmString = base64.StdEncoding.EncodeToString(thmBuf.Bytes()) - return dbSaveTeamGameScreenshot(teamId, &Screenshot{ + return tm.saveScreenshot(&Screenshot{ Image: base64.StdEncoding.EncodeToString(buf.Bytes()), Thumbnail: thmString, Filetype: fltp, diff --git a/admin_teams.go b/admin_teams.go index 18efe83..d2000f0 100644 --- a/admin_teams.go +++ b/admin_teams.go @@ -11,7 +11,7 @@ import ( ) func refreshTeamsInMemory() { - site.Teams = dbGetAllTeams() + site.Teams = db.getAllTeams() } func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) { @@ -23,11 +23,11 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) switch vars["function"] { case "save": name := req.FormValue("teamname") - if dbGetTeamByName(name) != nil { + if db.getTeamByName(name) != nil { // A team with that name already exists page.session.setFlashMessage("A team with the name "+name+" already exists!", "error") } else { - if err := dbCreateNewTeam(name); err != nil { + if err := db.newTeam(name); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("Team "+name+" created!", "success") @@ -41,13 +41,13 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) } } else if teamId != "" { // Functions for existing team - if dbIsValidTeam(teamId) { + tm := db.getTeam(teamId) + if tm != nil { switch vars["function"] { case "save": - tm := new(Team) tm.UUID = teamId tm.Name = req.FormValue("teamname") - if err := dbUpdateTeam(teamId, tm); err != nil { + if err := tm.save(); err != nil { page.session.setFlashMessage("Error updating team: "+err.Error(), "error") } else { page.session.setFlashMessage("Team Updated!", "success") @@ -56,20 +56,20 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) redirect("/admin/teams", w, req) case "delete": var err error - t := dbGetTeam(teamId) - if err = dbDeleteTeam(teamId); err != nil { + if err = tm.delete(); err != nil { page.session.setFlashMessage("Error deleting team: "+err.Error(), "error") } else { - page.session.setFlashMessage("Team "+t.Name+" Deleted", "success") + page.session.setFlashMessage("Team "+tm.Name+" Deleted", "success") } refreshTeamsInMemory() redirect("/admin/teams", w, req) case "savemember": mbrName := req.FormValue("newmembername") - mbrSlack := req.FormValue("newmemberslackid") - mbrTwitter := req.FormValue("newmembertwitter") - mbrEmail := req.FormValue("newmemberemail") - if err := dbAddTeamMember(teamId, mbrName, mbrEmail, mbrSlack, mbrTwitter); err != nil { + mbr := newTeamMember(mbrName) + mbr.SlackId = req.FormValue("newmemberslackid") + mbr.Twitter = req.FormValue("newmembertwitter") + mbr.Email = req.FormValue("newmemberemail") + if err := tm.updateTeamMember(mbr); err != nil { page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") } else { page.session.setFlashMessage(mbrName+" added to team!", "success") @@ -77,18 +77,21 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) refreshTeamsInMemory() redirect("/admin/teams/"+teamId+"#members", w, req) case "deletemember": - mbrId := req.FormValue("memberid") - m, _ := dbGetTeamMember(teamId, mbrId) - if err := dbDeleteTeamMember(teamId, mbrId); err != nil { - page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") + m := tm.getTeamMember(req.FormValue("memberid")) + if m != nil { + if err := tm.deleteTeamMember(m); err != nil { + page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") + } else { + page.session.setFlashMessage(m.Name+" deleted from team", "success") + } + refreshTeamsInMemory() } else { - page.session.setFlashMessage(m.Name+" deleted from team", "success") + page.session.setFlashMessage("Couldn't find team member to delete", "error") } - refreshTeamsInMemory() redirect("/admin/teams/"+teamId+"#members", w, req) default: page.SubTitle = "Edit Team" - t := dbGetTeam(teamId) + t := db.getTeam(teamId) page.TemplateData = t page.show("admin-editteam.html", w) } @@ -101,7 +104,7 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) type teamsPageData struct { Teams []Team } - page.TemplateData = teamsPageData{Teams: dbGetAllTeams()} + page.TemplateData = teamsPageData{Teams: db.getAllTeams()} page.SubTitle = "Teams" page.show("admin-teams.html", w) } diff --git a/admin_users.go b/admin_users.go index 713fe4f..f4bdcea 100644 --- a/admin_users.go +++ b/admin_users.go @@ -29,7 +29,7 @@ func handleAdminDoLogin(w http.ResponseWriter, req *http.Request) { // If it can't, it returns an error func doLogin(email, password string) error { if strings.TrimSpace(email) != "" && strings.TrimSpace(password) != "" { - return dbCheckCredentials(email, password) + return db.checkCredentials(email, password) } return errors.New("Invalid Credentials") } @@ -53,12 +53,12 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) switch vars["function"] { case "save": email = req.FormValue("email") - if dbIsValidUserEmail(email) { + if db.isValidUserEmail(email) { // User already exists page.session.setFlashMessage("A user with email address "+email+" already exists!", "error") } else { password := req.FormValue("password") - if err := dbUpdateUserPassword(email, string(password)); err != nil { + if err := db.updateUserPassword(email, string(password)); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("User "+email+" created!", "success") @@ -73,10 +73,10 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) switch vars["function"] { case "save": var err error - if dbIsValidUserEmail(email) { + if db.isValidUserEmail(email) { password := req.FormValue("password") if password != "" { - if err = dbUpdateUserPassword(email, password); err != nil { + if err = db.updateUserPassword(email, password); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("User "+email+" created!", "success") @@ -86,8 +86,8 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) } case "delete": var err error - if dbIsValidUserEmail(email) { - if err = dbDeleteUser(email); err != nil { + if db.isValidUserEmail(email) { + if err = db.deleteUser(email); err != nil { page.session.setFlashMessage(err.Error(), "error") } else { page.session.setFlashMessage("User "+email+" deleted!", "success") @@ -96,7 +96,7 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) redirect("/admin/users", w, req) default: page.SubTitle = "Edit Admin User" - if !dbIsValidUserEmail(email) { + if !db.isValidUserEmail(email) { page.session.setFlashMessage("Couldn't find the requested user, please try again.", "error") redirect("/admin/users", w, req) } @@ -107,7 +107,7 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) type usersPageData struct { Users []string } - page.TemplateData = usersPageData{Users: dbGetAllUsers()} + page.TemplateData = usersPageData{Users: db.getAllUsers()} page.SubTitle = "Admin Users" page.show("admin-users.html", w) diff --git a/admin_votes.go b/admin_votes.go index 5a2e514..63ef05f 100644 --- a/admin_votes.go +++ b/admin_votes.go @@ -2,7 +2,6 @@ package main import ( "errors" - "fmt" "net/http" "time" @@ -56,14 +55,12 @@ func getCondorcetResult() []Ranking { teamWins[allPairs[i].winner.UUID]++ } } - fmt.Println(teamWins) // Rank them by wins rankedWins := make(map[int][]string) for k, v := range teamWins { rankedWins[v] = append(rankedWins[v], k) } - fmt.Println(rankedWins) currRank := 1 for len(rankedWins) > 0 { topWins := 0 diff --git a/main.go b/main.go index 03badeb..632931c 100644 --- a/main.go +++ b/main.go @@ -23,29 +23,6 @@ import ( const AppName = "gjvote" const DbName = AppName + ".db" -// SiteData is stuff that stays the same -type siteData struct { - Title string - Port int - SessionName string - ServerDir string - DevMode bool - - CurrentJam string - - Teams []Team - Votes []Vote -} - -func (s *siteData) getTeamByUUID(uuid string) *Team { - for i := range s.Teams { - if s.Teams[i].UUID == uuid { - return &s.Teams[i] - } - } - return nil -} - // pageData is stuff that changes per request type pageData struct { Site *siteData @@ -85,8 +62,9 @@ var site *siteData var r *mux.Router func main() { + db = new(gjDatabase) loadConfig() - dbSaveSiteConfig(site) + site.save() initialize() r = mux.NewRouter() @@ -132,7 +110,7 @@ func main() { } func loadConfig() { - site = dbGetSiteConfig() + site = db.getSiteConfig() if len(os.Args) > 1 { for _, v := range os.Args { @@ -177,9 +155,9 @@ func loadConfig() { func initialize() { // Check if the database has been created - assertError(initDatabase()) + assertError(db.initialize()) - if !dbHasUser() { + if !db.hasUser() { reader := bufio.NewReader(os.Stdin) fmt.Println("Create new Admin user") fmt.Print("Email: ") @@ -197,20 +175,20 @@ func initialize() { fmt.Println("Entered Passwords don't match!") } } - assertError(dbUpdateUserPassword(email, string(pw1))) + assertError(db.updateUserPassword(email, string(pw1))) } - if !dbHasCurrentJam() { + if !db.hasCurrentJam() { reader := bufio.NewReader(os.Stdin) fmt.Println("Create New Game Jam") fmt.Print("GameJam Name: ") gjName, _ := reader.ReadString('\n') gjName = strings.TrimSpace(gjName) - if dbSetCurrentJam(gjName) != nil { + if db.setCurrentJam(gjName) != nil { fmt.Println("Error saving Current Jam") } } - jmNm, err := dbGetCurrentJam() + jmNm, err := db.getCurrentJam() if err == nil { fmt.Println("Current Jam Name: " + jmNm) } else { @@ -218,8 +196,8 @@ func initialize() { } // Load all votes into memory - site.Votes = dbGetAllVotes() - site.Teams = dbGetAllTeams() + site.Votes = db.getAllVotes() + site.Teams = db.getAllTeams() } func loggingHandler(h http.Handler) http.Handler { @@ -246,7 +224,7 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { // First check if we're logged in userEmail, _ := p.session.getStringValue("email") // With a valid account - p.LoggedIn = dbIsValidUserEmail(userEmail) + p.LoggedIn = db.isValidUserEmail(userEmail) p.Site = site p.SubTitle = "GameJam Voting" @@ -282,19 +260,20 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { } p.HideAdminMenu = true - if p.CurrentJam, err = dbGetCurrentJam(); err != nil { + if p.CurrentJam, err = db.getCurrentJam(); err != nil { p.FlashMessage = "Error Loading Current GameJam: " + err.Error() p.FlashClass = "error" } p.ClientId = p.session.getClientId() - p.ClientIsAuth = clientIsAuthenticated(p.ClientId, req) + cl := db.getClient(p.ClientId) + p.ClientIsAuth = cl.Auth p.ClientIsServer = clientIsServer(req) // Public Mode - p.PublicMode = dbGetPublicSiteMode() + p.PublicMode = db.getPublicSiteMode() // Authentication Mode - p.AuthMode = dbGetAuthMode() + p.AuthMode = db.getAuthMode() return p } @@ -330,7 +309,7 @@ func redirect(url string, w http.ResponseWriter, req *http.Request) { } func resetToDefaults() { - def := GetDefaultSiteConfig() + def := NewSiteData() fmt.Println("Reset settings to defaults?") fmt.Print(site.Title, " -> ", def.Title, "\n") fmt.Print(site.Port, " -> ", def.Port, "\n") @@ -341,7 +320,7 @@ func resetToDefaults() { conf, _ := reader.ReadString('\n') conf = strings.ToUpper(strings.TrimSpace(conf)) if strings.HasPrefix(conf, "Y") { - if dbSaveSiteConfig(def) != nil { + if def.save() != nil { errorExit("Error resetting to defaults") } fmt.Println("Reset to defaults") diff --git a/model.go b/model.go index 2365065..d28d2b8 100644 --- a/model.go +++ b/model.go @@ -7,14 +7,12 @@ import ( "github.com/br0xen/boltease" ) -var db *boltease.DB -var dbOpened int +type gjDatabase struct { + bolt *boltease.DB + dbOpened int +} -const ( - SiteModeWaiting = iota - SiteModeVoting - SiteModeError -) +var db *gjDatabase const ( AuthModeAuthentication = iota @@ -22,20 +20,11 @@ const ( AuthModeError ) -func GetDefaultSiteConfig() *siteData { - ret := new(siteData) - ret.Title = "ICT GameJam" - ret.Port = 8080 - ret.SessionName = "ict-gamejam" - ret.ServerDir = "./" - return ret -} - -func openDatabase() error { - dbOpened += 1 - if dbOpened == 1 { +func (db *gjDatabase) open() error { + db.dbOpened += 1 + if db.dbOpened == 1 { var err error - db, err = boltease.Create(DbName, 0600, nil) + db.bolt, err = boltease.Create(DbName, 0600, nil) if err != nil { return err } @@ -43,60 +32,60 @@ func openDatabase() error { return nil } -func closeDatabase() error { - dbOpened -= 1 - if dbOpened == 0 { - return db.CloseDB() +func (db *gjDatabase) close() error { + db.dbOpened -= 1 + if db.dbOpened == 0 { + return db.bolt.CloseDB() } return nil } -func initDatabase() error { +func (db *gjDatabase) initialize() error { var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return err } - defer closeDatabase() + defer db.close() // Create the path to the bucket to store admin users - if err := db.MkBucketPath([]string{"users"}); err != nil { + if err := db.bolt.MkBucketPath([]string{"users"}); err != nil { return err } // Create the path to the bucket to store jam informations - if err := db.MkBucketPath([]string{"jam"}); err != nil { + if err := db.bolt.MkBucketPath([]string{"jam"}); err != nil { return err } // Create the path to the bucket to store site config data - return db.MkBucketPath([]string{"site"}) + return db.bolt.MkBucketPath([]string{"site"}) } -func dbSetCurrentJam(name string) error { +func (db *gjDatabase) setCurrentJam(name string) error { var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return err } - defer closeDatabase() + defer db.close() - return db.SetValue([]string{"site"}, "current-jam", name) + return db.bolt.SetValue([]string{"site"}, "current-jam", name) } -func dbHasCurrentJam() bool { +func (db *gjDatabase) hasCurrentJam() bool { var err error - if _, err = dbGetCurrentJam(); err != nil { + if _, err = db.getCurrentJam(); err != nil { return false } return true } -func dbGetCurrentJam() (string, error) { +func (db *gjDatabase) getCurrentJam() (string, error) { var ret string var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return "", err } - defer closeDatabase() + defer db.close() - ret, err = db.GetValue([]string{"site"}, "current-jam") + ret, err = db.bolt.GetValue([]string{"site"}, "current-jam") if err == nil && strings.TrimSpace(ret) == "" { return ret, errors.New("No Jam Name Specified") @@ -104,69 +93,49 @@ func dbGetCurrentJam() (string, error) { return ret, err } -func dbGetSiteConfig() *siteData { +func (db *gjDatabase) getSiteConfig() *siteData { var ret *siteData - def := GetDefaultSiteConfig() + def := NewSiteData() var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return def } - defer closeDatabase() + defer db.close() ret = new(siteData) siteConf := []string{"site"} - if ret.Title, err = db.GetValue(siteConf, "title"); err != nil { + if ret.Title, err = db.bolt.GetValue(siteConf, "title"); err != nil { ret.Title = def.Title } - if ret.Port, err = db.GetInt(siteConf, "port"); err != nil { + if ret.Port, err = db.bolt.GetInt(siteConf, "port"); err != nil { ret.Port = def.Port } - if ret.SessionName, err = db.GetValue(siteConf, "session-name"); err != nil { + if ret.SessionName, err = db.bolt.GetValue(siteConf, "session-name"); err != nil { ret.SessionName = def.SessionName } - if ret.ServerDir, err = db.GetValue(siteConf, "server-dir"); err != nil { + if ret.ServerDir, err = db.bolt.GetValue(siteConf, "server-dir"); err != nil { ret.ServerDir = def.ServerDir } return ret } -func dbSaveSiteConfig(dat *siteData) error { - var err error - if err = openDatabase(); err != nil { - return err - } - defer closeDatabase() - - siteConf := []string{"site"} - if err = db.SetValue(siteConf, "title", dat.Title); err != nil { - return err - } - if err = db.SetInt(siteConf, "port", dat.Port); err != nil { - return err - } - if err = db.SetValue(siteConf, "session-name", dat.SessionName); err != nil { - return err - } - return db.SetValue(siteConf, "server-dir", dat.ServerDir) -} - -func dbGetAuthMode() int { - if ret, err := db.GetInt([]string{"site"}, "auth-mode"); err != nil { +func (db *gjDatabase) getAuthMode() int { + if ret, err := db.bolt.GetInt([]string{"site"}, "auth-mode"); err != nil { return AuthModeAuthentication } else { return ret } } -func dbSetAuthMode(mode int) error { +func (db *gjDatabase) setAuthMode(mode int) error { if mode < 0 || mode >= AuthModeError { return errors.New("Invalid site mode") } - return db.SetInt([]string{"site"}, "auth-mode", mode) + return db.bolt.SetInt([]string{"site"}, "auth-mode", mode) } -func dbGetPublicSiteMode() int { - if ret, err := db.GetInt([]string{"site"}, "public-mode"); err != nil { +func (db *gjDatabase) getPublicSiteMode() int { + if ret, err := db.bolt.GetInt([]string{"site"}, "public-mode"); err != nil { return SiteModeWaiting } else { return ret @@ -177,5 +146,5 @@ func dbSetPublicSiteMode(mode int) error { if mode < 0 || mode >= SiteModeError { return errors.New("Invalid site mode") } - return db.SetInt([]string{"site"}, "public-mode", mode) + return db.bolt.SetInt([]string{"site"}, "public-mode", mode) } diff --git a/model_clients.go b/model_clients.go index fe95ec1..b8ebbf3 100644 --- a/model_clients.go +++ b/model_clients.go @@ -1,5 +1,12 @@ package main +import ( + "fmt" + "strconv" + "strings" + "time" +) + type Client struct { UUID string Auth bool @@ -7,49 +14,49 @@ type Client struct { IP string } -func dbGetAllClients() []Client { +func (db *gjDatabase) getAllClients() []Client { var ret []Client var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return ret } - defer db.CloseDB() + defer db.close() var clientUids []string - if clientUids, err = db.GetBucketList([]string{"clients"}); err != nil { + if clientUids, err = db.bolt.GetBucketList([]string{"clients"}); err != nil { return ret } for _, v := range clientUids { - if cl := dbGetClient(v); cl != nil { + if cl := db.getClient(v); cl != nil { ret = append(ret, *cl) } } return ret } -func dbGetClient(id string) *Client { +func (db *gjDatabase) getClient(id string) *Client { var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return nil } - defer db.CloseDB() + defer db.close() cl := new(Client) cl.UUID = id - cl.Auth = dbClientIsAuth(id) - cl.Name, _ = db.GetValue([]string{"clients", id}, "name") - cl.IP, _ = db.GetValue([]string{"clients", id}, "ip") + cl.Auth, _ = db.bolt.GetBool([]string{"clients", id}, "auth") + cl.Name, _ = db.bolt.GetValue([]string{"clients", id}, "name") + cl.IP, _ = db.bolt.GetValue([]string{"clients", id}, "ip") return cl } -func dbGetClientByIp(ip string) *Client { +func (db *gjDatabase) getClientByIp(ip string) *Client { var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return nil } - defer db.CloseDB() + defer db.close() - allClients := dbGetAllClients() + allClients := db.getAllClients() for i := range allClients { if allClients[i].IP == ip { return &allClients[i] @@ -58,85 +65,90 @@ func dbGetClientByIp(ip string) *Client { return nil } -func dbSetClientName(cid, name string) error { +func (c *Client) save() error { var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return nil } - defer db.CloseDB() + defer db.close() - err = db.SetValue([]string{"clients", cid}, "name", name) + if err = db.bolt.SetBool([]string{"clients", c.UUID}, "auth", c.Auth); err != nil { + return err + } + if err = db.bolt.SetValue([]string{"clients", c.UUID}, "name", c.Name); err != nil { + return err + } + return db.bolt.SetValue([]string{"clients", c.UUID}, "ip", c.IP) +} + +func (c *Client) getVotes() []Vote { + var ret []Vote + var err error + if err = db.open(); err != nil { + return ret + } + defer db.close() + + var times []string + votesBkt := []string{"votes", c.UUID} + if times, err = db.bolt.GetBucketList(votesBkt); err != nil { + return ret + } + for _, t := range times { + var tm time.Time + if tm, err = time.Parse(time.RFC3339, t); err == nil { + var vt *Vote + if vt, err = c.getVote(tm); err == nil { + ret = append(ret, *vt) + } else { + fmt.Println(err) + } + } + } + return ret +} + +func (c *Client) getVote(timestamp time.Time) (*Vote, error) { + var err error + if err = db.open(); err != nil { + return nil, err + } + defer db.close() + + vt := new(Vote) + vt.Timestamp = timestamp + vt.ClientId = c.UUID + votesBkt := []string{"votes", c.UUID, timestamp.Format(time.RFC3339)} + var choices []string + if choices, err = db.bolt.GetKeyList(votesBkt); err != nil { + // Couldn't find the vote... + return nil, err + } + for _, v := range choices { + ch := new(GameChoice) + var rank int + + if rank, err = strconv.Atoi(v); err == nil { + ch.Rank = rank + ch.Team, err = db.bolt.GetValue(votesBkt, v) + vt.Choices = append(vt.Choices, *ch) + } + } + return vt, nil +} + +func (c *Client) saveVote(timestamp time.Time, votes []string) error { + var err error + if err = db.open(); err != nil { + return nil + } + defer db.close() + // Make sure we don't clobber a duplicate vote + votesBkt := []string{"votes", c.UUID, timestamp.Format(time.RFC3339)} + for i := range votes { + if strings.TrimSpace(votes[i]) != "" { + db.bolt.SetValue(votesBkt, strconv.Itoa(i), votes[i]) + } + } return err } - -func dbGetClientName(cid string) string { - if err := db.OpenDB(); err != nil { - return "" - } - defer db.CloseDB() - - name, _ := db.GetValue([]string{"clients", cid}, "name") - return name -} - -func dbAddDeauthClient(cid, ip string) error { - var err error - if err = db.OpenDB(); err != nil { - return err - } - defer db.CloseDB() - - err = db.SetBool([]string{"clients", cid}, "auth", false) - if err != nil { - return err - } - return db.SetValue([]string{"clients", cid}, "ip", ip) -} - -func dbAuthClient(cid, ip string) error { - var err error - if err = db.OpenDB(); err != nil { - return err - } - defer db.CloseDB() - - err = db.SetBool([]string{"clients", cid}, "auth", true) - if err != nil { - return err - } - return db.SetValue([]string{"clients", cid}, "ip", ip) -} - -func dbDeAuthClient(cid string) error { - var err error - if err = db.OpenDB(); err != nil { - return err - } - defer db.CloseDB() - - return db.SetBool([]string{"clients", cid}, "auth", false) -} - -func dbClientIsAuth(cid string) bool { - var err error - if err = db.OpenDB(); err != nil { - return false - } - defer db.CloseDB() - - var isAuth bool - if isAuth, err = db.GetBool([]string{"clients", cid}, "auth"); err != nil { - return false - } - return isAuth -} - -func dbUpdateClientIP(cid, ip string) error { - var err error - if err = db.OpenDB(); err != nil { - return err - } - defer db.CloseDB() - - return db.SetValue([]string{"clients", cid}, "ip", ip) -} diff --git a/model_games.go b/model_games.go index 1a1a7e4..d0fddbf 100644 --- a/model_games.go +++ b/model_games.go @@ -1,10 +1,6 @@ package main -import ( - "errors" - - "github.com/pborman/uuid" -) +import "errors" type Game struct { Name string @@ -22,150 +18,61 @@ type Screenshot struct { Filetype string } -func dbUpdateTeamGame(teamId, name, link, desc string) error { +// Create a new game object, must have a valid team id +func newGame(tmId string) *Game { var err error - if err = openDatabase(); err != nil { - return err + if err = db.open(); err != nil { + return nil } - defer closeDatabase() + defer db.close() - // Make sure the team is valid - tm := dbGetTeam(teamId) + tm := db.getTeam(tmId) if tm == nil { - return errors.New("Invalid team") + return nil } - gamePath := []string{"teams", teamId, "game"} + return &Game{TeamId: tmId} +} - if err := db.MkBucketPath(gamePath); err != nil { +func (gm *Game) save() error { + var err error + if err = db.open(); err != nil { return err } - if name == "" { - name = tm.Name + "'s Game" + defer db.close() + + tm := db.getTeam(gm.TeamId) + if tm == nil { + return errors.New("Invalid Team: " + gm.TeamId) } - if err := db.SetValue(gamePath, "name", name); err != nil { + gamePath := []string{"teams", gm.TeamId, "game"} + if err := db.bolt.MkBucketPath(gamePath); err != nil { return err } - if err := db.SetValue(gamePath, "link", link); err != nil { + + if gm.Name == "" { + gm.Name = tm.Name + "'s Game" + } + if err := db.bolt.SetValue(gamePath, "name", gm.Name); err != nil { return err } - if err := db.SetValue(gamePath, "description", desc); err != nil { + if err := db.bolt.SetValue(gamePath, "link", gm.Link); err != nil { return err } - if err := db.MkBucketPath(append(gamePath, "screenshots")); err != nil { + if err := db.bolt.SetValue(gamePath, "description", gm.Description); err != nil { + return err + } + if err := db.bolt.MkBucketPath(append(gamePath, "screenshots")); err != nil { return err } return err } -func dbGetAllGames() []Game { +func (db *gjDatabase) getAllGames() []Game { var ret []Game - tms := dbGetAllTeams() + tms := db.getAllTeams() for i := range tms { - ret = append(ret, *dbGetTeamGame(tms[i].UUID)) + ret = append(ret, *tms[i].getGame()) } return ret } - -func dbGetTeamGame(teamId string) *Game { - var err error - if err = openDatabase(); err != nil { - return nil - } - defer closeDatabase() - - gamePath := []string{"teams", teamId, "game"} - gm := new(Game) - if gm.Name, err = db.GetValue(gamePath, "name"); err != nil { - gm.Name = "" - } - gm.TeamId = teamId - if gm.Description, err = db.GetValue(gamePath, "description"); err != nil { - gm.Description = "" - } - if gm.Link, err = db.GetValue(gamePath, "link"); err != nil { - gm.Link = "" - } - gm.Screenshots = dbGetTeamGameScreenshots(teamId) - return gm -} - -// Screenshots are saved as base64 encoded pngs -func dbGetTeamGameScreenshots(teamId string) []Screenshot { - var ret []Screenshot - var err error - ssPath := []string{"teams", teamId, "game", "screenshots"} - var ssIds []string - if ssIds, err = db.GetBucketList(ssPath); err != nil { - return ret - } - for _, v := range ssIds { - if ss := dbGetTeamGameScreenshot(teamId, v); ss != nil { - ret = append(ret, *ss) - } - } - return ret -} - -func dbGetTeamGameScreenshot(teamId, ssId string) *Screenshot { - var err error - ssPath := []string{"teams", teamId, "game", "screenshots", ssId} - ret := new(Screenshot) - ret.UUID = ssId - if ret.Description, err = db.GetValue(ssPath, "description"); err != nil { - return nil - } - if ret.Image, err = db.GetValue(ssPath, "image"); err != nil { - return nil - } - if ret.Thumbnail, err = db.GetValue(ssPath, "thumbnail"); err != nil { - return nil - } - if ret.Thumbnail == "" { - ret.Thumbnail = ret.Image - } - if ret.Filetype, err = db.GetValue(ssPath, "filetype"); err != nil { - return nil - } - return ret -} - -func dbSaveTeamGameScreenshot(teamId string, ss *Screenshot) error { - var err error - if err = openDatabase(); err != nil { - return nil - } - defer closeDatabase() - - ssPath := []string{"teams", teamId, "game", "screenshots"} - // Generate a UUID for this screenshot - uuid := uuid.New() - ssPath = append(ssPath, uuid) - if err := db.MkBucketPath(ssPath); err != nil { - return err - } - if err := db.SetValue(ssPath, "description", ss.Description); err != nil { - return err - } - if err := db.SetValue(ssPath, "image", ss.Image); err != nil { - return err - } - if err := db.SetValue(ssPath, "thumbnail", ss.Thumbnail); err != nil { - return err - } - if err := db.SetValue(ssPath, "filetype", ss.Filetype); err != nil { - return err - } - return nil -} - -func dbDeleteTeamGameScreenshot(teamId, ssId string) error { - var err error - if err = openDatabase(); err != nil { - return nil - } - defer closeDatabase() - - ssPath := []string{"teams", teamId, "game", "screenshots"} - return db.DeleteBucket(ssPath, ssId) -} diff --git a/model_site.go b/model_site.go new file mode 100644 index 0000000..2a0125f --- /dev/null +++ b/model_site.go @@ -0,0 +1,62 @@ +package main + +const ( + SiteModeWaiting = iota + SiteModeVoting + SiteModeError +) + +// SiteData is stuff that stays the same +type siteData struct { + Title string + Port int + SessionName string + ServerDir string + DevMode bool + Mode int + + CurrentJam string + + Teams []Team + Votes []Vote +} + +// NewSiteData returns a siteData object with the default values +func NewSiteData() *siteData { + ret := new(siteData) + ret.Title = "ICT GameJam" + ret.Port = 8080 + ret.SessionName = "ict-gamejam" + ret.ServerDir = "./" + return ret +} + +func (s *siteData) getTeamByUUID(uuid string) *Team { + for i := range s.Teams { + if s.Teams[i].UUID == uuid { + return &s.Teams[i] + } + } + return nil +} + +// save 's' to the database +func (s *siteData) save() error { + var err error + if err = db.open(); err != nil { + return err + } + defer db.close() + + siteConf := []string{"site"} + if err = db.bolt.SetValue(siteConf, "title", s.Title); err != nil { + return err + } + if err = db.bolt.SetInt(siteConf, "port", s.Port); err != nil { + return err + } + if err = db.bolt.SetValue(siteConf, "session-name", s.SessionName); err != nil { + return err + } + return db.bolt.SetValue(siteConf, "server-dir", s.ServerDir) +} diff --git a/model_teams.go b/model_teams.go index e116337..5f95935 100644 --- a/model_teams.go +++ b/model_teams.go @@ -13,250 +13,68 @@ type Team struct { Game *Game } -type TeamMember struct { - UUID string - Name string - SlackId string - Twitter string - Email string -} - -func dbCreateNewTeam(nm string) error { +// newTeam creates a team with name nm and stores it in the DB +func (db *gjDatabase) newTeam(nm string) error { var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return err } - defer closeDatabase() + defer db.close() // Generate a UUID uuid := uuid.New() teamPath := []string{"teams", uuid} - if err := db.MkBucketPath(teamPath); err != nil { + if err := db.bolt.MkBucketPath(teamPath); err != nil { return err } - if err := db.SetValue(teamPath, "name", nm); err != nil { + if err := db.bolt.SetValue(teamPath, "name", nm); err != nil { return err } - if err := db.MkBucketPath(append(teamPath, "members")); err != nil { + if err := db.bolt.MkBucketPath(append(teamPath, "members")); err != nil { return err } gamePath := append(teamPath, "game") - if err := db.MkBucketPath(gamePath); err != nil { + if err := db.bolt.MkBucketPath(gamePath); err != nil { return err } - if err := db.SetValue(append(gamePath), "name", ""); err != nil { + if err := db.bolt.SetValue(append(gamePath), "name", ""); err != nil { return err } - return db.MkBucketPath(append(gamePath, "screenshots")) + return db.bolt.MkBucketPath(append(gamePath, "screenshots")) } -func dbIsValidTeam(id string) bool { +// getTeam returns a team with the given id, or nil +func (db *gjDatabase) getTeam(id string) *Team { var err error - if err = openDatabase(); err != nil { - return false - } - defer closeDatabase() - - teamPath := []string{"teams"} - if teamUids, err := db.GetBucketList(teamPath); err == nil { - for _, v := range teamUids { - if v == id { - return true - } - } - } - return false -} - -func dbGetAllTeams() []Team { - var ret []Team - var err error - if err = openDatabase(); err != nil { - return ret - } - defer closeDatabase() - - teamPath := []string{"teams"} - var teamUids []string - if teamUids, err = db.GetBucketList(teamPath); err != nil { - return ret - } - for _, v := range teamUids { - if tm := dbGetTeam(v); tm != nil { - ret = append(ret, *tm) - } - } - return ret -} - -func dbGetTeam(id string) *Team { - var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return nil } - defer closeDatabase() + defer db.close() teamPath := []string{"teams", id} tm := new(Team) tm.UUID = id - if tm.Name, err = db.GetValue(teamPath, "name"); err != nil { + if tm.Name, err = db.bolt.GetValue(teamPath, "name"); err != nil { return nil } - tm.Members, _ = dbGetTeamMembers(id) - tm.Game = dbGetTeamGame(id) + tm.Members = tm.getTeamMembers() + tm.Game = tm.getGame() return tm } -func dbGetTeamByName(nm string) *Team { - var err error - if err = openDatabase(); err != nil { - return nil - } - defer closeDatabase() - - teamPath := []string{"teams"} - var teamUids []string - if teamUids, err = db.GetBucketList(teamPath); err != nil { - for _, v := range teamUids { - var name string - if name, err = db.GetValue(append(teamPath, v), "name"); name == nm { - return dbGetTeam(v) - } - } - } - return nil -} - -func dbUpdateTeam(id string, tm *Team) error { - var err error - if err = openDatabase(); err != nil { - return err - } - defer closeDatabase() - - teamPath := []string{"teams", id} - return db.SetValue(teamPath, "name", tm.Name) -} - -func dbDeleteTeam(id string) error { - var err error - if err = openDatabase(); err != nil { - return err - } - defer closeDatabase() - - teamPath := []string{"teams"} - return db.DeleteBucket(teamPath, id) -} - -func dbEditTeamGame(teamid, name string) error { - var err error - if err = openDatabase(); err != nil { - return err - } - defer closeDatabase() - - gamePath := []string{"teams", teamid, "game"} - return db.SetValue(gamePath, "name", name) -} - -func dbAddTeamMember(teamid, mbrName, mbrEmail, mbrSlack, mbrTwitter string) error { - // First check if this member already exists on this team - mbrs, _ := dbGetTeamMembers(teamid) - if len(mbrs) > 0 { - for i := range mbrs { - if mbrs[i].Name == mbrName { - return dbUpdateTeamMember(teamid, mbrs[i].UUID, mbrName, mbrEmail, mbrSlack, mbrTwitter) - } - } - } - // It's really an add - mbrId := uuid.New() - return dbUpdateTeamMember(teamid, mbrId, mbrName, mbrEmail, mbrSlack, mbrTwitter) -} - -func dbUpdateTeamMember(teamid, mbrId, mbrName, mbrEmail, mbrSlack, mbrTwitter string) error { - var err error - if err = openDatabase(); err != nil { - return err - } - defer closeDatabase() - - mbrPath := []string{"teams", teamid, "members", mbrId} - if db.SetValue(mbrPath, "name", mbrName) != nil { - return err - } - if db.SetValue(mbrPath, "slackid", mbrSlack) != nil { - return err - } - if db.SetValue(mbrPath, "twitter", mbrTwitter) != nil { - return err - } - if db.SetValue(mbrPath, "email", mbrEmail) != nil { - return err - } - return nil -} - -func dbGetTeamMembers(teamid string) ([]TeamMember, error) { - var ret []TeamMember - var err error - if err = openDatabase(); err != nil { - return ret, err - } - defer closeDatabase() - - teamPath := []string{"teams", teamid, "members"} - var memberUuids []string - if memberUuids, err = db.GetBucketList(teamPath); err == nil { - for _, v := range memberUuids { - var mbr *TeamMember - if mbr, err = dbGetTeamMember(teamid, v); err == nil { - ret = append(ret, *mbr) - } - } - } - return ret, err -} - -func dbGetTeamMember(teamid, mbrid string) (*TeamMember, error) { - var err error - if err = openDatabase(); err != nil { - return nil, err - } - defer closeDatabase() - - mbr := new(TeamMember) - teamMbrPath := []string{"teams", teamid, "members", mbrid} - mbr.UUID = mbrid - if mbr.Name, err = db.GetValue(teamMbrPath, "name"); err != nil { - return nil, err - } - if mbr.SlackId, err = db.GetValue(teamMbrPath, "slackid"); err != nil { - return nil, err - } - if mbr.Twitter, err = db.GetValue(teamMbrPath, "twitter"); err != nil { - return nil, err - } - if mbr.Email, err = db.GetValue(teamMbrPath, "email"); err != nil { - return nil, err - } - return mbr, err -} - // This function returns the team for a specific member -func dbGetMembersTeam(mbrid string) (*Team, error) { +func (db *gjDatabase) getTeamForMember(mbrid string) (*Team, error) { var err error - if err = openDatabase(); err != nil { + if err = db.open(); err != nil { return nil, err } - defer closeDatabase() + defer db.close() - teams := dbGetAllTeams() + teams := db.getAllTeams() for i := range teams { var tmMbrs []TeamMember - tmMbrs, err = dbGetTeamMembers(teams[i].UUID) + tmMbrs = teams[i].getTeamMembers() if err == nil { for j := range tmMbrs { if tmMbrs[j].UUID == mbrid { @@ -268,40 +86,289 @@ func dbGetMembersTeam(mbrid string) (*Team, error) { return nil, errors.New("Unable to find team member") } -// This function searches all teams for a member with the given name -func dbGetTeamMembersByName(mbrName string) ([]TeamMember, error) { +// getAllTeams returns all teams in the database +func (db *gjDatabase) getAllTeams() []Team { + var ret []Team + var err error + if err = db.open(); err != nil { + return ret + } + defer db.close() + + teamPath := []string{"teams"} + var teamUids []string + if teamUids, err = db.bolt.GetBucketList(teamPath); err != nil { + return ret + } + for _, v := range teamUids { + if tm := db.getTeam(v); tm != nil { + ret = append(ret, *tm) + } + } + return ret +} + +// getTeamByName returns a team with the given name or nil +func (db *gjDatabase) getTeamByName(nm string) *Team { + var err error + if err = db.open(); err != nil { + return nil + } + defer db.close() + + teamPath := []string{"teams"} + var teamUids []string + if teamUids, err = db.bolt.GetBucketList(teamPath); err != nil { + for _, v := range teamUids { + var name string + if name, err = db.bolt.GetValue(append(teamPath, v), "name"); name == nm { + return db.getTeam(v) + } + } + } + return nil +} + +// save saves the team to the db +func (tm *Team) save() error { + var err error + if err = db.open(); err != nil { + return err + } + defer db.close() + + teamPath := []string{"teams", tm.UUID} + if err = db.bolt.SetValue(teamPath, "name", tm.Name); err != nil { + return err + } + + // TODO: Save Team Members + // TODO: Save Team Game + return nil +} + +// delete removes the team from the database +func (tm *Team) delete() error { + var err error + if err = db.open(); err != nil { + return err + } + defer db.close() + + teamPath := []string{"teams"} + return db.bolt.DeleteBucket(teamPath, tm.UUID) +} + +func (tm *Team) getGame() *Game { + var err error + if err = db.open(); err != nil { + return nil + } + defer db.close() + + gamePath := []string{"teams", tm.UUID, "game"} + gm := new(Game) + if gm.Name, err = db.bolt.GetValue(gamePath, "name"); err != nil { + gm.Name = "" + } + gm.TeamId = tm.UUID + if gm.Description, err = db.bolt.GetValue(gamePath, "description"); err != nil { + gm.Description = "" + } + if gm.Link, err = db.bolt.GetValue(gamePath, "link"); err != nil { + gm.Link = "" + } + gm.Screenshots = tm.getScreenshots() + return gm +} + +// Screenshots are saved as base64 encoded pngs +func (tm *Team) saveScreenshot(ss *Screenshot) error { + var err error + if err = db.open(); err != nil { + return nil + } + defer db.close() + + ssPath := []string{"teams", tm.UUID, "game", "screenshots"} + // Generate a UUID for this screenshot + uuid := uuid.New() + ssPath = append(ssPath, uuid) + if err := db.bolt.MkBucketPath(ssPath); err != nil { + return err + } + if err := db.bolt.SetValue(ssPath, "description", ss.Description); err != nil { + return err + } + if err := db.bolt.SetValue(ssPath, "image", ss.Image); err != nil { + return err + } + if err := db.bolt.SetValue(ssPath, "thumbnail", ss.Thumbnail); err != nil { + return err + } + if err := db.bolt.SetValue(ssPath, "filetype", ss.Filetype); err != nil { + return err + } + return nil +} + +func (tm *Team) getScreenshots() []Screenshot { + var ret []Screenshot + var err error + ssPath := []string{"teams", tm.UUID, "game", "screenshots"} + var ssIds []string + if ssIds, err = db.bolt.GetBucketList(ssPath); err != nil { + return ret + } + for _, v := range ssIds { + if ss := tm.getScreenshot(v); ss != nil { + ret = append(ret, *ss) + } + } + return ret +} + +func (tm *Team) getScreenshot(ssId string) *Screenshot { + var err error + ssPath := []string{"teams", tm.UUID, "game", "screenshots", ssId} + ret := new(Screenshot) + ret.UUID = ssId + if ret.Description, err = db.bolt.GetValue(ssPath, "description"); err != nil { + return nil + } + if ret.Image, err = db.bolt.GetValue(ssPath, "image"); err != nil { + return nil + } + if ret.Thumbnail, err = db.bolt.GetValue(ssPath, "thumbnail"); err != nil { + return nil + } + if ret.Thumbnail == "" { + ret.Thumbnail = ret.Image + } + if ret.Filetype, err = db.bolt.GetValue(ssPath, "filetype"); err != nil { + return nil + } + return ret +} + +func (tm *Team) deleteScreenshot(ssId string) error { + var err error + if err = db.open(); err != nil { + return nil + } + defer db.close() + + ssPath := []string{"teams", tm.UUID, "game", "screenshots"} + return db.bolt.DeleteBucket(ssPath, ssId) +} + +type TeamMember struct { + UUID string + Name string + SlackId string + Twitter string + Email string +} + +// Create a new team member, only a name is required +func newTeamMember(nm string) *TeamMember { + m := TeamMember{Name: nm} + return &m +} + +func (tm *Team) getTeamMember(mbrId string) *TeamMember { + var err error + if err = db.open(); err != nil { + return nil + } + defer db.close() + + mbr := new(TeamMember) + mbr.UUID = mbrId + teamMbrPath := []string{"teams", tm.UUID, "members", mbr.UUID} + if mbr.Name, err = db.bolt.GetValue(teamMbrPath, "name"); err != nil { + return nil + } + if mbr.SlackId, err = db.bolt.GetValue(teamMbrPath, "slackid"); err != nil { + return nil + } + if mbr.Twitter, err = db.bolt.GetValue(teamMbrPath, "twitter"); err != nil { + return nil + } + if mbr.Email, err = db.bolt.GetValue(teamMbrPath, "email"); err != nil { + return nil + } + return mbr +} + +func (tm *Team) getTeamMembers() []TeamMember { var ret []TeamMember var err error - if err = openDatabase(); err != nil { - return ret, err + if err = db.open(); err != nil { + return ret } - defer closeDatabase() + defer db.close() - teams := dbGetAllTeams() - for i := range teams { - var tmMbrs []TeamMember - tmMbrs, err = dbGetTeamMembers(teams[i].UUID) - if err == nil { - for j := range tmMbrs { - if tmMbrs[j].Name == mbrName { - ret = append(ret, tmMbrs[j]) + teamPath := []string{"teams", tm.UUID, "members"} + var memberUuids []string + if memberUuids, err = db.bolt.GetBucketList(teamPath); err == nil { + for _, v := range memberUuids { + var mbr *TeamMember + if mbr = tm.getTeamMember(v); mbr != nil { + ret = append(ret, *mbr) + } + } + } + return ret +} + +func (tm *Team) updateTeamMember(mbr *TeamMember) error { + var err error + if err = db.open(); err != nil { + return err + } + defer db.close() + + if mbr.UUID == "" { + mbrs := tm.getTeamMembers() + if len(mbrs) > 0 { + for i := range mbrs { + if mbrs[i].Name == mbr.Name { + mbr.UUID = mbrs[i].UUID + break } } } } - if len(ret) == 0 { - return nil, errors.New("Couldn't find any members with the requested name") + if mbr.UUID == "" { + // It's really a new one + mbr.UUID = uuid.New() } - return ret, nil -} -func dbDeleteTeamMember(teamId, mbrId string) error { - var err error - if err = openDatabase(); err != nil { + mbrPath := []string{"teams", tm.UUID, "members", mbr.UUID} + if db.bolt.SetValue(mbrPath, "name", mbr.Name) != nil { return err } - defer closeDatabase() - - teamPath := []string{"teams", teamId, "members"} - return db.DeleteBucket(teamPath, mbrId) + if db.bolt.SetValue(mbrPath, "slackid", mbr.SlackId) != nil { + return err + } + if db.bolt.SetValue(mbrPath, "twitter", mbr.Twitter) != nil { + return err + } + if db.bolt.SetValue(mbrPath, "email", mbr.Email) != nil { + return err + } + return nil +} + +// deleteTeamMember removes a member from the database +func (tm *Team) deleteTeamMember(mbr *TeamMember) error { + var err error + if err = db.open(); err != nil { + return err + } + defer db.close() + + teamPath := []string{"teams", tm.UUID, "members"} + return db.bolt.DeleteBucket(teamPath, mbr.UUID) } diff --git a/model_users.go b/model_users.go index dfbfa68..bcd051c 100644 --- a/model_users.go +++ b/model_users.go @@ -4,73 +4,73 @@ import "golang.org/x/crypto/bcrypt" // dbHasUser // Returns true if there are any users in the database -func dbHasUser() bool { - return len(dbGetAllUsers()) > 0 +func (db *gjDatabase) hasUser() bool { + return len(db.getAllUsers()) > 0 } -func dbGetAllUsers() []string { - if err := db.OpenDB(); err != nil { +func (db *gjDatabase) getAllUsers() []string { + if err := db.open(); err != nil { return []string{} } - defer db.CloseDB() + defer db.close() - usrs, err := db.GetBucketList([]string{"users"}) + usrs, err := db.bolt.GetBucketList([]string{"users"}) if err != nil { return []string{} } return usrs } -func dbIsValidUserEmail(email string) bool { - if err := db.OpenDB(); err != nil { +func (db *gjDatabase) isValidUserEmail(email string) bool { + if err := db.open(); err != nil { return false } - defer db.CloseDB() + defer db.close() usrPath := []string{"users", email} - _, err := db.GetValue(usrPath, "password") + _, err := db.bolt.GetValue(usrPath, "password") return err == nil } -func dbCheckCredentials(email, pw string) error { +func (db *gjDatabase) checkCredentials(email, pw string) error { var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return err } - defer db.CloseDB() + defer db.close() var uPw string usrPath := []string{"users", email} - if uPw, err = db.GetValue(usrPath, "password"); err != nil { + if uPw, err = db.bolt.GetValue(usrPath, "password"); err != nil { return err } return bcrypt.CompareHashAndPassword([]byte(uPw), []byte(pw)) } -// dbUpdateUserPassword +// updateUserPassword // Takes an email address and a password // Creates the user if it doesn't exist, encrypts the password // and updates it in the db -func dbUpdateUserPassword(email, password string) error { +func (db *gjDatabase) updateUserPassword(email, password string) error { cryptPw, cryptError := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if cryptError != nil { return cryptError } - if err := db.OpenDB(); err != nil { + if err := db.open(); err != nil { return err } - defer db.CloseDB() + defer db.close() usrPath := []string{"users", email} - return db.SetValue(usrPath, "password", string(cryptPw)) + return db.bolt.SetValue(usrPath, "password", string(cryptPw)) } -func dbDeleteUser(email string) error { +func (db *gjDatabase) deleteUser(email string) error { var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return err } - defer db.CloseDB() + defer db.close() - return db.DeleteBucket([]string{"users"}, email) + return db.bolt.DeleteBucket([]string{"users"}, email) } diff --git a/model_votes.go b/model_votes.go index 8565d25..f5520e6 100644 --- a/model_votes.go +++ b/model_votes.go @@ -1,11 +1,6 @@ package main -import ( - "fmt" - "strconv" - "strings" - "time" -) +import "time" // A Choice is a ranking of a game in a vote type GameChoice struct { @@ -20,94 +15,17 @@ type Vote struct { Choices []GameChoice } -func dbGetAllVotes() []Vote { +func (db *gjDatabase) getAllVotes() []Vote { var ret []Vote var err error - if err = db.OpenDB(); err != nil { + if err = db.open(); err != nil { return ret } - defer db.CloseDB() + defer db.close() - votesBkt := []string{"votes"} - var clients []string - if clients, err = db.GetBucketList(votesBkt); err != nil { - // Couldn't get the list of clients - return ret - } - for _, clid := range clients { - ret = append(ret, dbGetClientVotes(clid)...) + clients := db.getAllClients() + for _, cl := range clients { + ret = append(ret, cl.getVotes()...) } return ret } - -func dbGetClientVotes(clientId string) []Vote { - var ret []Vote - var err error - if err = db.OpenDB(); err != nil { - return ret - } - defer db.CloseDB() - - var times []string - votesBkt := []string{"votes", clientId} - if times, err = db.GetBucketList(votesBkt); err != nil { - return ret - } - for _, t := range times { - var tm time.Time - if tm, err = time.Parse(time.RFC3339, t); err == nil { - var vt *Vote - if vt, err = dbGetVote(clientId, tm); err == nil { - ret = append(ret, *vt) - } else { - fmt.Println(err) - } - } - } - return ret -} - -func dbGetVote(clientId string, timestamp time.Time) (*Vote, error) { - var err error - if err = db.OpenDB(); err != nil { - return nil, err - } - defer db.CloseDB() - - vt := new(Vote) - vt.Timestamp = timestamp - vt.ClientId = clientId - votesBkt := []string{"votes", clientId, timestamp.Format(time.RFC3339)} - var choices []string - if choices, err = db.GetKeyList(votesBkt); err != nil { - // Couldn't find the vote... - return nil, err - } - for _, v := range choices { - ch := new(GameChoice) - var rank int - - if rank, err = strconv.Atoi(v); err == nil { - ch.Rank = rank - ch.Team, err = db.GetValue(votesBkt, v) - vt.Choices = append(vt.Choices, *ch) - } - } - return vt, nil -} - -func dbSaveVote(clientId string, timestamp time.Time, votes []string) error { - var err error - if err = db.OpenDB(); err != nil { - return nil - } - defer db.CloseDB() - // Make sure we don't clobber a duplicate vote - votesBkt := []string{"votes", clientId, timestamp.Format(time.RFC3339)} - for i := range votes { - if strings.TrimSpace(votes[i]) != "" { - db.SetValue(votesBkt, strconv.Itoa(i), votes[i]) - } - } - return err -} diff --git a/page_session.go b/page_session.go index b6fbc05..99c99ff 100644 --- a/page_session.go +++ b/page_session.go @@ -43,7 +43,7 @@ func (p *pageSession) getClientId() string { fmt.Println(" Client IP:" + clientIp) if clientIp != "127.0.0.1" { fmt.Println(" Pulling data by IP") - cli = dbGetClientByIp(clientIp) + cli = db.getClientByIp(clientIp) } if cli != nil { clientId = cli.UUID diff --git a/public_endpoints.go b/public_endpoints.go index 78cf365..5d31c4a 100644 --- a/public_endpoints.go +++ b/public_endpoints.go @@ -17,7 +17,7 @@ func initPublicPage(w http.ResponseWriter, req *http.Request) *pageData { func handleMain(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) - if dbGetPublicSiteMode() == SiteModeWaiting { + if db.getPublicSiteMode() == SiteModeWaiting { page.SubTitle = "" page.show("public-waiting.html", w) } else { @@ -28,7 +28,7 @@ func handleMain(w http.ResponseWriter, req *http.Request) { func loadVotingPage(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) // Client authentication required - if (dbGetAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { + if (db.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { page.show("unauthorized.html", w) return } @@ -37,7 +37,7 @@ func loadVotingPage(w http.ResponseWriter, req *http.Request) { Timestamp string } vpd := new(votingPageData) - tms := dbGetAllTeams() + tms := db.getAllTeams() // Randomize the team list rand.Seed(time.Now().Unix()) @@ -55,7 +55,7 @@ func loadVotingPage(w http.ResponseWriter, req *http.Request) { func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) // Client authentication required - if (dbGetAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { + if (db.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { page.show("unauthorized.html", w) return } @@ -69,7 +69,8 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { page.session.setFlashMessage("Error parsing timestamp: "+ts, "error") redirect("/", w, req) } - if _, err := dbGetVote(page.ClientId, timestamp); err == nil { + client := db.getClient(page.ClientId) + if _, err := client.getVote(timestamp); err == nil { // Duplicate vote... Cancel it. page.session.setFlashMessage("Duplicate vote!", "error") redirect("/", w, req) @@ -77,10 +78,10 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { // voteSlice is an ordered string slice of the voters preferences voteCSV := req.FormValue("uservote") voteSlice := strings.Split(voteCSV, ",") - if err := dbSaveVote(page.ClientId, timestamp, voteSlice); err != nil { + if err := client.saveVote(timestamp, voteSlice); err != nil { page.session.setFlashMessage("Error Saving Vote: "+err.Error(), "error") } - if newVote, err := dbGetVote(page.ClientId, timestamp); err == nil { + if newVote, err := client.getVote(timestamp); err == nil { site.Votes = append(site.Votes, *newVote) } page.session.setFlashMessage("Vote Saved!", "success large fading") @@ -90,7 +91,12 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) { // Thumbnail requests are open even without client authentication vars := mux.Vars(req) - ss := dbGetTeamGameScreenshot(vars["teamid"], vars["imageid"]) + tm := db.getTeam(vars["teamid"]) + if tm == nil { + http.Error(w, "Couldn't find image", 404) + return + } + ss := tm.getScreenshot(vars["imageid"]) if ss == nil { http.Error(w, "Couldn't find image", 404) return @@ -107,7 +113,12 @@ func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) { func handleImageRequest(w http.ResponseWriter, req *http.Request) { // Image requests are open even without client authentication vars := mux.Vars(req) - ss := dbGetTeamGameScreenshot(vars["teamid"], vars["imageid"]) + tm := db.getTeam(vars["teamid"]) + if tm == nil { + http.Error(w, "Couldn't find image", 404) + return + } + ss := tm.getScreenshot(vars["imageid"]) if ss == nil { http.Error(w, "Couldn't find image", 404) return @@ -123,70 +134,72 @@ func handleImageRequest(w http.ResponseWriter, req *http.Request) { func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) { // Team Management pages are open even without client authentication - if dbGetPublicSiteMode() == SiteModeVoting { + if db.getPublicSiteMode() == SiteModeVoting { redirect("/", w, req) } page := initPublicPage(w, req) vars := mux.Vars(req) page.SubTitle = "Team Details" teamId := vars["id"] - if teamId != "" { + tm := db.getTeam(teamId) + if tm != nil { // Team self-management functions - if !dbIsValidTeam(teamId) { - http.Error(w, "Page Not Found", 404) - return - } switch vars["function"] { case "": page.SubTitle = "Team Management" - t := dbGetTeam(teamId) - page.TemplateData = t + page.TemplateData = tm page.show("public-teammgmt.html", w) case "savemember": - mbrName := req.FormValue("newmembername") - mbrSlack := req.FormValue("newmemberslackid") - mbrTwitter := req.FormValue("newmembertwitter") - mbrEmail := req.FormValue("newmemberemail") - if err := dbAddTeamMember(teamId, mbrName, mbrEmail, mbrSlack, mbrTwitter); err != nil { + m := newTeamMember(req.FormValue("newmembername")) + m.SlackId = req.FormValue("newmemberslackid") + m.Twitter = req.FormValue("newmembertwitter") + m.Email = req.FormValue("newmemberemail") + if err := tm.updateTeamMember(m); err != nil { page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") } else { - page.session.setFlashMessage(mbrName+" added to team!", "success") + page.session.setFlashMessage(m.Name+" added to team!", "success") } refreshTeamsInMemory() - redirect("/team/"+teamId+"#members", w, req) + redirect("/team/"+tm.UUID+"#members", w, req) case "deletemember": mbrId := req.FormValue("memberid") - m, _ := dbGetTeamMember(teamId, mbrId) - if err := dbDeleteTeamMember(teamId, mbrId); err != nil { - page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") + m := tm.getTeamMember(mbrId) + if m != nil { + if err := tm.deleteTeamMember(m); err != nil { + page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") + } else { + page.session.setFlashMessage(m.Name+" deleted from team", "success") + } } else { - page.session.setFlashMessage(m.Name+" deleted from team", "success") + page.session.setFlashMessage("Couldn't find member to delete", "error") } refreshTeamsInMemory() - redirect("/team/"+teamId, w, req) + redirect("/team/"+tm.UUID, w, req) case "savegame": - name := req.FormValue("gamename") - link := req.FormValue("gamelink") - desc := req.FormValue("gamedesc") - if dbIsValidTeam(teamId) { - if err := dbUpdateTeamGame(teamId, name, link, desc); err != nil { - page.session.setFlashMessage("Error updating game: "+err.Error(), "error") - } else { - page.session.setFlashMessage("Team game updated", "success") - } - redirect("/team/"+teamId, w, req) + gm := newGame(tm.UUID) + gm.Name = req.FormValue("gamename") + gm.Link = req.FormValue("gamelink") + gm.Description = req.FormValue("gamedesc") + if err := gm.save(); err != nil { + page.session.setFlashMessage("Error updating game: "+err.Error(), "error") + } else { + page.session.setFlashMessage("Team game updated", "success") } + redirect("/team/"+tm.UUID, w, req) case "screenshotupload": - if err := saveScreenshots(teamId, req); err != nil { + if err := saveScreenshots(tm, req); err != nil { page.session.setFlashMessage("Error updating game: "+err.Error(), "error") } - redirect("/team/"+teamId, w, req) + redirect("/team/"+tm.UUID, w, req) case "screenshotdelete": ssid := vars["subid"] - if err := dbDeleteTeamGameScreenshot(teamId, ssid); err != nil { + if err := tm.deleteScreenshot(ssid); err != nil { page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") } - redirect("/team/"+teamId, w, req) + redirect("/team/"+tm.UUID, w, req) } + } else { + http.Error(w, "Page Not Found", 404) + return } }