From abd7e803e9ad900fbaa2c395cdf909f4edabf828 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Fri, 21 Apr 2017 13:17:18 -0500 Subject: [PATCH] Making Progress --- adminEndpoints.go | 99 +++++++++++--------- assets/js/gjvote.js | 18 +++- config.json | 2 +- main.go | 75 ++++++++++++++- model.go | 109 ++++++++++------------ model_games.go | 8 ++ model_teams.go | 167 ++++++++++++++++++++++++++++++++++ model_users.go | 76 ++++++++++++++++ publicEndpoints.go | 29 +----- templates/admin-addteam.html | 12 +++ templates/admin-editteam.html | 42 +++++++++ templates/admin-teams.html | 31 +++++++ 12 files changed, 531 insertions(+), 137 deletions(-) create mode 100644 model_games.go create mode 100644 model_teams.go create mode 100644 model_users.go create mode 100644 templates/admin-addteam.html create mode 100644 templates/admin-editteam.html create mode 100644 templates/admin-teams.html diff --git a/adminEndpoints.go b/adminEndpoints.go index 0e31f6a..67f2638 100644 --- a/adminEndpoints.go +++ b/adminEndpoints.go @@ -4,57 +4,13 @@ import ( "net/http" "github.com/gorilla/mux" - "github.com/gorilla/sessions" ) func initAdminRequest(w http.ResponseWriter, req *http.Request) *pageData { - if site.DevMode { - w.Header().Set("Cache-Control", "no-cache") - } - p := new(pageData) - // Get session - var err error - var s *sessions.Session - if s, err = sessionStore.Get(req, site.SessionName); err != nil { - http.Error(w, err.Error(), 500) - return p - } - p.session = new(pageSession) - p.session.session = s - p.session.req = req - p.session.w = w - - // First check if we're logged in - userEmail, _ := p.session.getStringValue("email") - - // With a valid account - p.LoggedIn = dbIsValidUserEmail(userEmail) - p.Site = site - p.SubTitle = "" - p.Stylesheets = make([]string, 0, 0) - p.Stylesheets = append(p.Stylesheets, "/assets/css/pure-min.css") - p.Stylesheets = append(p.Stylesheets, "/assets/css/grids-responsive-min.css") - p.Stylesheets = append(p.Stylesheets, "/assets/font-awesome/css/font-awesome.min.css") - p.Stylesheets = append(p.Stylesheets, "/assets/css/gjvote.css") + p := InitPageData(w, req) p.Stylesheets = append(p.Stylesheets, "/assets/css/admin.css") - - p.HeaderScripts = make([]string, 0, 0) - p.HeaderScripts = append(p.HeaderScripts, "/assets/js/snack-min.js") - p.Scripts = make([]string, 0, 0) p.Scripts = append(p.Scripts, "/assets/js/admin.js") - p.FlashMessage, p.FlashClass = p.session.getFlashMessage() - if p.FlashClass == "" { - p.FlashClass = "hidden" - } - // Build the menu - if p.LoggedIn { - p.Menu = append(p.Menu, menuItem{"Votes", "/admin/votes", "fa-sticky-note"}) - p.Menu = append(p.Menu, menuItem{"Teams", "/admin/teams", "fa-users"}) - p.Menu = append(p.Menu, menuItem{"Games", "/admin/games", "fa-gamepad"}) - p.BottomMenu = append(p.BottomMenu, menuItem{"Users", "/admin/users", "fa-user"}) - p.BottomMenu = append(p.BottomMenu, menuItem{"Logout", "/admin/dologout", "fa-sign-out"}) - } return p } @@ -183,6 +139,59 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData) // handleAdminTeams func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) { + vars := mux.Vars(req) + page.SubTitle = "Teams" + teamId := vars["id"] + if teamId == "new" { + switch vars["function"] { + case "save": + name := req.FormValue("teamname") + if dbIsValidTeam(name) { + // 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 { + page.session.setFlashMessage(err.Error(), "error") + } else { + page.session.setFlashMessage("Team "+name+" created!", "success") + } + } + redirect("/admin/teams", w, req) + default: + page.SubTitle = "Add New Team" + page.show("admin-addteam.html", w) + } + } else if teamId != "" { + if dbIsValidTeam(teamId) { + switch vars["function"] { + case "save": + page.session.setFlashMessage("Not implemented yet...", "success") + redirect("/admin/teams", w, req) + case "delete": + var err error + if err = dbDeleteTeam(teamId); err != nil { + page.session.setFlashMessage("Error deleting team: "+err.Error(), "error") + } + redirect("/admin/teams", w, req) + default: + page.SubTitle = "Edit Team" + t := dbGetTeam(teamId) + page.TemplateData = t + page.show("admin-editteam.html", w) + } + } else { + page.session.setFlashMessage("Couldn't find the requested team, please try again.", "error") + redirect("/admin/teams", w, req) + } + } else { + type teamsPageData struct { + Teams []Team + } + + page.TemplateData = teamsPageData{Teams: dbGetAllTeams()} + page.SubTitle = "Teams" + page.show("admin-teams.html", w) + } } // handleAdminGames diff --git a/assets/js/gjvote.js b/assets/js/gjvote.js index d7d773b..d0591f3 100644 --- a/assets/js/gjvote.js +++ b/assets/js/gjvote.js @@ -1 +1,17 @@ -console.log('ICTGameJam Wahoo!'); +function showAdminPanel() { + +} + + +document.onkeydown = function(evt) { + evt = evt || window.event; + var isEscape = false; + if("key" in evt) { + isEscape = (evt.key == "Escape" || evt.key == "Esc"); + } else { + isEscape = (evt.keyCode == 27); + } + if(isEscape) { + showAdminPanel(); + } +} diff --git a/config.json b/config.json index 03decc1..14efff8 100644 --- a/config.json +++ b/config.json @@ -1 +1 @@ -{"title":"ICT GameJam Voting","port":8080,"session":"ict-gamejam","dir":"./","devmode":true,"db":"gjvote.db"} \ No newline at end of file +{"title":"ICT GameJam Voting","port":8080,"session":"ict-gamejam","dir":"./","devmode":true,"db":"gjvote.db","CurrentJam":""} \ No newline at end of file diff --git a/main.go b/main.go index bf9e5c3..ef8279f 100644 --- a/main.go +++ b/main.go @@ -30,6 +30,8 @@ type siteData struct { ServerDir string `json:"dir"` DevMode bool `json:"devmode"` DB string `json:"db"` + + CurrentJam string } // pageData is stuff that changes per request @@ -47,6 +49,7 @@ type pageData struct { Menu []menuItem BottomMenu []menuItem session *pageSession + CurrentJam string TemplateData interface{} } @@ -124,7 +127,7 @@ func saveConfig() { func initialize() { // Check if the database has been created - assertError(openDatabase()) + assertError(initDatabase()) if !dbHasUser() { reader := bufio.NewReader(os.Stdin) @@ -146,12 +149,82 @@ func initialize() { } assertError(dbUpdateUserPassword(email, string(pw1))) } + if !dbHasCurrentJam() { + 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 { + fmt.Println("Error saving Current Jam") + } + } } func loggingHandler(h http.Handler) http.Handler { return handlers.LoggingHandler(os.Stdout, h) } +func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { + if site.DevMode { + w.Header().Set("Cache-Control", "no-cache") + } + p := new(pageData) + // Get session + var err error + var s *sessions.Session + if s, err = sessionStore.Get(req, site.SessionName); err != nil { + http.Error(w, err.Error(), 500) + return p + } + p.session = new(pageSession) + p.session.session = s + p.session.req = req + p.session.w = w + + // First check if we're logged in + userEmail, _ := p.session.getStringValue("email") + // With a valid account + p.LoggedIn = dbIsValidUserEmail(userEmail) + + p.Site = site + p.SubTitle = "GameJam Voting" + p.Stylesheets = make([]string, 0, 0) + p.Stylesheets = append(p.Stylesheets, "/assets/css/pure-min.css") + p.Stylesheets = append(p.Stylesheets, "/assets/css/grids-responsive-min.css") + p.Stylesheets = append(p.Stylesheets, "/assets/font-awesome/css/font-awesome.min.css") + p.Stylesheets = append(p.Stylesheets, "/assets/css/gjvote.css") + + p.HeaderScripts = make([]string, 0, 0) + p.HeaderScripts = append(p.HeaderScripts, "/assets/js/snack-min.js") + + p.Scripts = make([]string, 0, 0) + p.Scripts = append(p.Scripts, "/assets/js/gjvote.js") + + p.FlashMessage, p.FlashClass = p.session.getFlashMessage() + if p.FlashClass == "" { + p.FlashClass = "hidden" + } + + // Build the menu + if p.LoggedIn { + p.Menu = append(p.Menu, menuItem{"Admin", "/admin", "fa-key"}) + p.Menu = append(p.Menu, menuItem{"Votes", "/admin/votes", "fa-sticky-note"}) + p.Menu = append(p.Menu, menuItem{"Teams", "/admin/teams", "fa-users"}) + p.Menu = append(p.Menu, menuItem{"Games", "/admin/games", "fa-gamepad"}) + + p.BottomMenu = append(p.BottomMenu, menuItem{"Users", "/admin/users", "fa-user"}) + p.BottomMenu = append(p.BottomMenu, menuItem{"Logout", "/admin/dologout", "fa-sign-out"}) + } + + if p.CurrentJam, err = dbGetCurrentJam(); err != nil { + p.FlashMessage = "Error Loading Current GameJam: " + err.Error() + p.FlashClass = "error" + } + + return p +} + func (p *pageData) show(tmplName string, w http.ResponseWriter) error { for _, tmpl := range []string{ "htmlheader.html", diff --git a/model.go b/model.go index b6ae171..a07a815 100644 --- a/model.go +++ b/model.go @@ -1,11 +1,6 @@ package main -import ( - "fmt" - - "github.com/br0xen/boltease" - "golang.org/x/crypto/bcrypt" -) +import "github.com/br0xen/boltease" var db *boltease.DB var dbOpened bool @@ -24,83 +19,73 @@ func openDatabase() error { func initDatabase() error { openDatabase() - db.MkBucketPath([]string{"users"}) - db.MkBucketPath([]string{"teams"}) - return nil + // Create the path to the bucket to store admin users + if err := db.MkBucketPath([]string{"users"}); err != nil { + return err + } + // Create the path to the bucket to store jam informations + if err := db.MkBucketPath([]string{"jams"}); err != nil { + return err + } + // Create the path to the bucket to store site config data + return db.MkBucketPath([]string{"site"}) } -// dbHasUser -// Returns true if there are any users in the database -func dbHasUser() bool { - return len(dbGetAllUsers()) > 0 -} - -func dbGetAllUsers() []string { +func dbSetCurrentJam(name string) error { if err := db.OpenDB(); err != nil { - return []string{} + return err } defer db.CloseDB() - usrs, err := db.GetBucketList([]string{"users"}) - if err != nil { - return []string{} - } - return usrs + return db.SetValue([]string{"site"}, "current-jam", name) } -func dbIsValidUserEmail(email string) bool { - if err := db.OpenDB(); err != nil { +func dbHasCurrentJam() bool { + var nm string + var err error + if nm, err = dbGetCurrentJam(); err != nil { return false } - defer db.CloseDB() - - usrPath := []string{"users", email} - if _, err := db.GetValue(usrPath, "password"); err != nil { - return false - } - return true + ret, err := dbIsValidJam(nm) + return ret && err != nil } -func dbCheckCredentials(email, pw string) error { - var err error - if err = db.OpenDB(); err != nil { - return err - } - defer db.CloseDB() - - var uPw string - usrPath := []string{"users", email} - if uPw, err = db.GetValue(usrPath, "password"); err != nil { - return err - } - return bcrypt.CompareHashAndPassword([]byte(uPw), []byte(pw)) -} - -// dbUpdateUserPassword -// 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 { - cryptPw, cryptError := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if cryptError != nil { - return cryptError - } +func dbGetCurrentJam() (string, error) { if err := db.OpenDB(); err != nil { - return err + return "", err } defer db.CloseDB() - usrPath := []string{"users", email} - return db.SetValue(usrPath, "password", string(cryptPw)) + return db.GetValue([]string{"site"}, "current-jam") } -func dbDeleteUser(email string) error { +func dbIsValidJam(name string) (bool, error) { var err error - fmt.Println("Deleting User:", email) if err = db.OpenDB(); err != nil { - return err + return false, err } defer db.CloseDB() - return db.DeleteBucket([]string{"users"}, email) + // Get all keys in the jams bucket + var keys []string + if keys, err = db.GetKeyList([]string{"jams", name}); err != nil { + return false, err + } + // All valid gamejams will have: + // "name" + // "teams" + for _, v := range []string{"name", "teams"} { + found := false + for j := range keys { + if keys[j] == v { + found = true + break + } + } + if !found { + // If we make it here, we didn't find a key we need + return false, nil + } + } + return true, nil } diff --git a/model_games.go b/model_games.go new file mode 100644 index 0000000..b09cc28 --- /dev/null +++ b/model_games.go @@ -0,0 +1,8 @@ +package main + +import "github.com/pborman/uuid" + +type Game struct { + UUID *uuid.UUID + Name string +} diff --git a/model_teams.go b/model_teams.go new file mode 100644 index 0000000..d437c38 --- /dev/null +++ b/model_teams.go @@ -0,0 +1,167 @@ +package main + +import ( + "fmt" + + "github.com/pborman/uuid" +) + +type Team struct { + UUID string + Name string + Members []TeamMember + Game *Game +} + +type TeamMember struct { + UUID string + Name string + SlackId string + Twitter string + Email string +} + +func dbCreateNewTeam(nm string) error { + var err error + if err = db.OpenDB(); err != nil { + return err + } + defer db.CloseDB() + + var currJam string + if currJam, err = dbGetCurrentJam(); err != nil { + return err + } + + // Generate a UUID + uuid := uuid.New() + teamPath := []string{"jams", currJam, "teams", uuid} + + if err := db.MkBucketPath(teamPath); err != nil { + fmt.Println("Error at 39: " + uuid) + return err + } + if err := db.SetValue(teamPath, "name", nm); err != nil { + fmt.Println("Error at 43") + return err + } + if err := db.MkBucketPath(append(teamPath, "members")); err != nil { + fmt.Println("Error at 47") + return err + } + gamePath := append(teamPath, "game") + if err := db.MkBucketPath(gamePath); err != nil { + fmt.Println("Error at 52") + return err + } + if err := db.SetValue(append(gamePath), "name", ""); err != nil { + fmt.Println("Error at 56") + return err + } + return db.MkBucketPath(append(gamePath, "screenshots")) +} + +func dbIsValidTeam(nm string) bool { + var err error + var currJam string + if err = db.OpenDB(); err != nil { + return false + } + defer db.CloseDB() + + if currJam, err = dbGetCurrentJam(); err != nil { + return false + } + teamPath := []string{"jams", currJam, "teams"} + if teamUids, err := db.GetBucketList(teamPath); err == nil { + for _, v := range teamUids { + if tstName, err := db.GetValue(append(teamPath, v), "name"); err == nil { + if tstName == nm { + return true + } + } + } + } + return false +} + +func dbGetAllTeams() []Team { + var ret []Team + var err error + var currJam string + if err = db.OpenDB(); err != nil { + return ret + } + defer db.CloseDB() + + if currJam, err = dbGetCurrentJam(); err != nil { + return ret + } + teamPath := []string{"jams", currJam, "teams"} + if teamUids, err := db.GetBucketList(teamPath); err != nil { + for _, v := range teamUids { + if tm := dbGetTeam(v); tm != nil { + ret = append(ret, *tm) + } + } + } + return ret +} + +func dbGetTeam(id string) *Team { + var err error + var currJam string + if err = db.OpenDB(); err != nil { + return nil + } + defer db.CloseDB() + + if currJam, err = dbGetCurrentJam(); err != nil { + return nil + } + teamPath := []string{"jams", currJam, "teams", id} + tm := new(Team) + if tm.Name, err = db.GetValue(teamPath, "name"); err != nil { + return nil + } + return tm +} + +func dbGetTeamByName(nm string) *Team { + var err error + var currJam string + if err = db.OpenDB(); err != nil { + return nil + } + defer db.CloseDB() + + if currJam, err = dbGetCurrentJam(); err != nil { + return nil + } + teamPath := []string{"jams", currJam, "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 dbDeleteTeam(id string) error { + var err error + var currJam string + if err = db.OpenDB(); err != nil { + return err + } + defer db.CloseDB() + + if currJam, err = dbGetCurrentJam(); err != nil { + return err + } + teamPath := []string{"jams", currJam, "teams"} + return db.DeleteBucket(teamPath, id) +} diff --git a/model_users.go b/model_users.go new file mode 100644 index 0000000..dfbfa68 --- /dev/null +++ b/model_users.go @@ -0,0 +1,76 @@ +package main + +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 dbGetAllUsers() []string { + if err := db.OpenDB(); err != nil { + return []string{} + } + defer db.CloseDB() + + usrs, err := db.GetBucketList([]string{"users"}) + if err != nil { + return []string{} + } + return usrs +} + +func dbIsValidUserEmail(email string) bool { + if err := db.OpenDB(); err != nil { + return false + } + defer db.CloseDB() + + usrPath := []string{"users", email} + _, err := db.GetValue(usrPath, "password") + return err == nil +} + +func dbCheckCredentials(email, pw string) error { + var err error + if err = db.OpenDB(); err != nil { + return err + } + defer db.CloseDB() + + var uPw string + usrPath := []string{"users", email} + if uPw, err = db.GetValue(usrPath, "password"); err != nil { + return err + } + return bcrypt.CompareHashAndPassword([]byte(uPw), []byte(pw)) +} + +// dbUpdateUserPassword +// 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 { + cryptPw, cryptError := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if cryptError != nil { + return cryptError + } + if err := db.OpenDB(); err != nil { + return err + } + defer db.CloseDB() + + usrPath := []string{"users", email} + return db.SetValue(usrPath, "password", string(cryptPw)) +} + +func dbDeleteUser(email string) error { + var err error + if err = db.OpenDB(); err != nil { + return err + } + defer db.CloseDB() + + return db.DeleteBucket([]string{"users"}, email) +} diff --git a/publicEndpoints.go b/publicEndpoints.go index 6437fa3..3eaa607 100644 --- a/publicEndpoints.go +++ b/publicEndpoints.go @@ -3,41 +3,16 @@ package main import ( "fmt" "net/http" - - "github.com/gorilla/sessions" ) func initPublicPage(w http.ResponseWriter, req *http.Request) *pageData { - p := new(pageData) - // Get session - var err error - var s *sessions.Session - if s, err = sessionStore.Get(req, site.SessionName); err != nil { - http.Error(w, err.Error(), 500) - return p - } - p.session = new(pageSession) - p.session.session = s - p.session.req = req - p.session.w = w - - p.Site = site - p.SubTitle = "GameJam Voting" - p.Stylesheets = make([]string, 0, 0) - p.Stylesheets = append(p.Stylesheets, "/assets/css/pure-min.css") - p.Stylesheets = append(p.Stylesheets, "/assets/font-awesome/css/font-awesome.min.css") - p.Stylesheets = append(p.Stylesheets, "/assets/css/gjvote.css") - p.HeaderScripts = make([]string, 0, 0) - p.HeaderScripts = append(p.HeaderScripts, "/assets/js/snack-min.js") - p.Scripts = make([]string, 0, 0) - p.Scripts = append(p.Scripts, "/assets/js/gjvote.js") - p.FlashMessage, p.FlashClass = p.session.getFlashMessage() + p := InitPageData(w, req) return p } func handleMain(w http.ResponseWriter, req *http.Request) { page := initPublicPage(w, req) - page.SubTitle = "Place your Vote!" + page.SubTitle = "!" for _, tmpl := range []string{ "htmlheader.html", "main.html", diff --git a/templates/admin-addteam.html b/templates/admin-addteam.html new file mode 100644 index 0000000..98e2f8a --- /dev/null +++ b/templates/admin-addteam.html @@ -0,0 +1,12 @@ +
+
+
+
+ + +
+ + +
+
+
diff --git a/templates/admin-editteam.html b/templates/admin-editteam.html new file mode 100644 index 0000000..ae6a2a3 --- /dev/null +++ b/templates/admin-editteam.html @@ -0,0 +1,42 @@ +
+
+
+
+ {{ .TemplateData.Name }} +
+ +
+ + +
+ +
+ Cancel + + +
+
+
+
+ diff --git a/templates/admin-teams.html b/templates/admin-teams.html new file mode 100644 index 0000000..7d4e8c4 --- /dev/null +++ b/templates/admin-teams.html @@ -0,0 +1,31 @@ +
+ Add Team +
+ + + + + + + + + {{ range $i, $v := .TemplateData.Teams }} + + + + + {{ end }} + + +