diff --git a/admin_endpoints.go b/admin_endpoints.go index 52251c6..c901fe9 100644 --- a/admin_endpoints.go +++ b/admin_endpoints.go @@ -62,7 +62,7 @@ func handleAdminSetMode(w http.ResponseWriter, req *http.Request, page *pageData if err != nil { page.session.setFlashMessage("Invalid Mode: "+vars["id"], "error") } - if dbSetPublicSiteMode(newMode) != nil { + if db.setPublicSiteMode(newMode) != nil { page.session.setFlashMessage("Invalid Mode: "+vars["id"], "error") } redirect("/admin", w, req) @@ -74,7 +74,7 @@ func handleAdminSetAuthMode(w http.ResponseWriter, req *http.Request, page *page if err != nil { page.session.setFlashMessage("Invalid Authentication Mode: "+vars["id"], "error") } - if db.setAuthMode(newMode) != nil { + if db.site.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 8fc755f..6003a94 100644 --- a/admin_games.go +++ b/admin_games.go @@ -33,7 +33,7 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData) if tm != nil { switch vars["function"] { case "save": - gm := newGame(tm.UUID) + gm := db.newGame(tm.UUID) gm.Name = req.FormValue("gamename") gm.Link = req.FormValue("gamelink") gm.Description = req.FormValue("gamedesc") diff --git a/main.go b/main.go index 29dcd08..be09458 100644 --- a/main.go +++ b/main.go @@ -60,9 +60,14 @@ var sessionSecret = "JCOP5e8ohkTcOzcSMe74" var sessionStore = sessions.NewCookieStore([]byte(sessionSecret)) var site *siteData var r *mux.Router +var m *model func main() { - db = new(gjDatabase) + var err error + if m, err = NewModel(); err != nil { + errorExit("Unable to initialize Model: " + err.Error()) + } + loadConfig() site.save() initialize() @@ -70,13 +75,10 @@ func main() { r = mux.NewRouter() r.StrictSlash(true) - if site.DevMode { + if m.site.DevMode { fmt.Println("Operating in Development Mode") } - //s := http.StripPrefix("/assets/", http.FileServer(FS(site.DevMode))) - //http.Dir(site.ServerDir+"assets/"))) - //r.PathPrefix("/assets/").Handler(s) - r.PathPrefix("/assets/").Handler(http.FileServer(FS(site.DevMode))) + r.PathPrefix("/assets/").Handler(http.FileServer(FS(m.site.DevMode))) // Public Subrouter pub := r.PathPrefix("/").Subrouter() @@ -110,8 +112,6 @@ func main() { } func loadConfig() { - site = db.getSiteConfig() - if len(os.Args) > 1 { for _, v := range os.Args { key := v @@ -124,27 +124,27 @@ func loadConfig() { } switch key { case "-title": - site.Title = val - fmt.Print("Set site title: ", site.Title, "\n") + m.site.Title = val + fmt.Print("Set site title: ", m.site.Title, "\n") case "-port": var tryPort int var err error if tryPort, err = strconv.Atoi(val); err != nil { fmt.Print("Invalid port given: ", val, " (Must be an integer)\n") - tryPort = site.Port + tryPort = m.site.Port } // TODO: Make sure a valid port number is given - site.Port = tryPort + m.site.Port = tryPort case "-session-name": - site.SessionName = val + m.site.SessionName = val case "-server-dir": // TODO: Probably check if the given directory is valid - site.ServerDir = val + m.site.ServerDir = val case "-help", "-h", "-?": printHelp() done() case "-dev": - site.DevMode = true + m.site.DevMode = true case "-reset-defaults": resetToDefaults() done() @@ -154,10 +154,9 @@ func loadConfig() { } func initialize() { - // Check if the database has been created - assertError(db.initialize()) - - if !db.hasUser() { + // Test if we have an admin user first + if !m.hasUser() { + // Nope, create one reader := bufio.NewReader(os.Stdin) fmt.Println("Create new Admin user") fmt.Print("Email: ") @@ -175,29 +174,26 @@ func initialize() { fmt.Println("Entered Passwords don't match!") } } - assertError(db.updateUserPassword(email, string(pw1))) + assertError(m.updateUserPassword(email, string(pw1))) } - if !db.hasCurrentJam() { + + // Now test if the 'current jam' is named + if m.jam.Name == "" { reader := bufio.NewReader(os.Stdin) fmt.Println("Create New Game Jam") fmt.Print("GameJam Name: ") gjName, _ := reader.ReadString('\n') gjName = strings.TrimSpace(gjName) - if db.setCurrentJam(gjName) != nil { + if db.setJamName(gjName) != nil { fmt.Println("Error saving Current Jam") } } - jmNm, err := db.getCurrentJam() - if err == nil { + if m.jam.Name != "" { fmt.Println("Current Jam Name: " + jmNm) } else { - fmt.Println(err.Error()) + fmt.Println("No Jam Name Specified") } - - // Load all votes into memory - site.Votes = db.getAllVotes() - site.Teams = db.getAllTeams() } func loggingHandler(h http.Handler) http.Handler { @@ -261,7 +257,7 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { } p.HideAdminMenu = true - if p.CurrentJam, err = db.getCurrentJam(); err != nil { + if p.CurrentJam = db.getJamName(); p.CurrentJam != "" { p.FlashMessage = "Error Loading Current GameJam: " + err.Error() p.FlashClass = "error" } @@ -274,7 +270,7 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData { // Public Mode p.PublicMode = db.getPublicSiteMode() // Authentication Mode - p.AuthMode = db.getAuthMode() + p.AuthMode = db.site.getAuthMode() return p } @@ -310,12 +306,12 @@ func redirect(url string, w http.ResponseWriter, req *http.Request) { } func resetToDefaults() { - def := NewSiteData() + def := NewSiteData(m) fmt.Println("Reset settings to defaults?") - fmt.Print(site.Title, " -> ", def.Title, "\n") - fmt.Print(site.Port, " -> ", def.Port, "\n") - fmt.Print(site.SessionName, " -> ", def.SessionName, "\n") - fmt.Print(site.ServerDir, " -> ", def.ServerDir, "\n") + fmt.Print(m.site.Title, " -> ", def.Title, "\n") + fmt.Print(m.site.Port, " -> ", def.Port, "\n") + fmt.Print(m.site.SessionName, " -> ", def.SessionName, "\n") + fmt.Print(m.site.ServerDir, " -> ", def.ServerDir, "\n") fmt.Println("Are you sure? (y/N): ") reader := bufio.NewReader(os.Stdin) conf, _ := reader.ReadString('\n') diff --git a/model.go b/model.go index 488f262..a878ac9 100644 --- a/model.go +++ b/model.go @@ -2,45 +2,66 @@ package main import ( "errors" - "strings" "github.com/br0xen/boltease" ) -// TODO: I don't think we need a global for this... -var db *currJamDb +// model stores the current jam in memory, and has the ability to access archived dbs +type model struct { + bolt *boltease.DB + dbOpened int + dbFileName string -// gjdb is the interface that works for the current jam database as well as all archive databases -type gjdb interface { - getDB() *boltease.DB - open() error - close() error - - getJamName() string - setJamName(nm string) + site *siteData // Configuration data for the site + jam *Gamejam // The currently active gamejam + clients []Client // Web clients that have connected to the server } +// Authentication Modes: Flags for which clients are able to vote const ( AuthModeAuthentication = iota AuthModeAll AuthModeError ) -// currJamDb also contains site configuration information -type currJamDb struct { - bolt *boltease.DB - dbOpened int +// Update Flags: Which parts of the model need to be updated +const ( + UpdateSiteData = iota + UpdateJamData +) + +func NewModel() (*model, error) { + var err error + m := new(model) + + m.dbFileName = DbName + if err = m.openDB(); err != nil { + return nil, errors.New("Unable to open DB: " + err.Error()) + } + defer m.closeDB() + + // Initialize the DB + if err = m.initDB(); err != nil { + return nil, errors.New("Unable to initialize DB: " + err.Error()) + } + + // Load the site data + m.site = m.LoadSiteData() + + // Load the jam data + m.jam = m.LoadCurrentJam() + + // Load web clients + m.clients = m.LoadAllClients() + + return &m, nil } -func (db *currJamDb) getDB() *boltease.DB { - return db.bolt -} - -func (db *currJamDb) open() error { - db.dbOpened += 1 +func (m *model) openDB() error { + m.dbOpened += 1 if db.dbOpened == 1 { var err error - db.bolt, err = boltease.Create(DbName, 0600, nil) + m.bolt, err = boltease.Create(m.dbFileName, 0600, nil) if err != nil { return err } @@ -48,112 +69,58 @@ func (db *currJamDb) open() error { return nil } -func (db *currJamDb) close() error { - db.dbOpened -= 1 - if db.dbOpened == 0 { - return db.bolt.CloseDB() +func (m *model) closeDB() error { + m.dbOpened -= 1 + if m.dbOpened == 0 { + return m.bolt.CloseDB() } return nil } -// initialize the 'current jam' database -func (db *currJamDb) initialize() error { +func (m *model) initDB() error { var err error - if err = db.open(); err != nil { + if err = m.openDB(); err != nil { return err } - defer db.close() + defer m.closeDB() // Create the path to the bucket to store admin users - if err := db.bolt.MkBucketPath([]string{"users"}); err != nil { + if err = m.bolt.MkBucketPath([]string{"users"}); err != nil { return err } - // Create the path to the bucket to store jam informations - if err := db.bolt.MkBucketPath([]string{"jam"}); err != nil { + // Create the path to the bucket to store the web clients + if err = m.bolt.MkBucketPath([]string{"clients"}); err != nil { + return err + } + // Create the path to the bucket to store the current jam & teams + if err = m.bolt.MkBucketPath([]string{"jam", "teams"}); err != nil { + return err + } + // Create the path to the bucket to store the list of archived jams + if err = m.bolt.MkBucketPath([]string{"archive"}); err != nil { return err } // Create the path to the bucket to store site config data - return db.bolt.MkBucketPath([]string{"site"}) + return m.bolt.MkBucketPath([]string{"site"}) } -func (db *currJamDb) getSiteConfig() *siteData { - var ret *siteData - def := NewSiteData() +// saveChanges saves any parts of the model that have been flagged as changed to the database +func (m *model) saveChanges() error { var err error - if err = db.open(); err != nil { - return def - } - defer db.close() - - ret = new(siteData) - siteConf := []string{"site"} - if ret.Title, err = db.bolt.GetValue(siteConf, "title"); err != nil { - ret.Title = def.Title - } - if ret.Port, err = db.bolt.GetInt(siteConf, "port"); err != nil { - ret.Port = def.Port - } - if ret.SessionName, err = db.bolt.GetValue(siteConf, "session-name"); err != nil { - ret.SessionName = def.SessionName - } - if ret.ServerDir, err = db.bolt.GetValue(siteConf, "server-dir"); err != nil { - ret.ServerDir = def.ServerDir - } - return ret -} - -func (db *currJamDb) setJamName(name string) error { - var err error - if err = db.open(); err != nil { + if err = m.openDB(); err != nil { return err } - defer db.close() + defer m.closeDB() - return db.bolt.SetValue([]string{"site"}, "current-jam", name) -} - -func (db *currJamDb) getJamName() string { - var ret string - var err error - if err = db.open(); err != nil { - return "", err + if m.site.needsSave() { + if err = m.site.saveToDB(); err != nil { + return err + } } - defer db.close() - - ret, err = db.bolt.GetValue([]string{"site"}, "current-jam") - - if err == nil && strings.TrimSpace(ret) == "" { - return ret, errors.New("No Jam Name Specified") + if m.jam.needsSave() { + if err = m.jam.saveToDB(); err != nil { + return err + } } - return ret, err -} - -func (db *currJamDb) getAuthMode() int { - if ret, err := db.bolt.GetInt([]string{"site"}, "auth-mode"); err != nil { - return AuthModeAuthentication - } else { - return ret - } -} - -func (db *currJamDb) setAuthMode(mode int) error { - if mode < 0 || mode >= AuthModeError { - return errors.New("Invalid site mode") - } - return db.bolt.SetInt([]string{"site"}, "auth-mode", mode) -} - -func (db *currJamDb) getPublicSiteMode() int { - if ret, err := db.bolt.GetInt([]string{"site"}, "public-mode"); err != nil { - return SiteModeWaiting - } else { - return ret - } -} - -func (db *currJamDb) setPublicSiteMode(mode int) error { - if mode < 0 || mode >= SiteModeError { - return errors.New("Invalid site mode") - } - return db.bolt.SetInt([]string{"site"}, "public-mode", mode) + return nil } diff --git a/model_clients.go b/model_clients.go index 4a0790a..2d60e9e 100644 --- a/model_clients.go +++ b/model_clients.go @@ -1,12 +1,15 @@ package main import ( - "fmt" "strconv" "strings" "time" ) +/** + * Client + * A client is a system that is connecting to the web server + */ type Client struct { UUID string Auth bool @@ -14,57 +17,53 @@ type Client struct { IP string } -func (db *currJamDb) getAllClients() []Client { - var ret []Client +// Load all clients +func (m *model) LoadAllClients() []Client { var err error - if err = db.open(); err != nil { - return ret + if err = m.openDB(); err != nil { + return err } - defer db.close() + defer m.closeDB() var clientUids []string - if clientUids, err = db.bolt.GetBucketList([]string{"clients"}); err != nil { - return ret + if clientUids, err = m.bolt.GetBucketList([]string{"clients"}); err != nil { + return err } for _, v := range clientUids { - if cl := db.getClient(v); cl != nil { - ret = append(ret, *cl) + if cl := m.LoadClient(v); cl != nil { + m.clients = append(m.clients, *cl) } } - return ret } -func (db *currJamDb) getClient(id string) *Client { +// Load a client from the DB and return it +func (m *model) LoadClient(clId string) *Client { var err error - if err = db.open(); err != nil { + if err = m.openDB(); err != nil { return nil } - defer db.close() + defer m.closeDB() cl := new(Client) cl.UUID = id - 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") + cl.Auth, _ = m.bolt.GetBool([]string{"clients", id}, "auth") + cl.Name, _ = m.bolt.GetValue([]string{"clients", id}, "name") + cl.IP, _ = m.bolt.GetValue([]string{"clients", id}, "ip") return cl } -func (db *currJamDb) getClientByIp(ip string) *Client { - var err error - if err = db.open(); err != nil { - return nil - } - defer db.close() - - allClients := db.getAllClients() - for i := range allClients { - if allClients[i].IP == ip { - return &allClients[i] +func (m *model) getClientById(ip string) *Client { + for i := range m.clients { + if m.clients[i].IP == ip { + return &m.clients[i].IP } } return nil } +/** + * OLD FUNCTIONS + */ func (c *Client) save() error { var err error if err = db.open(); err != nil { @@ -81,62 +80,6 @@ func (c *Client) save() error { 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 { diff --git a/model_gamejam.go b/model_gamejam.go index 1c343af..82ef444 100644 --- a/model_gamejam.go +++ b/model_gamejam.go @@ -1,12 +1,11 @@ package main -import ( - "time" +import "time" - "github.com/br0xen/boltease" -) - -// Gamejam is specifically for an archived game jam +/** + * Gamejam + * Gamejam is the struct for any gamejam (current or archived) + */ type Gamejam struct { UUID string Name string @@ -14,74 +13,56 @@ type Gamejam struct { Teams []Team Votes []Vote - db *boltease.DB - dbOpened int + m *model + updates []string } -// Archived Gamejam data is stored in it's own file to keep things nice and organized -func (gj *Gamejam) openDB() error { - gj.dbOpened += 1 - if gj.dbOpened == 1 { - var err error - gj.db, err = boltease.Create(gj.UUID+".db", 0600, nil) - if err != nil { - return err - } - } - return nil +func NewGamejam(m *model) *Gamejam { + gj := new(Gamejam) + gj.m = m + return gj } -func (gj *Gamejam) closeDB() error { - gj.dbOpened -= 1 - if gj.dbOpened == 0 { - return gj.db.CloseDB() - } - return nil -} - -// archiveGameJam creates a separate gamejam file and populates it with the -// given name, teams, and votes -func archiveGamejam(nm string, teams []Team, votes []Vote) error { - // TODO - return nil -} - -// dbGetGamejam returns a gamejam with the given uuid -// or nil if it couldn't be found -func dbGetGamejam(id string) *Gamejam { - var err error - if err = openDatabase(); err != nil { - return nil - } - defer closeDatabase() - - ret := Gamejam{UUID: id} - // TODO: Load gamejam teams, other details - return ret -} - -// dbGetGamejamByName looks for a gamejam with the given name -// and returns it, or it returns nil if it couldn't find it -func dbGetGamejamByName(nm string) *Gamejam { - var err error - if err = openDatabase(); err != nil { +func (m *model) LoadCurrentJam() *Gamejam { + if err := m.openDB(); err != nil { return err } - defer closeDatabase() + defer m.closeDB() - var gjid string - if gjs, err = db.GetBucketList([]string{"gamejams"}); err == nil { - for _, v := range gjUids { - tstNm, _ := db.GetValue([]string{"gamejams", v}, "name") - if tstNm == nm { - // We've got it - gjid = v - break - } + var err error + jamPath := []string{"jam"} + gj := NewGamejam(m) + gj.Name, _ = m.bolt.GetValue(jamPath, "name") + + // Load all teams + gj.Teams = gj.LoadAllTeams() + + // Load all votes + gj.Votes = gj.LoadAllVotes() + + return gj +} + +func (gj *Gamejam) getTeamByUUID(uuid string) *Team { + for i := range gj.Teams { + if gj.Teams[i].UUID == uuid { + return &gj.Teams[i] } } - if gjid == "" { - return nil - } - return dbGetGamejam(gjid) + return nil +} + +func (gj *Gamejam) needsSave() bool { + return len(updates) > 0 +} + +func (gj *Gamejam) saveToDB() error { + if err := s.m.openDB(); err != nil { + return err + } + defer s.m.closeDB() + + for i := range updates { + // TODO: Save + } } diff --git a/model_games.go b/model_games.go index d0fddbf..6d325ae 100644 --- a/model_games.go +++ b/model_games.go @@ -2,6 +2,10 @@ package main import "errors" +/** + * Game + * A team's game, including links, description, and screenshots + */ type Game struct { Name string TeamId string @@ -18,8 +22,87 @@ type Screenshot struct { Filetype string } +// Load a team's game from the DB and return it +func (gj *Gamejam) LoadTeamGame(tmId string) *Game { + var err error + if err = gj.m.openDB(); err != nil { + return nil + } + defer gj.m.closeDB() + + gamePath := []string{"jam", "teams", tmId, "game"} + gm := new(Game) + gm.TeamId = tm.UUID + if gm.Name, err = gj.m.bolt.GetValue(gamePath, "name"); err != nil { + gm.Name = "" + } + if gm.Description, err = gj.m.bolt.GetValue(gamePath, "description"); err != nil { + gm.Description = "" + } + if gm.Link, err = gj.m.bolt.GetValue(gamePath, "link"); err != nil { + gm.Link = "" + } + // Now get the game screenshots + gm.Screenshots = gj.LoadTeamGameScreenshots(tmId) + + return &gm +} + +func (gj *Gamejam) LoadTeamGameScreenshots(tmId string) []Screenshot { + var err error + if err = gj.m.openDB(); err != nil { + return nil + } + defer gj.m.closeDB() + + var ret []Screenshot + ssBktPath := []string{"jam", "teams", tmId, "game", "screenshots"} + var ssIds []string + ssIds, _ = gj.m.bolt.GetBucketList(ssBktPath) + for _, v := range ssIds { + ssLd := gj.LoadTeamGameScreenshot(tmId, v) + if ssLd != nil { + ret = append(ret, ssLd) + } + } + return ret +} + +func (gj *Gamejam) LoadTeamGameScreenshot(tmId, ssId string) *Screenshot { + var err error + if err = gj.m.openDB(); err != nil { + return nil + } + defer gj.m.closeDB() + + var ret []Screenshot + ssPath := []string{"jam", "teams", tmId, "game", "screenshots", ssId} + ret := new(Screenshot) + ret.UUID = ssId + if ret.Description, err = gj.m.bolt.GetValue(ssPath, "description"); err != nil { + return nil + } + if ret.Image, err = gj.m.bolt.GetValue(ssPath, "image"); err != nil { + return nil + } + if ret.Thumbnail, err = gj.m.bolt.GetValue(ssPath, "thumbnail"); err != nil { + return nil + } + if ret.Thumbnail == "" { + ret.Thumbnail = ret.Image + } + if ret.Filetype, err = gj.m.bolt.GetValue(ssPath, "filetype"); err != nil { + return nil + } + return ret +} + +/** + * OLD FUNCTIONS + */ + // Create a new game object, must have a valid team id -func newGame(tmId string) *Game { +func (db *currJamDb) newGame(tmId string) *Game { var err error if err = db.open(); err != nil { return nil @@ -33,6 +116,15 @@ func newGame(tmId string) *Game { return &Game{TeamId: tmId} } +func (db *currJamDb) getAllGames() []Game { + var ret []Game + tms := db.getAllTeams() + for i := range tms { + ret = append(ret, *tms[i].getGame()) + } + return ret +} + func (gm *Game) save() error { var err error if err = db.open(); err != nil { @@ -67,12 +159,3 @@ func (gm *Game) save() error { return err } - -func (db *gjDatabase) getAllGames() []Game { - var ret []Game - tms := db.getAllTeams() - for i := range tms { - ret = append(ret, *tms[i].getGame()) - } - return ret -} diff --git a/model_site.go b/model_site.go deleted file mode 100644 index 2a0125f..0000000 --- a/model_site.go +++ /dev/null @@ -1,62 +0,0 @@ -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_sitedata.go b/model_sitedata.go new file mode 100644 index 0000000..5f0cdfe --- /dev/null +++ b/model_sitedata.go @@ -0,0 +1,112 @@ +package main + +/** + * SiteData + * Contains configuration for the website + */ +type siteData struct { + title string + port int + sessionName string + serverDir string + authMode int + publicMode int + + DevMode bool + Mode int + + m *model + changed bool +} + +// NewSiteData returns a siteData object with the default values +func NewSiteData(m *model) *siteData { + ret := new(siteData) + ret.Title = "ICT GameJam" + ret.Port = 8080 + ret.SessionName = "ict-gamejam" + ret.ServerDir = "./" + ret.m = m + return ret +} + +// Mode flags for how the site is currently running +const ( + SiteModeWaiting = iota + SiteModeVoting + SiteModeError +) + +// load the site data out of the database +// If fields don't exist in the DB, don't clobber what is already in s +func (s *siteData) loadFromDB() error { + if err := s.m.openDB(); err != nil { + return err + } + defer s.m.closeDB() + + siteConf := []string{"site"} + if title, err := s.m.bolt.GetValue(siteConf, "title"); err == nil { + s.Title = title + } + if port, err := s.m.bolt.GetInt(siteConf, "port"); err == nil { + s.Port = port + } + if sessionName, err = s.m.bolt.GetValue(siteConf, "session-name"); err == nil { + s.SessionName = sessionName + } + if serverDir, err = s.m.bolt.GetValue(siteConf, "server-dir"); err == nil { + s.ServerDir = serverDir + } + s.changed = false + return nil +} + +func (s *siteData) needsSave() bool { + return s.changed +} + +func (s *siteData) saveToDB() error { + if err := s.m.openDB(); err != nil { + return err + } + defer s.m.closeDB() + + siteConf := []string{"site"} + if err = s.m.bolt.SetValue(siteConf, "title", s.Title); err != nil { + return err + } + if err = s.m.bolt.SetInt(siteConf, "port", s.Port); err != nil { + return err + } + if err = s.m.bolt.SetValue(siteConf, "session-name", s.SessionName); err != nil { + return err + } + if err = s.m.bolt.SetValue(siteConf, "server-dir", s.ServerDir); err != nil { + return err + } + s.changed = false + return nil +} + +func (s *siteData) getAuthMode() int { + return s.authMode +} + +func (s *siteData) setAuthMode(mode int) { + if mode != s.authMode { + s.authMode = mode + s.changed = true + } +} + +func (s *siteData) getPublicMode() int { + return s.publicMode +} + +func (s *siteData) setPublicMode(mode int) { + if mode != s.publicMode { + s.publicMode = mode + s.changed = true + } +} diff --git a/model_teams.go b/model_teams.go index 5f95935..aa8844e 100644 --- a/model_teams.go +++ b/model_teams.go @@ -6,6 +6,9 @@ import ( "github.com/pborman/uuid" ) +/** + * Team + */ type Team struct { UUID string Name string @@ -13,8 +16,130 @@ type Team struct { Game *Game } -// newTeam creates a team with name nm and stores it in the DB -func (db *gjDatabase) newTeam(nm string) error { +// Create a team +func NewTeam(nm string) *Team { + return &Team{ + UUID: uuid.New(), + Name: nm, + } +} + +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 +} + +// LoadAllTeams loads all teams for the jam out of the database +func (gj *Gamejam) LoadAllTeams() []Team { + var err error + var ret []Team + if err = gj.m.openDB(); err != nil { + return err + } + defer gj.m.closeDB() + + teamsPath := []string{"jam", "teams"} + if tmUUIDs, err = m.bolt.GetBucketList(mbrsPath); err != nil { + return ret + } + for _, v := range tmUUIDs { + tm := gj.LoadTeam(v) + if tm != nil { + ret = append(ret, tm) + } + } + return ret +} + +// Load a team out of the database +func (gj *Gamejam) LoadTeam(uuid string) *Team { + var err error + if err = gj.m.openDB(); err != nil { + return err + } + defer gj.m.closeDB() + + // Team Data + tmPath := []string{"jam", "teams", uuid} + tm := new(Team) + tm.UUID = uuid + if tm.Name, err = gj.m.bolt.GetValue(tmPath, "name"); err != nil { + return nil + } + + // Team Members + tm.Members = gj.LoadTeamMembers(uuid) + + // Team Game + tm.Game = gj.LoadTeamGame(uuid) + +} + +// Load the members of a team from the DB and return them +func (gj *Gamejam) LoadTeamMembers(tmId string) []TeamMember { + var err error + var ret []TeamMember + if err = gj.m.openDB(); err != nil { + return ret + } + defer gj.m.closeDB() + + // Team Members + var memberUuids []string + mbrsPath := []string{"jam", "teams", tmId, "members"} + if memberUuids, err = gj.m.bolt.GetBucketList(mbrsPath); err == nil { + for _, v := range memberUuids { + mbr := gj.LoadTeamMember(tmId, v) + if mbr != nil { + ret = append(ret, mbr) + } + } + } + return ret +} + +// Load a team member from the DB and return it +func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) *TeamMember { + var err error + if err = gj.m.openDB(); err != nil { + return nil + } + defer gj.m.closeDB() + + mbr := new(TeamMember) + mbr.UUID = v + teamMbrPath := append(mbrsPath, mbr.UUID) + // Name is the only required field + if mbr.Name, err = gj.m.bolt.GetValue(teamMbrPath, "name"); err != nil { + return nil + } + if mbr.SlackId, err = gj.m.bolt.GetValue(teamMbrPath, "slackid"); err != nil { + mbr.SlackId = "" + } + if mbr.Twitter, err = gj.m.bolt.GetValue(teamMbrPath, "twitter"); err != nil { + mbr.Twitter = "" + } + if mbr.Email, err = gj.m.bolt.GetValue(teamMbrPath, "email"); err != nil { + mbr.Email = "" + } + return mbr +} + +/** + * OLD FUNCTIONS + */ + +// NewTeam creates a team with name nm and stores it in the DB +func NewTeam(nm string) error { var err error if err = db.open(); err != nil { return err @@ -45,7 +170,7 @@ func (db *gjDatabase) newTeam(nm string) error { } // getTeam returns a team with the given id, or nil -func (db *gjDatabase) getTeam(id string) *Team { +func (db *currJamDb) getTeam(id string) *Team { var err error if err = db.open(); err != nil { return nil @@ -64,7 +189,7 @@ func (db *gjDatabase) getTeam(id string) *Team { } // This function returns the team for a specific member -func (db *gjDatabase) getTeamForMember(mbrid string) (*Team, error) { +func (db *currJamDb) getTeamForMember(mbrid string) (*Team, error) { var err error if err = db.open(); err != nil { return nil, err @@ -87,7 +212,7 @@ func (db *gjDatabase) getTeamForMember(mbrid string) (*Team, error) { } // getAllTeams returns all teams in the database -func (db *gjDatabase) getAllTeams() []Team { +func (db *currJamDb) getAllTeams() []Team { var ret []Team var err error if err = db.open(); err != nil { @@ -109,7 +234,7 @@ func (db *gjDatabase) getAllTeams() []Team { } // getTeamByName returns a team with the given name or nil -func (db *gjDatabase) getTeamByName(nm string) *Team { +func (db *currJamDb) getTeamByName(nm string) *Team { var err error if err = db.open(); err != nil { return nil @@ -212,45 +337,6 @@ func (tm *Team) saveScreenshot(ss *Screenshot) error { 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 { @@ -262,20 +348,6 @@ func (tm *Team) deleteScreenshot(ssId string) error { 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 { diff --git a/model_users.go b/model_users.go index bcd051c..f179600 100644 --- a/model_users.go +++ b/model_users.go @@ -2,46 +2,49 @@ package main import "golang.org/x/crypto/bcrypt" -// dbHasUser +// These are all model functions that have to do with users + // Returns true if there are any users in the database -func (db *gjDatabase) hasUser() bool { - return len(db.getAllUsers()) > 0 +func (m *model) hasUser() bool { + return len(m.getAllUsers()) > 0 } -func (db *gjDatabase) getAllUsers() []string { - if err := db.open(); err != nil { +func (m *model) getAllUsers() []string { + if err := m.openDB(); err != nil { return []string{} } - defer db.close() + defer m.closeDB() - usrs, err := db.bolt.GetBucketList([]string{"users"}) + usrs, err := m.bolt.GetBucketList([]string{"users"}) if err != nil { return []string{} } return usrs } -func (db *gjDatabase) isValidUserEmail(email string) bool { - if err := db.open(); err != nil { +// Is the given email one that is in our DB? +func (m *model) isValidUserEmail(email string) bool { + if err := m.openDB(); err != nil { return false } - defer db.close() + defer m.closeDB() usrPath := []string{"users", email} - _, err := db.bolt.GetValue(usrPath, "password") + _, err := m.bolt.GetValue(usrPath, "password") return err == nil } -func (db *gjDatabase) checkCredentials(email, pw string) error { +// Is the email and pw given valid? +func (m *model) checkCredentials(email, pw string) error { var err error - if err = db.open(); err != nil { + if err = m.openDB(); err != nil { return err } - defer db.close() + defer m.closeDB() var uPw string usrPath := []string{"users", email} - if uPw, err = db.bolt.GetValue(usrPath, "password"); err != nil { + if uPw, err = m.bolt.GetValue(usrPath, "password"); err != nil { return err } return bcrypt.CompareHashAndPassword([]byte(uPw), []byte(pw)) @@ -51,26 +54,26 @@ func (db *gjDatabase) checkCredentials(email, pw string) error { // 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 (db *gjDatabase) updateUserPassword(email, password string) error { +func (m *model) updateUserPassword(email, password string) error { cryptPw, cryptError := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if cryptError != nil { return cryptError } - if err := db.open(); err != nil { + if err := m.openDB(); err != nil { return err } - defer db.close() + defer m.closeDB() usrPath := []string{"users", email} - return db.bolt.SetValue(usrPath, "password", string(cryptPw)) + return m.bolt.SetValue(usrPath, "password", string(cryptPw)) } -func (db *gjDatabase) deleteUser(email string) error { +func (m *model) deleteUser(email string) error { var err error - if err = db.open(); err != nil { + if err = m.openDB(); err != nil { return err } - defer db.close() + defer m.closeDB() - return db.bolt.DeleteBucket([]string{"users"}, email) + return m.bolt.DeleteBucket([]string{"users"}, email) } diff --git a/model_votes.go b/model_votes.go index f5520e6..4cc0dd6 100644 --- a/model_votes.go +++ b/model_votes.go @@ -1,6 +1,9 @@ package main -import "time" +import ( + "strconv" + "time" +) // A Choice is a ranking of a game in a vote type GameChoice struct { @@ -15,7 +18,64 @@ type Vote struct { Choices []GameChoice } -func (db *gjDatabase) getAllVotes() []Vote { +// LoadAllVotes loads all votes for the jam out of the database +func (gj *Gamejam) LoadAllVotes() []Vote { + var ret []Vote + if err := gj.m.openDB(); err != nil { + return err + } + defer gj.m.closeDB() + + votesPath := []string{"jam", "votes"} + if cliUUIDs, err = m.bolt.GetBucketList(votesPath); err != nil { + return ret + } + for _, cId := range cliUUIDs { + vtsPth := append(votesPath, cId) + if times, err := m.bolt.GetBucketList(vtsPth); err != nil { + // Error reading this bucket, move on to the next + continue + } + for _, t := range times { + vt := gj.LoadVote(cId, t) + if vt != nil { + ret = append(ret, vt) + } + } + } + return ret +} + +// Load a vote from the DB and return it +func (gj *Gamejam) LoadVote(clientId, tm string) *Vote { + var tm time.Time + if tm, err = time.Parse(time.RFC3339, t); err != nil { + return nil + } + vt := new(Vote) + vt.Timestamp = tm + vt.ClientId = cId + vtPth := append(vtsPth, t) + var choices []string + if choices, err = m.bolt.GetKeyList(vtPth); err != nil { + return nil + } + for _, v := range choices { + ch := new(GameChoices) + var rank int + if rank, err = strconv.Atoi(v); err == nil { + ch.Rank = rank + ch.Team, _ = m.bolt.GetValue(vtPth, v) + vt.Choices = append(vt.Choices, *ch) + } + } + return &vt +} + +/** + * OLD FUNCTIONS + */ +func (db *currJamDb) getAllVotes() []Vote { var ret []Vote var err error if err = db.open(); err != nil { diff --git a/public_endpoints.go b/public_endpoints.go index 5d31c4a..47de0d8 100644 --- a/public_endpoints.go +++ b/public_endpoints.go @@ -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 (db.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { + if (db.site.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { page.show("unauthorized.html", w) return } @@ -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 (db.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { + if (db.site.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { page.show("unauthorized.html", w) return }