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/assets.go b/assets.go index 9efeb32..4fb2c8c 100644 --- a/assets.go +++ b/assets.go @@ -191,7 +191,7 @@ var _escData = map[string]*_escFile{ "/assets/css/admin.css": { local: "assets/css/admin.css", size: 291, - modtime: 1501270412, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/2TPwW7DIAwG4DtPYalnqnbTDkmueRECjFkDjAxMlaa8+yCJom29/nz4tw1+XTXFYmOB bwEQFDuM0tv3MsL97ZYek1iF6GyhUijInJS2v+2ed31gDO5aPmpYokK/SV05E4+QCFsVnyPZ5upLln0D @@ -203,7 +203,7 @@ hdHyphdiY5u+pwdk8mjgMgzDdL5IVgZrHuG1F7ZU6U/HVKNpg3zvuczzPD1v+HLbPyRlDEZ3XPk//atX "/assets/css/gjvote.css": { local: "assets/css/gjvote.css", size: 4373, - modtime: 1501250721, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/6xX22rkOBO+z1MIwsAEfpvunvQk3YHhhzm8wFwOcyFbsi0iW0aWO50sefctndySLfcs yy47kJZKVV9VfXVwo1qO/rpBqGVd1lBWN+qItpvNh6eb95ubQpBXc1sKLuQR3T48PDytCWMjqehZZYSW @@ -233,7 +233,7 @@ JADP0qw5bZuDO7DKg4/AWTubtBtBQ0NonI0TDT8K76+iCImX9nDSHXwI/mFgRSPLUWamaxjLkg7Durbd "/assets/img/favicon.png": { local: "assets/img/favicon.png", size: 4287, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/wC/EEDviVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QAAAAA AAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QQDEzgbdWMDlwAAAAxpVFh0Q29tbWVu @@ -313,7 +313,7 @@ GwM5xLYdRhVY/PPdX2X8mMmnqO5/AdOTL+cjjP0hAAAAAElFTkSuQmCCAQAA//+FC1PpvxAAAA== "/assets/js/admin.js": { local: "assets/js/admin.js", size: 0, - modtime: 1500039735, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/wEAAP//AAAAAAAAAAA= `, @@ -322,7 +322,7 @@ H4sIAAAJbogA/wEAAP//AAAAAAAAAAA= "/assets/js/gjvote.js": { local: "assets/js/gjvote.js", size: 3561, - modtime: 1500995424, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/6xXX2/jNgx/z6fQcg920NRwBgwDmuUOt9sNG9DeDWjfij4oNpNoVSTPktML2nz3UbL8 N3LSbteH2hZ/5E8iKZJZFSLRTAqi5XrN4WO6ZeIvKoCHE/I8ImRHc7IFUZAFSWVS4KuO/ikg398Ch0TL @@ -348,7 +348,7 @@ bQEn8BBF8LXoRH5KZnEcezLEg/3vGzYqxl9mzj52YT22GQDOzmPkqeCzeTMP4cJ7rCKec9W9sh48PDS2 "/assets/vendor/css/grids-responsive-min.css": { local: "assets/vendor/css/grids-responsive-min.css", size: 8032, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/8SVQY/bRgyF7/4VzqFAG1SyTWlGsvdSNM2hQFAUyKlHxxbWQm3JsLVJtov977UhjVd8 JGeR0/r2KHokksP3zd6/m/z9cKqmX+epT2nyoT0+nur7XTel+SKb/rPete27yad6UzXnajt9aLbVadrt @@ -378,7 +378,7 @@ m2lyu18rhduak8S4HXzF4rbigQa3g/9Y3NbsKMbtfm0Vbms2ZnFbc7gIt4P/WdzW7FDndvBJi9u9zSjc "/assets/vendor/css/pure-min.css": { local: "assets/vendor/css/pure-min.css", size: 16449, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/+w7aY/bRrLf/SsYGwY8BqmRqHNIxHg5nPfykBc8IPtl4cwCLbIlcYcXSGoOa/Xft/og WX1Q1GQXi/2wUWyT3dXV1XV1VXXz9uM3b/7/WFHncTpZTfw3PxTlS5XsD43jT2dz58/kUBTfvPkliWhe @@ -452,7 +452,7 @@ I6RidK8h9u8BAAD//+G2/iZBQAAA "/assets/vendor/font-awesome/HELP-US-OUT.txt": { local: "assets/vendor/font-awesome/HELP-US-OUT.txt", size: 323, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/zyPwU7zMBCE73mKuf0/Umjv3BCoqDdewbhrOTTxWN5No7w961Jxnd35ZuaMzCrYuWLm TXBiMbxuolzkgHPql3+uJ67lgsmwqqR1HlFnCSq4EIsgIIUbG4L/xCzxCq6GZcccTNRQG78l2jic2P7w @@ -464,7 +464,7 @@ yVJ08kHdDRVzQkNk3e/la1CTXyC3chiGz/vMTu2rvI0Pc/n5PTjjwy378BMAAP//GVwmskMBAAA= "/assets/vendor/font-awesome/css/font-awesome.css": { local: "assets/vendor/font-awesome/css/font-awesome.css", size: 37414, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9R934/kuJHm+/wVeTPwTbdRWV1S/m7DZ8+dYZwBe23AY2Af9oWSqBS7JFFDSVWVvej/ fUkpM/lRmRGqBeZlG3ZPp/QxRIrBYETwI/Xpt//ru8VvF4s/67pb/PQqW13Jxfpx9/i0SE6LP2biRR5F @@ -596,7 +596,7 @@ GGshktbGUJ38nb06fip4ETVv7tf5O8OXn42dm53fvnhyvyphbMz7ebE8372e67EoVJbJ2l0bvtGwcKr5 "/assets/vendor/font-awesome/css/font-awesome.min.css": { local: "assets/vendor/font-awesome/css/font-awesome.min.css", size: 31000, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/8x9S4/kuJX13r8iv2746yojIzvFeGfB454HDBiwMQt7MYvZUBIVYqUkqvTIzKhC/fch JV6KjDyUe4BezGDQrhQPKYq8vC8eMn7+w//73d0f7u7+rJrh7l9fRa9qcbd7OD483qXXu19y/iIuvMmv @@ -722,7 +722,7 @@ fwIAAP//3Dj1Ahh5AAA= "/assets/vendor/font-awesome/fonts/FontAwesome.otf": { local: "assets/vendor/font-awesome/fonts/FontAwesome.otf", size: 134808, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5y7d1xT1xs4fA4kN+HeDEZCJeEmaqu2dQNBcbRuW63WWmrdyAgEhQSTMGVvCCIjyBZU tE6c1dpqS7fVbmm/rd21/Xba9VVP4AT6PjfRjvf3x/t+fl5PzrjPOc85z37uvTwaHf0oYlEh8kUj5y9a @@ -2570,7 +2570,7 @@ oJ2u1bkNoTENr2D7KYPKTdVa+vdzTkMI+XuTqY14mp/Xf/Tv/wcAAP//MaGCwpgOAgA= "/assets/vendor/font-awesome/fonts/fontawesome-webfont.eot": { local: "assets/vendor/font-awesome/fonts/fontawesome-webfont.eot", size: 165742, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/8y9CXyUxfk4PjPvtff17pFsNslu9spFCNkrhJAs95GAKCgKiAFFEERRQJR6rEJVkCoC IlqPoJVKay316NcLu7XVXkptpdZa229sa7X1aEBrFbJv/s/M++5mswRov9/f7/f5Q+Z9553zmWeemXme @@ -4216,7 +4216,7 @@ P//xbwAAAP//T3Adum6HAgA= "/assets/vendor/font-awesome/fonts/fontawesome-webfont.svg": { local: "assets/vendor/font-awesome/fonts/fontawesome-webfont.svg", size: 444379, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/+z963PcWJIliH+PvwK//Jntlx0ocR94dXXNWE+/bM02t8dsZ3ZtP6VREjPJKUgUKIhZ pb9+/ZzjFxFBRARJpVKV1dtVKTIYAC7u069f9+PH//6//PndVD1c33+8vXv/x+/Cq+a76uNy9f7t1XT3 @@ -6490,7 +6490,7 @@ v9vYP//T//6//6/9Xy7//E+/f/3f//4v//xP//78f/7L5f8NAAD//67xO1/bxwYA "/assets/vendor/font-awesome/fonts/fontawesome-webfont.ttf": { local: "assets/vendor/font-awesome/fonts/fontawesome-webfont.ttf", size: 165548, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/8y9CXxU1fU4fu992+zbmyWZTJKZzJaNEDJbCCEZ9iUBUVAUEAOKIIiigChVGYWqIHUB RLQuQSuV1lrq0q8bdmqr3ZTaSq21tt/Y1mrr0oDWKmRe/ufe92YyGQK03+/v9/v8Ife9d/dzzz333nPO @@ -8134,7 +8134,7 @@ t3e3d7d3t3e3d7d3t3fYO+wd9g57h73D3mHvYFKqpcPSYemwdFg6LB3jfLR09+kT/13/8eyZf9V6Po26 "/assets/vendor/font-awesome/fonts/fontawesome-webfont.woff": { local: "assets/vendor/font-awesome/fonts/fontawesome-webfont.woff", size: 98024, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/2y3Y3AvzxfuG9t2dmzs2LadHdu2bdu2bdvJN7Zt2/f3r3vOuzNVz3T3Z56Z7lnVNWuW m7yYGBAwEBAQsO8ZEPx/LUhoNRAYECTQ/+MQE1OR/c/I+F8X73+y6hXvEBcRFfuPyfw3JvxPf0DugMDk @@ -9775,7 +9775,7 @@ FiYWDAzs/xcAAP//GDN74Oh+AQA= "/assets/vendor/font-awesome/fonts/fontawesome-webfont.woff2": { local: "assets/vendor/font-awesome/fonts/fontawesome-webfont.woff2", size: 77160, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/wAiQN2/d09GMgABAAAAAS1oAA0AAAAChpgAAS0OAAQBywAAAAAAAAAAAAAAAAAAAAAA AAAAP0ZGVE0cGiAGYACFchEIComZKIe2WAE2AiQDlXALlhAABCAFiQYHtHVbUglyR2H3kYQqug2BJ+09 @@ -11070,7 +11070,7 @@ k9HLPfid1jIi//FfI/7/veJjnx5xAvDNUt22m/rzx3w2xw0l/ovVf9HS/C+Nth0dvj7vJck5gSE83UoW "/assets/vendor/font-awesome/less/animated.less": { local: "assets/vendor/font-awesome/less/animated.less", size: 713, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9xQzUrEMBC+5ynmIqyHuF1F0HqpRx8jthMZ2k5CJqJS8u7GQLVCtT3vQAgk3+8cj/DI NJqIHTy1jkXlF/3nKHXVTNboVkT7gJbekxZPDJMC0G/43FPUpgiS4xoytHxfCxBbYooIAzGa8JAJ8+wk @@ -11082,7 +11082,7 @@ yudU7ZC9ub3fI7yApdLnPHp8BgAA//912/xDyQIAAA== "/assets/vendor/font-awesome/less/bordered-pulled.less": { local: "assets/vendor/font-awesome/less/bordered-pulled.less", size: 585, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4xRQWrDMBC86xVzyiEgKWlTKM4llD6gX1AtyRUolpFsKBj/vSu7xYVWxj4s7O7M7Iwl JV5C1CYajQPeBu+NZjTkpY8xcRut4nVKvIvGus+Jv88KGBnQKa1d21QQD+ZO5SnXM9UrLRdchRS80xCn @@ -11095,7 +11095,7 @@ AAA= "/assets/vendor/font-awesome/less/core.less": { local: "assets/vendor/font-awesome/less/core.less", size: 452, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SQTU7DQAyF93MKSyzYMBQkNrSb8iPuMUmcxOrErsamv+rdcQYQEohsrNjvfX6exQKe kyK85KQKr9gTk5Fw8EH87wvhdn3uU2xV47a453CBcwDoSLc5HZdAnIkxNlnazcoHvbAtgaVMKf8qa+fM @@ -11108,7 +11108,7 @@ cErcY7Mh+zp8Eqkap7FRyuT3dvOzxUlOUfTwRzeUdNQ2ZVyFcAkfAQAA//8hEyifxAEAAA== "/assets/vendor/font-awesome/less/fixed-width.less": { local: "assets/vendor/font-awesome/less/fixed-width.less", size: 119, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9LXV3DLrEhNUQjPTCnJUPBMzs8r5gIK6uICXHoO1WmJusnFxboFRalpmRW1umnlCtVc CgrlIBOsFDQMLVJzFfQVDE00rYGiJakVJbqJOZnpeVYKyal5JalF1ly1XIAAAAD//wJXRMV3AAAA @@ -11118,7 +11118,7 @@ CgrlIBOsFDQMLVJzFfQVDE00rYGiJakVJbqJOZnpeVYKyal5JalF1ly1XIAAAAD//wJXRMV3AAAA "/assets/vendor/font-awesome/less/font-awesome.less": { local: "assets/vendor/font-awesome/less/font-awesome.less", size: 495, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3TPzUrEMBAH8HufYtyTLrZ1QRDqpSIsLFQ81BeYNtPdwTQJk7gfb2/WD1ZIPOTy+8/8 w9TLqwKWAGtrAjwdyNuZ4L56qO5gOEGrcE9bNOoEJexCcE1dT3ESvwcrttHbP/LV1fFIxtN/K7X+ya/P @@ -11131,7 +11131,7 @@ Ex9JlQdWmR7NPiQ4WFEkccd9aE0qydHwjCETiA1nLyfNzmVyH3B8zziPNnO4H4XIlEKoLld9BgAA//+H "/assets/vendor/font-awesome/less/icons.less": { local: "assets/vendor/font-awesome/less/icons.less", size: 49712, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5SdzbbsqJG253UVOfw+r852ud01cU9ckx574gsgJTLFSUnoICn33sfL99786Afl0fsG Nal1dvEQUiIIIoIA/vyny//afrr8/qFH2+nLPOrxMjX68s/eVLbWl38481KT/3vUl9+dVpf/949//v7/ @@ -11249,7 +11249,7 @@ HjBHN/PtFIrFFuQy7BBoFa0nmvKagFD5/wIAAP//3HvahDDCAAA= "/assets/vendor/font-awesome/less/larger.less": { local: "assets/vendor/font-awesome/less/larger.less", size: 370, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SOwWrEIBCG7z7Ff1loF6bS1Vw2l1577hNImJihxhSVZWnYd6+1lxISwYN+3/z/aI33 YYn4kG/Oqr7o6Cilz5jdJ2eUiTEuscCYE4JLnhMSB1fkxihL4/KbWm9xEis+a/Xyto6OhpzpK/Eo9wcF @@ -11261,7 +11261,7 @@ j1WhRVGuC1zxZHmGhnnu63+okzSx+KlUYhqxjdw4FRlcIBfExyvotTv16rFTcblj/V9w4bnHnmg2ojkS "/assets/vendor/font-awesome/less/list.less": { local: "assets/vendor/font-awesome/less/list.less", size: 377, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SPwU7DMBBE7/6KOaEgtARQT6mEekXiJ0yyCSu5thVvoVWUf8c2FAkoPtkz3pm3bYtn SYqnPvhk8ov+O8bc7pbRUp8SxZlHOa50cFgMEO0wiJ/I8agd7rZZ2tt5Ev+l7PKYE3qXQV+L6XIjJT05 @@ -11273,7 +11273,7 @@ Jj1F7uCD52I8ZgsLYkiiEnyHmZ1VeeMtVrNeACjfC8D3gH1JwR20pn2W0+/2evkLpSF2aB54jxb3m+sq "/assets/vendor/font-awesome/less/mixins.less": { local: "assets/vendor/font-awesome/less/mixins.less", size: 1603, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/7RU32/bNhB+919xyIDVAUJL7o+HyijgNOvaAcsKLB62V1o6SddQPIGkozhF/vcdKWvO sibYS+UHk8e7j3ffd8csg0u6JetnslJPfrPZotaKSrbzU/g6A6jI90bvCyBryKLaGi6vV3JQsw0FWHad @@ -11294,7 +11294,7 @@ Op0m+0NdfGzjcmyxgy7pDUuGSZqjZdIjH7dHOW7I09bgaB4VmaLuhf+/AwAA//+AodbcQwYAAA== "/assets/vendor/font-awesome/less/path.less": { local: "assets/vendor/font-awesome/less/path.less", size: 771, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5ySwW6DMAyG7zyFpUpLW40i7Qia1l6qnbYd+gJpcSBSSFBiYKji3RfouqJV6zY4Yfv3 91uxoyVsX1928LbZPQewhPDHD5ZREKyF0RQKfkA4BgCfUSFVGwPb+mjToDMFssRXnT3EUFk1Z+uj4OEg @@ -11307,7 +11307,7 @@ HDWkWKMypdTZcI/ufJcNyiynGHQPUMk57ahVeMl2wUcAAAD//5aYxFMDAwAA "/assets/vendor/font-awesome/less/rotated-flipped.less": { local: "assets/vendor/font-awesome/less/rotated-flipped.less", size: 622, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4SQwU6EMBCG732K/2TWhNmF9eCye/Gica++AWFbbSQdUhpjJLy7QzXGKKWcyMz//Qzf bocnDk3QF1zhobN9L2/nlt2gZEWpR6nt3WgaaoeBeq+NfZ/Ixx6qS2DEVpZWar6nm7q86OcC1fUJmNJw @@ -11319,7 +11319,7 @@ mV9h2ON8f6A6o/nomQNWZBe5iMjLZsRROvPHTyb4Y2JUgLFd0P4Ix06f1KQ+AwAA//8sXplCbgIAAA== "/assets/vendor/font-awesome/less/screen-reader.less": { local: "assets/vendor/font-awesome/less/screen-reader.less", size: 118, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9LXVwhOLkpNzVMISk1MSS0q5gKK6OICXFx6xUW6+Xk5lQrVCjCmhqa1Qi1cQjctP7m0 ODEpJxVJCUIQohgQAAD//wZe7992AAAA @@ -11329,7 +11329,7 @@ ODEpJxVJCUIQohgQAAD//wZe7992AAAA "/assets/vendor/font-awesome/less/stacked.less": { local: "assets/vendor/font-awesome/less/stacked.less", size: 476, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3yQzVLDMAyE73kKXbhhmvaYXnrlzBO4ttJoqtoZS5RApu+OXH4GOgTftNqd/eTVCp7U hyNGeAw5SWOCW3pN87Cbe++CiBsL9jRdnNQ0zA3AmIWUcuqgIHulM25NjSQj+9cOKDEldHvO4VgXLxR1 @@ -11341,7 +11341,7 @@ vwACJsXyPwDMv2+hNGAh3cJyqJJBn5M6oTf8OP9POyX7EkFzh8y5dLAzw6dYE+8BAAD//+m7zRzcAQAA "/assets/vendor/font-awesome/less/variables.less": { local: "assets/vendor/font-awesome/less/variables.less", size: 22563, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4R8ubqkupK1f56ivnud/zeoAnLu4/TX3XabbbUjhMhUbUCUgMy96+lbIiOEFkPdbZxB K4Tm0IpB+ePHt/8RVouiVv1f7n+S3b+//vr3SiSVaYekE8Pj377R3z++f//hS/t//D1L9Pq3SgrRKy+W @@ -11445,7 +11445,7 @@ lxmH8DsD58samQPIGeRrMAyT5ffvX/8XAAD//wGLrUUjWAAA "/assets/vendor/font-awesome/scss/_animated.scss": { local: "assets/vendor/font-awesome/scss/_animated.scss", size: 715, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9yPzUrAMAzH732KgArzULcpgs6LV88+QZ3pCNvS0lRURt/dOphOmG5nA6XQ/j9+KUt4 9MRM3MFD61hUftG/jlIXJ9OpNboV0T6gpbekJQfApAD0Kz71FLVhGk0kxw1k6fx9KUBsiSkiDMRowl02 @@ -11457,7 +11457,7 @@ LHPQkDbr/csguNdfr+Ikopfi5nyHYNuSGe6Xnh7fbTAjypfnk6I6m69vnBgMi3VhbCC4aCIW1TN2P+oB "/assets/vendor/font-awesome/scss/_bordered-pulled.scss": { local: "assets/vendor/font-awesome/scss/_bordered-pulled.scss", size: 592, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4xR0WqEMBB8z1cMtNzDQZK79grFeyv9gP5CahIbyBlJFAriv3ejLfZBPX1Y2N2Z2Rkj Jd5C1CYajQM+Ou+NZjTkax9j4qF/tIqXKfEmGuu+B/45SqBnQKO0dnVVQDyZG5WXXM9Ur7SccAVS8E5D @@ -11470,7 +11470,7 @@ AgAA "/assets/vendor/font-awesome/scss/_core.scss": { local: "assets/vendor/font-awesome/scss/_core.scss", size: 459, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SQzWrjQAzH734KQRb2srPZhV6anPpB30O2ZVtkLIWRmk/y7pXtlkJLfRk8+v1/kma9 hkc0gqeMZvBMHQs7q1RRSD99VfV3df3VYWrM0r5E6HSDawXQsu0znjfAklko1Vmb3TYKnYpvQLSMmL8c @@ -11483,7 +11483,7 @@ i2kCkvElMjHObb3czpKBuB98uYeX4B6OZDrSFmJGG7Q4CUs/94CWmowF5xWWtrN1mmigwj6HGpTfDgMe "/assets/vendor/font-awesome/scss/_fixed-width.scss": { local: "assets/vendor/font-awesome/scss/_fixed-width.scss", size: 120, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9LXV3DLrEhNUQjPTCnJUPBMzs8r5gIK6uICXHrK1SppibrJxcW6BUWpaZkVtbpp5QrV XAoK5SAjrBQ0DC1ScxX0FQxNNK2BoiWpFSW6iTmZ6XlWCsmpeSWpRdZctVyAAAAA///0UODoeAAAAA== @@ -11493,7 +11493,7 @@ XAoK5SAjrBQ0DC1ScxX0FQxNNK2BoiWpFSW6iTmZ6XlWCsmpeSWpRdZctVyAAAAA///0UODoeAAAAA== "/assets/vendor/font-awesome/scss/_icons.scss": { local: "assets/vendor/font-awesome/scss/_icons.scss", size: 50498, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5Sdy7LkKJL39/UUYfZ9i5m2ienq6alNz6o2s+5NPwAhESHySEKJ0LlkW7/7cNEFRcb/ 79SmLE/xw6VA4Lg7Dvz5T5f/taO//P6hZzvoyzLr+eI7ffnHaBrb6svfnXlXPvw968vvTqvLv/39H7// @@ -11611,7 +11611,7 @@ fHSj4kHBCG9FMsUBoZbR2tPU3AzE2v8XAAD//0NCmAVCxQAA "/assets/vendor/font-awesome/scss/_larger.scss": { local: "assets/vendor/font-awesome/scss/_larger.scss", size: 375, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SOQWrDMBBF9zrFhzbQBqaikbyJT9B1TyDMWB4qy0USIdTk7lXVTTCxQAvpvfl/tMbH sER8yg9nVV+0d5TSR8zuizPKxBiXWGDMAcElzwmJgytyYZSlcflLrbc4iRUftXp7Wp9HR0PO9J14lOuN @@ -11623,7 +11623,7 @@ Ne3GtLtmtzG7f/M3AAD//+ckNq13AQAA "/assets/vendor/font-awesome/scss/_list.scss": { local: "assets/vendor/font-awesome/scss/_list.scss", size: 378, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SPUUvEMBCE3/MrBhQ5kbUq99QD3wX/RGy3dSGXhGZPr5T+d5MoIlrzlMxkZ75tGjxL Ujx1wSeTX/TfMeb2YrkcLHUpUZx4kPNKJ4fFANH2vfiRHA/a4u6QpaOdRvFfShlzQu/S62sxXa6kpLNj @@ -11635,7 +11635,7 @@ z0rWyZiDO/bKU1GvtjjGyrHZiRvs9j9jyy4fAQAA///94pSpegEAAA== "/assets/vendor/font-awesome/scss/_mixins.scss": { local: "assets/vendor/font-awesome/scss/_mixins.scss", size: 1637, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/7RUTW/bRhC961cMHKORAa9I5eMQCgHquGlSoG6AWkV7XZFDcuLlDrG7Mi0H+u+dXYpx qsRBL6EO4g7ffL03s1kGV3RH1s/kTT36zGY/dxEGtVZUsp2fwacZQEW+N3pXAFlDFtXGcHmzkg8121CA @@ -11657,7 +11657,7 @@ AA== "/assets/vendor/font-awesome/scss/_path.scss": { local: "assets/vendor/font-awesome/scss/_path.scss", size: 783, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5ySwW6DMAyG7zyFpU5LW40i7Qiatl6qnbYd+gJpcSBSSFBiYKji3RfouiJ161Ryip3f 32/FjpaweX/bwsd6+xrAEsI/DyyjIHgRRlMo+B7hEAB8R4VUbQxs46N1g84UyBL/6uw+hsqqOZsd7gQP @@ -11670,7 +11670,7 @@ oclRQ4o1KlNKnQ276U472qDMcopB9wCVnNKOWoXnbBd8BQAA///XLL2lDwMAAA== "/assets/vendor/font-awesome/scss/_rotated-flipped.scss": { local: "assets/vendor/font-awesome/scss/_rotated-flipped.scss", size: 672, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4TRwUrEMBAG4Hue4gdFVui47Xpwu3vxorhX36C0iQZLpqRRxNJ3dxrFg3STngrz85H5 Z7vFM4cm6A5XeOztMMjfqWU3KhnRuU+pm4vp0jTUjiMNXhv7OZOPENUlMOHeurZ/7zQkZMX7nW7qstMv @@ -11683,7 +11683,7 @@ GAv1s9ZygSfmNxj2OD3sqc7c5OCZA1KXKbIZaTYfkuISoX+l5ZJ/tUwKMLYP2h/g2OmjmtV3AAAA//8K "/assets/vendor/font-awesome/scss/_screen-reader.scss": { local: "assets/vendor/font-awesome/scss/_screen-reader.scss", size: 134, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9LXVwhOLkpNzVMISk1MSS0q5gKK6OICXFx6xUW6+Xk5lQrVCg6Zeck5pSmpClAhDU1r hVq4At20/OTS4sSknFQsShGSEE2AAAAA///6AJxphgAAAA== @@ -11693,7 +11693,7 @@ hVq4At20/OTS4sSknFQsShGSEE2AAAAA///6AJxphgAAAA== "/assets/vendor/font-awesome/scss/_stacked.scss": { local: "assets/vendor/font-awesome/scss/_stacked.scss", size: 482, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4SQTVLDMAyF9zmFZoAdpmmX6QlYcwLXVhpNVTtjiWLI9O7Y5WcgQ6h3enpv3metVvCk 1h3Qw6OLQZoimKXXNA83021vjRMxY8Ke8tlIjcPUAIxRSCmGDhKyVTrhtqieZGT72gEFpoBmx9Ed6uKF @@ -11706,7 +11706,7 @@ AA== "/assets/vendor/font-awesome/scss/_variables.scss": { local: "assets/vendor/font-awesome/scss/_variables.scss", size: 22644, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/4R8uaKkupKtf76i3rnPeM+gCsi5r9fddptttSOEyFRtQJSAzL3r61siI4QWQ91tnEEr AI0RKwbljx/f/ltYLYpa9X+5/0l2//766/9WIqlMOySdGB7/9o3+/v7+/Ydv7f/+9n9KVYmxHv45i/b6 @@ -11810,7 +11810,7 @@ gOjaZygKhLqYz+mH+6LjAtfDvvwPBxTO3+HfTsqhECGG8RJLvC5f4mE4kxkP4ktuvVL+4UWqJiIFtvGL "/assets/vendor/font-awesome/scss/font-awesome.scss": { local: "assets/vendor/font-awesome/scss/font-awesome.scss", size: 430, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3SPS07DMBCG9znF0BVUJKESElLYBCFVqhTEIlxgYk/aEY5tjU0ftyctIOxFl//3P+yp lzcFLAHWzkZ4OVBwE8Fj9VQ9wHCCVuOetmj1CUrYxeibuh7nJP4EK3YzbxNy2epYkQ10rVKbX//2/GgD @@ -11823,7 +11823,7 @@ AAA= "/assets/vendor/js/snack-min.js": { local: "assets/vendor/js/snack-min.js", size: 8142, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5RZe5PbthH/v5+CQj0a4oSjdE4605JmVMePxhk7du1LmqkkZ0AQfNzxSJWkTncj8bt3 FwBfOqWTZiY+CcAuFvv47UPzi8mfLOvCqnIubp2byrIFtT4/8tx6mxWlzIVU20ldbyt3Po/TOtkFjiju @@ -11889,7 +11889,7 @@ VPzfAAAA//9Iiv1pzh8AAA== "/templates/admin-activateclient.html": { local: "templates/admin-activateclient.html", size: 1355, - modtime: 1501251393, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/7RUTWvcQAy951cMujsDPRbbEJoWcmkXkj+g9Si7A+OZYT7cLiH/vfLn2ktSCsledqXH s/SeJLtUuhONwRgraMgmClDfCFE+u9DOuM+BigFYogKNPlhSILBJ2tkKJKpWW9kYzVWifHkRt0/UeoOJ @@ -11905,7 +11905,7 @@ meKMt1/fTCL+BgAA//8UF+dLSwUAAA== "/templates/admin-addgame.html": { local: "templates/admin-addgame.html", size: 499, - modtime: 1496946580, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/2xRzWrzMBC85ymWvfvTC9iGDwql9NBC8wKKtUkE+kNapQkh796NYqc+VAdbszvemR33 xp5gcrqUAScKTBnHDUC/j9kv9VQzda3wvHXa2UMgg6AntjEMqLTxNqiD9lRUoG9V9IkQPPExmgE/P762 @@ -11918,7 +11918,7 @@ LnXnraiv45gJq3uXsvU6X3D8bwy8yg69enTmiNU6Y0Hyy8bNrPsTAAD//ynWXmvzAQAA "/templates/admin-addteam.html": { local: "templates/admin-addteam.html", size: 446, - modtime: 1498076184, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/0xQW27DMAz77ykE/Qe+QBJgF9gGrBdwbLU14BdsOVtvPyVNA/8YpEyQlEbrVjBe1zqh ochUcL4AjLdUwnueW6FhH5xo0N7dI1kEbdilOKHSNriomHSoKtKvqnolhED8SHbC76+f6+68eTvythK/ @@ -11931,7 +11931,7 @@ csT+BwAA//+M7Jg8vgEAAA== "/templates/admin-adduser.html": { local: "templates/admin-adduser.html", size: 842, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/6yTTW7CMBCF95zCmn3kCySWuui6qD/rysQDWPKf7DGU23dIQghIRaraVfwmz2/mUyat sQfRO11KBz0GwgxqJUS7jdlf6qlmbIbCfGq0s7uABoTuycbQgdTG2yBrwVxkwKMs+oAgPNI+mg7WL2/v @@ -11945,7 +11945,7 @@ K5ery4r/BLWa+n4HAAD//wwzWIlKAwAA "/templates/admin-clients.html": { local: "templates/admin-clients.html", size: 1023, - modtime: 1501252619, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5RTQavbMAy+91eI0OPSwI7DCYyVQdh4vMPeYUclVhqDYwfHL6OU/vfJSdMlbdPxCg2y 9En6JH8+nUBVYKyH3S9qWo2e9uhx900rMr6D83kjpOqh1Nh1adS1WFLck/OqRB1lLxZQSuWVNagZNCbV @@ -11960,7 +11960,7 @@ H2ey4EHOWfcfFlX1hMawwPnuZructHSLZf+oKDbCqxlf4Bj9GwAA///E71UP/wMAAA== "/templates/admin-editteam.html": { local: "templates/admin-editteam.html", size: 13536, - modtime: 1500999296, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/+xaW2/bOBZ+z6/gagvYxsb2LHamD77tTCedokA7u5gkD4vBPNASbXNDSYZIOfUG/u97 eJFESpSspGmLYtqHRubl3Ph9h4eUHh7QizynEZot0eSGxHuGBbnCAk9ub99eodPpYhHRAwoZ5nwZhCQR @@ -12011,7 +12011,7 @@ AAA= "/templates/admin-edituser.html": { local: "templates/admin-edituser.html", size: 1654, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/7xUwW7bMAy99ysIXZwASXzPXA/bukMPw4qtPQ3DoEhMLFSWBEluEQT59zGSk9pZ26E7 7BJQ4tMjH/PMSqoHEJqHcMkEmoie1RcA1dr69njvOo/zdHGK5lyrjUHJgIuorLlkJZetMmUX0Idyt4PF @@ -12030,7 +12030,7 @@ zrZTSnYGAAA= "/templates/admin-games.html": { local: "templates/admin-games.html", size: 507, - modtime: 1500039735, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3RRwW6DMAy98xUW4jjofQJOk3brZfsBg98KEiRVkiFNVf99TrJq3dpeome/Z/vZOZ1o /iBjAzXvWI8LB7xwYA149XQ+F63MW7+3dOAVnibeQANgaHRQrbS7yBfaBotHKgg8LKBZujLV1CkuaVzY @@ -12043,7 +12043,7 @@ GgriffNPZfY7AAD//1NqSJj7AQAA "/templates/admin-login.html": { local: "templates/admin-login.html", size: 806, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/6SSTW7DIBCF9zkFYm/5AralLrqsGjW9AIZJggoMgqFNbl9i4x8WrVR1Fd7kzZtvrOmU /mTSiBh7LsERBD4cGOvOGOxS9ylAMxXWVyOMvjhQnAlJGl3PW6Gsdq1CgxftOLNAV1Q9P76e3qfIR6gG @@ -12057,7 +12057,7 @@ Xo6z3V9nVvnKh0MZ+h0AAP//kX1qvCYDAAA= "/templates/admin-main.html": { local: "templates/admin-main.html", size: 1541, - modtime: 1501270519, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/6xTTW/bMAy991dwRoBcJrvtboVjIMiAnfaBrtvOis04QmUpk2QHQdH/PtJygmRN2yDt xbLIp6dHPiqvVAellt5PktKagCYkxQUAx3mlv+Wn4kc716qEr7bCPKN9TMzbEKwBa0pK3k+StTKVXafa @@ -12073,7 +12073,7 @@ EXnfsPcmr6nBTP6F1/cmbz06Jv/F65EZHZZ/AQAA//8pWoRoBQYAAA== "/templates/admin-menu.html": { local: "templates/admin-menu.html", size: 0, - modtime: 1500039735, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/wEAAP//AAAAAAAAAAA= `, @@ -12082,7 +12082,7 @@ H4sIAAAJbogA/wEAAP//AAAAAAAAAAA= "/templates/admin-teams.html": { local: "templates/admin-teams.html", size: 1090, - modtime: 1500052988, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5xTwYrcMAy9z1eIMMdmfC8eQ2GhFNo97X6AEms2po4z2E5KWfbfK9tJmxmSlm5Osvyk J708S20maC2GcK6aIcahr8MVW4KWXCRfqQOARDCar6P7pPUTYV8tFdfRU92MXOZgFddhbFsKoYLO0+Vc @@ -12098,7 +12098,7 @@ SRlCBAAA "/templates/admin-users.html": { local: "templates/admin-users.html", size: 1005, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5xTwY7TMBC99ytGYaWmEok5QxqJFXtA4rbwAa5n0lg4drCdrqoq/87E2VShKkJsL3ke +82bmTetUJ9AGRnCPju4GF1XhF4qAkU2ks/qDUAlQSNfR/sZ8Ufg6MLoB0/FYWCahRUuwqAUhZBB66nZ @@ -12114,7 +12114,7 @@ XzHfrrS2uzLN6JsOkTvo3Iny7fxf2KYCxs3I30osvf4OAAD//yR+jxHtAwAA "/templates/admin-votes.html": { local: "templates/admin-votes.html", size: 837, - modtime: 1501270323, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3xSTW/cIBC9768YWXusbam9RRip2lxy6SFd9Y7NJIuKwYJZS1WU/94Bm5Av5cTM4w3z 5g1CmxUmq2IcmoDxaim2k3ekjMPQyIO4fJenawjoCO63e9Ezdnh6gqDcI8LRfIPjCjcDdGecF6sIbxWp @@ -12129,7 +12129,7 @@ bmfD8/MBoLIp0SnzjytXqHmnZBJD98r9ZeQmp7R2v9SMnIsxQC+3p9DpVFMj0fMYLJbUaBGMHprVE8Y2 "/templates/footer.html": { local: "templates/footer.html", size: 228, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/1yPwQqDMAyG7z5FyV1Ez7HvUteAhWwZJhV8+7lNsTaH5PB95OfHLqbVN7hvl+IIT4mB W1lp4bCBb5zDn+D2wbkvHEvGBB67uT/xUGDN02UMh3FPmSRuX3wFVDybyUvhgJX34KA6wkJK1r4zc/Hq @@ -12140,7 +12140,7 @@ bPU/nwAAAP///4iBjuQAAAA= "/templates/header.html": { local: "templates/header.html", size: 1364, - modtime: 1500039735, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/9xUQW7bMBC8+xUL1cfaBHpsJQNpiqIBklP6AVpcRwtTVEBSAgpBf+9SomSqTlGjx5ws cmeHM8sxc1daevWHTSctlJrQ+AcFBWR9v7+Py2HIvmxyMSNzRR1DpXNFduKfCkqGoQVu+R7W96HGTYcN @@ -12156,7 +12156,7 @@ tbL5p7Gw1g4Tpzcpk0r9h6y3RV3N+m8xgynfDee7/kfkvo6gdxW8+KYsH+kbEw8Pf1mebXyUxpQ+t8ef "/templates/htmlfooter.html": { local: "templates/htmlfooter.html", size: 115, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/1IAAhv9lMwyOy4Qs7paoSgxLz1VQSVTR0GlTMHKVkEvOLkos6CkWKG2FqzEphjMVygu SrZVAqoHqqqtVbKz0YeIw81JzUuBaLHRT8pPqbTjstHPKMnNseMCBAAA//8zytRAcwAAAA== @@ -12166,7 +12166,7 @@ SrZVAqoHqqqtVbKz0YeIw81JzUuBaLHRT8pPqbTjstHPKMnNseMCBAAA//8zytRAcwAAAA== "/templates/htmlheader.html": { local: "templates/htmlheader.html", size: 801, - modtime: 1496942648, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/5xSTY/TMBC991dMrT2uYxBCwCrpZVkJLoDUvaDVHlxnUk9x7GBPslRV/jtOP2hh98Qp fvPx5s3LlPOPX2/vv3+7A8utW8zKwwegtKjr6ZGfLbIGY3VMyJXouZHvxWXK6xYrMRA+dSGyABM8o8+l @@ -12182,7 +12182,7 @@ AAAA//9ablv2IQMAAA== "/templates/public-teammgmt.html": { local: "templates/public-teammgmt.html", size: 10204, - modtime: 1500999274, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/+xa2Y/buBl/n7+CVQPYRsf2Ft3Ngy9ss5NdBMimxc4MimKRB1qibTaUaIiUJ+7A/3s/ HpJIibI1kwMoNvMyEo/v/H0HaT0+ohdFQRM0W6LJHUn3DEtygyWe3N+/uUGn09UioQcUMyzEMopJJkke @@ -12228,7 +12228,7 @@ TMvrt/8FAAD//x6rSUXcJwAA "/templates/public-voting.html": { local: "templates/public-voting.html", size: 12039, - modtime: 1500477012, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/+xaXY/bNta+n1/B6C0qG+OP9t3FXsyHgWTS7mbRpEAyyaIYDArZom0lsmhI1Ezcwfz3 fc4hKVGy5HGy3aIXm4uMTB4ens/nHFJ6eBDJUmRKi8m13GzTSMuXkY7wI9oU4vHxRIiLOLmbvVFiFW1k @@ -12291,7 +12291,7 @@ keHbF0X7EWiY9EaXLdydVrKhth9phno/0i6m7guY+muQfwcAAP//0nhPKAcvAAA= "/templates/public-waiting.html": { local: "templates/public-waiting.html", size: 24, - modtime: 1498146095, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/7JJySyz83QOUXBPzE1V8ErMtdEHiXABAgAA//+MadFWGAAAAA== `, @@ -12300,7 +12300,7 @@ H4sIAAAJbogA/7JJySyz83QOUXBPzE1V8ErMtdEHiXABAgAA//+MadFWGAAAAA== "/templates/unauthorized.html": { local: "templates/unauthorized.html", size: 90, - modtime: 1501246590, + modtime: 1506689947, compressed: ` H4sIAAAJbogA/3LOyUzNK1EIzUssLcnIL8qsSk3Rs0kqUtC344JSnmkKlfmlCmmpqTkKJRmZxQoglKeQ WlSUX6SjkJyfV5KYXKKQmKeQmJKbmZdZXFKUWJJfpMcFCAAA//8ZCPIcWgAAAA== 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 } }