Compiling... But still work to do.

This commit is contained in:
Brian Buller 2017-10-19 14:53:07 -05:00
parent d1496007fb
commit 1905f2c009
17 changed files with 563 additions and 297 deletions

14
.gitignore vendored
View File

@ -1,11 +1,11 @@
# Ignore the binaries # Ignore the binaries
gjvote ictgj-voting
gjvote.darwin64 ictgj-voting.darwin64
gjvote.linux386 ictgj-voting.linux386
gjvote.linux64 ictgj-voting.linux64
gjvote.linuxarm ictgj-voting.linuxarm
gjvote.win386.exe ictgj-voting.win386.exe
gjvote.win64.exe ictgj-voting.win64.exe
# Ignore the DBs # Ignore the DBs
*.db *.db

View File

@ -15,6 +15,6 @@ func handleAdminArchive(w http.ResponseWriter, req *http.Request, page *pageData
type archivePageData struct { type archivePageData struct {
Gamejams []Gamejam Gamejams []Gamejam
} }
apd := new(archivePageData) //apd := new(archivePageData)
} }
} }

View File

@ -30,11 +30,15 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData)
page.SubTitle = "Games" page.SubTitle = "Games"
page.show("admin-games.html", w) page.show("admin-games.html", w)
} else { } else {
tm := m.jam.GetTeamById(teamId) tm, _ := m.jam.GetTeamById(teamId)
if tm != nil { if tm != nil {
switch vars["function"] { switch vars["function"] {
case "save": case "save":
gm := NewGame(tm.UUID) var err error
var gm *Game
if gm, err = NewGame(tm.UUID); err != nil {
page.session.setFlashMessage("Error updating game: "+err.Error(), "error")
}
gm.Name = req.FormValue("gamename") gm.Name = req.FormValue("gamename")
gm.Link = req.FormValue("gamelink") gm.Link = req.FormValue("gamelink")
gm.Description = req.FormValue("gamedesc") gm.Description = req.FormValue("gamedesc")
@ -50,8 +54,13 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData)
} }
redirect("/admin/teams/"+tm.UUID+"#game", w, req) redirect("/admin/teams/"+tm.UUID+"#game", w, req)
case "screenshotdelete": case "screenshotdelete":
var ss *Screenshot
var err error
ssid := vars["subid"] ssid := vars["subid"]
if err := tm.deleteScreenshot(ssid); err != nil { if ss, err = NewScreenshot(tm.UUID, ssid); err != nil {
page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error")
}
if err = m.jam.DeleteScreenshot(ss); err != nil {
page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error")
} }
redirect("/admin/teams/"+tm.UUID+"#game", w, req) redirect("/admin/teams/"+tm.UUID+"#game", w, req)
@ -65,6 +74,8 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData)
func saveScreenshots(tm *Team, req *http.Request) error { func saveScreenshots(tm *Team, req *http.Request) error {
var err error var err error
var ss *Screenshot
file, hdr, err := req.FormFile("newssfile") file, hdr, err := req.FormFile("newssfile")
if err != nil { if err != nil {
return err return err
@ -74,13 +85,13 @@ func saveScreenshots(tm *Team, req *http.Request) error {
if len(hdr.Filename) > extIdx { if len(hdr.Filename) > extIdx {
fltp = hdr.Filename[extIdx+1:] fltp = hdr.Filename[extIdx+1:]
} }
m, _, err := image.Decode(file) mI, _, err := image.Decode(file)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
// We convert everything to jpg // We convert everything to jpg
if err = jpeg.Encode(buf, m, nil); err != nil { if err = jpeg.Encode(buf, mI, nil); err != nil {
return errors.New("Unable to encode image") return errors.New("Unable to encode image")
} }
thm := resize.Resize(200, 0, m, resize.Lanczos3) thm := resize.Resize(200, 0, mI, resize.Lanczos3)
thmBuf := new(bytes.Buffer) thmBuf := new(bytes.Buffer)
var thmString string var thmString string
if fltp == "gif" { if fltp == "gif" {
@ -94,9 +105,13 @@ func saveScreenshots(tm *Team, req *http.Request) error {
} }
thmString = base64.StdEncoding.EncodeToString(thmBuf.Bytes()) thmString = base64.StdEncoding.EncodeToString(thmBuf.Bytes())
return tm.saveScreenshot(&Screenshot{ if ss, err = NewScreenshot(tm.UUID, ""); err != nil {
Image: base64.StdEncoding.EncodeToString(buf.Bytes()), return err
Thumbnail: thmString, }
Filetype: fltp,
}) ss.Image = base64.StdEncoding.EncodeToString(buf.Bytes())
ss.Thumbnail = thmString
ss.Filetype = fltp
return m.jam.SaveScreenshot(ss)
} }

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"net/http" "net/http"
_ "image/gif" _ "image/gif"
@ -10,10 +11,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func refreshTeamsInMemory() {
site.Teams = db.getAllTeams()
}
func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) { func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData) {
vars := mux.Vars(req) vars := mux.Vars(req)
page.SubTitle = "Teams" page.SubTitle = "Teams"
@ -23,17 +20,11 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData)
switch vars["function"] { switch vars["function"] {
case "save": case "save":
name := req.FormValue("teamname") name := req.FormValue("teamname")
if db.getTeamByName(name) != nil { tm := NewTeam("")
// A team with that name already exists tm.Name = name
page.session.setFlashMessage("A team with the name "+name+" already exists!", "error") if err := m.jam.AddTeam(tm); err != nil {
} else { page.session.setFlashMessage("Error adding team: "+err.Error(), "error")
if err := db.newTeam(name); err != nil {
page.session.setFlashMessage(err.Error(), "error")
} else {
page.session.setFlashMessage("Team "+name+" created!", "success")
}
} }
refreshTeamsInMemory()
redirect("/admin/teams", w, req) redirect("/admin/teams", w, req)
default: default:
page.SubTitle = "Add New Team" page.SubTitle = "Add New Team"
@ -41,57 +32,57 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData)
} }
} else if teamId != "" { } else if teamId != "" {
// Functions for existing team // Functions for existing team
tm := db.getTeam(teamId) tm, _ := m.jam.GetTeamById(teamId)
if tm != nil { if tm != nil {
switch vars["function"] { switch vars["function"] {
case "save": case "save":
tm.UUID = teamId
tm.Name = req.FormValue("teamname") tm.Name = req.FormValue("teamname")
if err := tm.save(); err != nil { page.session.setFlashMessage("Team Updated!", "success")
page.session.setFlashMessage("Error updating team: "+err.Error(), "error")
} else {
page.session.setFlashMessage("Team Updated!", "success")
}
refreshTeamsInMemory()
redirect("/admin/teams", w, req) redirect("/admin/teams", w, req)
case "delete": case "delete":
var err error var err error
if err = tm.delete(); err != nil { if err = m.jam.RemoveTeamById(teamId); err != nil {
page.session.setFlashMessage("Error deleting team: "+err.Error(), "error") page.session.setFlashMessage("Error removing team: "+err.Error(), "error")
} else { } else {
page.session.setFlashMessage("Team "+tm.Name+" Deleted", "success") page.session.setFlashMessage("Team "+tm.Name+" Removed", "success")
} }
refreshTeamsInMemory()
redirect("/admin/teams", w, req) redirect("/admin/teams", w, req)
case "savemember": case "savemember":
mbrName := req.FormValue("newmembername") mbrName := req.FormValue("newmembername")
mbr := newTeamMember(mbrName) mbr, err := NewTeamMember(tm.UUID, "")
mbr.SlackId = req.FormValue("newmemberslackid") if err == nil {
mbr.Twitter = req.FormValue("newmembertwitter") mbr.SlackId = req.FormValue("newmemberslackid")
mbr.Email = req.FormValue("newmemberemail") mbr.Twitter = req.FormValue("newmembertwitter")
if err := tm.updateTeamMember(mbr); err != nil { mbr.Email = req.FormValue("newmemberemail")
}
if err := tm.AddTeamMember(mbr); err != nil {
page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") page.session.setFlashMessage("Error adding team member: "+err.Error(), "error")
} else { } else {
page.session.setFlashMessage(mbrName+" added to team!", "success") page.session.setFlashMessage(mbrName+" added to team!", "success")
} }
refreshTeamsInMemory()
redirect("/admin/teams/"+teamId+"#members", w, req) redirect("/admin/teams/"+teamId+"#members", w, req)
case "deletemember": case "deletemember":
m := tm.getTeamMember(req.FormValue("memberid")) var err error
if m != nil { var mbr *TeamMember
if err := tm.deleteTeamMember(m); err != nil { if mbr, err = tm.GetTeamMemberById(req.FormValue("memberid")); err != nil {
page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error") fmt.Println("Error removing team member: " + err.Error())
} else { page.session.setFlashMessage("Error deleting team member", "error")
page.session.setFlashMessage(m.Name+" deleted from team", "success") redirect("/admin/teams/"+teamId+"#members", w, req)
} }
refreshTeamsInMemory() if err = tm.RemoveTeamMemberById(mbr.UUID); err != nil {
fmt.Println("Error removing team member: " + err.Error())
page.session.setFlashMessage("Error deleting team member", "error")
} else { } else {
page.session.setFlashMessage("Couldn't find team member to delete", "error") page.session.setFlashMessage(mbr.Name+" deleted from team", "success")
} }
redirect("/admin/teams/"+teamId+"#members", w, req) redirect("/admin/teams/"+teamId+"#members", w, req)
default: default:
page.SubTitle = "Edit Team" page.SubTitle = "Edit Team"
t := db.getTeam(teamId) t, err := m.jam.GetTeamById(teamId)
if err != nil {
page.session.setFlashMessage("Error loading team: "+err.Error(), "error")
redirect("/admin/teams", w, req)
}
page.TemplateData = t page.TemplateData = t
page.show("admin-editteam.html", w) page.show("admin-editteam.html", w)
} }
@ -104,7 +95,7 @@ func handleAdminTeams(w http.ResponseWriter, req *http.Request, page *pageData)
type teamsPageData struct { type teamsPageData struct {
Teams []Team Teams []Team
} }
page.TemplateData = teamsPageData{Teams: db.getAllTeams()} page.TemplateData = m.jam
page.SubTitle = "Teams" page.SubTitle = "Teams"
page.show("admin-teams.html", w) page.show("admin-teams.html", w)
} }

View File

@ -29,7 +29,7 @@ func handleAdminDoLogin(w http.ResponseWriter, req *http.Request) {
// If it can't, it returns an error // If it can't, it returns an error
func doLogin(email, password string) error { func doLogin(email, password string) error {
if strings.TrimSpace(email) != "" && strings.TrimSpace(password) != "" { if strings.TrimSpace(email) != "" && strings.TrimSpace(password) != "" {
return db.checkCredentials(email, password) return m.checkCredentials(email, password)
} }
return errors.New("Invalid Credentials") return errors.New("Invalid Credentials")
} }
@ -53,12 +53,12 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData)
switch vars["function"] { switch vars["function"] {
case "save": case "save":
email = req.FormValue("email") email = req.FormValue("email")
if db.isValidUserEmail(email) { if m.isValidUserEmail(email) {
// User already exists // User already exists
page.session.setFlashMessage("A user with email address "+email+" already exists!", "error") page.session.setFlashMessage("A user with email address "+email+" already exists!", "error")
} else { } else {
password := req.FormValue("password") password := req.FormValue("password")
if err := db.updateUserPassword(email, string(password)); err != nil { if err := m.updateUserPassword(email, string(password)); err != nil {
page.session.setFlashMessage(err.Error(), "error") page.session.setFlashMessage(err.Error(), "error")
} else { } else {
page.session.setFlashMessage("User "+email+" created!", "success") page.session.setFlashMessage("User "+email+" created!", "success")
@ -73,10 +73,10 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData)
switch vars["function"] { switch vars["function"] {
case "save": case "save":
var err error var err error
if db.isValidUserEmail(email) { if m.isValidUserEmail(email) {
password := req.FormValue("password") password := req.FormValue("password")
if password != "" { if password != "" {
if err = db.updateUserPassword(email, password); err != nil { if err = m.updateUserPassword(email, password); err != nil {
page.session.setFlashMessage(err.Error(), "error") page.session.setFlashMessage(err.Error(), "error")
} else { } else {
page.session.setFlashMessage("User "+email+" created!", "success") page.session.setFlashMessage("User "+email+" created!", "success")
@ -86,8 +86,8 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData)
} }
case "delete": case "delete":
var err error var err error
if db.isValidUserEmail(email) { if m.isValidUserEmail(email) {
if err = db.deleteUser(email); err != nil { if err = m.deleteUser(email); err != nil {
page.session.setFlashMessage(err.Error(), "error") page.session.setFlashMessage(err.Error(), "error")
} else { } else {
page.session.setFlashMessage("User "+email+" deleted!", "success") 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) redirect("/admin/users", w, req)
default: default:
page.SubTitle = "Edit Admin User" page.SubTitle = "Edit Admin User"
if !db.isValidUserEmail(email) { if !m.isValidUserEmail(email) {
page.session.setFlashMessage("Couldn't find the requested user, please try again.", "error") page.session.setFlashMessage("Couldn't find the requested user, please try again.", "error")
redirect("/admin/users", w, req) redirect("/admin/users", w, req)
} }
@ -107,7 +107,7 @@ func handleAdminUsers(w http.ResponseWriter, req *http.Request, page *pageData)
type usersPageData struct { type usersPageData struct {
Users []string Users []string
} }
page.TemplateData = usersPageData{Users: db.getAllUsers()} page.TemplateData = usersPageData{Users: m.getAllUsers()}
page.SubTitle = "Admin Users" page.SubTitle = "Admin Users"
page.show("admin-users.html", w) page.show("admin-users.html", w)

View File

@ -23,22 +23,22 @@ func getCondorcetResult() []Ranking {
} }
var allPairs []teamPair var allPairs []teamPair
var ret []Ranking var ret []Ranking
for i := 0; i < len(site.Teams); i++ { for i := 0; i < len(m.jam.Teams); i++ {
for j := i + 1; j < len(site.Teams); j++ { for j := i + 1; j < len(m.jam.Teams); j++ {
// For each pairing find a winner // For each pairing find a winner
winner, pct, _ := findWinnerBetweenTeams(&site.Teams[i], &site.Teams[j]) winner, pct, _ := findWinnerBetweenTeams(&m.jam.Teams[i], &m.jam.Teams[j])
newPair := new(teamPair) newPair := new(teamPair)
if winner != nil { if winner != nil {
newPair.winner = winner newPair.winner = winner
if winner.UUID == site.Teams[i].UUID { if winner.UUID == m.jam.Teams[i].UUID {
newPair.loser = &site.Teams[j] newPair.loser = &m.jam.Teams[j]
} else { } else {
newPair.loser = &site.Teams[i] newPair.loser = &m.jam.Teams[i]
} }
newPair.majority = pct newPair.majority = pct
} else { } else {
newPair.winner = &site.Teams[i] newPair.winner = &m.jam.Teams[i]
newPair.loser = &site.Teams[j] newPair.loser = &m.jam.Teams[j]
newPair.majority = 50 newPair.majority = 50
} }
allPairs = append(allPairs, *newPair) allPairs = append(allPairs, *newPair)
@ -46,8 +46,8 @@ func getCondorcetResult() []Ranking {
} }
// initialize map of team wins // initialize map of team wins
teamWins := make(map[string]int) teamWins := make(map[string]int)
for i := range site.Teams { for i := range m.jam.Teams {
teamWins[site.Teams[i].UUID] = 0 teamWins[m.jam.Teams[i].UUID] = 0
} }
// Figure out how many wins each team has // Figure out how many wins each team has
for i := range allPairs { for i := range allPairs {
@ -72,7 +72,10 @@ func getCondorcetResult() []Ranking {
nR := new(Ranking) nR := new(Ranking)
nR.Rank = currRank nR.Rank = currRank
for i := range rankedWins[topWins] { for i := range rankedWins[topWins] {
nR.Teams = append(nR.Teams, *site.getTeamByUUID(rankedWins[topWins][i])) tm, _ := m.jam.GetTeamById(rankedWins[topWins][i])
if tm != nil {
nR.Teams = append(nR.Teams, *tm)
}
} }
ret = append(ret, *nR) ret = append(ret, *nR)
delete(rankedWins, topWins) delete(rankedWins, topWins)
@ -99,7 +102,7 @@ func uuidIsInRankingSlice(uuid string, sl []Ranking) bool {
func findWinnerBetweenTeams(tm1, tm2 *Team) (*Team, float32, error) { func findWinnerBetweenTeams(tm1, tm2 *Team) (*Team, float32, error) {
// tally gets incremented for a tm1 win, decremented for a tm2 win // tally gets incremented for a tm1 win, decremented for a tm2 win
var tm1votes, tm2votes float32 var tm1votes, tm2votes float32
for _, v := range site.Votes { for _, v := range m.jam.Votes {
for _, chc := range v.Choices { for _, chc := range v.Choices {
if chc.Team == tm1.UUID { if chc.Team == tm1.UUID {
tm1votes++ tm1votes++
@ -138,12 +141,12 @@ func handleAdminVotes(w http.ResponseWriter, req *http.Request, page *pageData)
Results []Ranking Results []Ranking
} }
vpd := new(votePageData) vpd := new(votePageData)
for i := range site.Votes { for i := range m.jam.Votes {
v := new(vpdVote) v := new(vpdVote)
v.Timestamp = site.Votes[i].Timestamp.Format(time.RFC3339) v.Timestamp = m.jam.Votes[i].Timestamp.Format(time.RFC3339)
v.ClientId = site.Votes[i].ClientId v.ClientId = m.jam.Votes[i].ClientId
for _, choice := range site.Votes[i].Choices { for _, choice := range m.jam.Votes[i].Choices {
for _, fndTm := range site.Teams { for _, fndTm := range m.jam.Teams {
if fndTm.UUID == choice.Team { if fndTm.UUID == choice.Team {
v.Choices = append(v.Choices, fndTm) v.Choices = append(v.Choices, fndTm)
break break

27
main.go
View File

@ -69,7 +69,9 @@ func main() {
} }
loadConfig() loadConfig()
site.save() if err = site.SaveToDB(); err != nil {
errorExit("Unable to save site config to DB: " + err.Error())
}
initialize() initialize()
r = mux.NewRouter() r = mux.NewRouter()
@ -184,13 +186,11 @@ func initialize() {
fmt.Print("GameJam Name: ") fmt.Print("GameJam Name: ")
gjName, _ := reader.ReadString('\n') gjName, _ := reader.ReadString('\n')
gjName = strings.TrimSpace(gjName) gjName = strings.TrimSpace(gjName)
if db.setJamName(gjName) != nil { m.jam.Name = gjName
fmt.Println("Error saving Current Jam")
}
} }
if m.jam.Name != "" { if m.jam.Name != "" {
fmt.Println("Current Jam Name: " + jmNm) fmt.Println("Current Jam Name: " + m.jam.Name)
} else { } else {
fmt.Println("No Jam Name Specified") fmt.Println("No Jam Name Specified")
} }
@ -220,7 +220,7 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData {
// First check if we're logged in // First check if we're logged in
userEmail, _ := p.session.getStringValue("email") userEmail, _ := p.session.getStringValue("email")
// With a valid account // With a valid account
p.LoggedIn = db.isValidUserEmail(userEmail) p.LoggedIn = m.isValidUserEmail(userEmail)
p.Site = site p.Site = site
p.SubTitle = "GameJam Voting" p.SubTitle = "GameJam Voting"
@ -257,20 +257,15 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData {
} }
p.HideAdminMenu = true p.HideAdminMenu = true
if p.CurrentJam = db.getJamName(); p.CurrentJam != "" {
p.FlashMessage = "Error Loading Current GameJam: " + err.Error()
p.FlashClass = "error"
}
p.ClientId = p.session.getClientId() p.ClientId = p.session.getClientId()
cl := db.getClient(p.ClientId) cl := m.GetClient(p.ClientId)
p.ClientIsAuth = cl.Auth p.ClientIsAuth = cl.Auth
p.ClientIsServer = clientIsServer(req) p.ClientIsServer = clientIsServer(req)
// Public Mode // Public Mode
p.PublicMode = db.getPublicSiteMode() p.PublicMode = m.site.GetPublicMode()
// Authentication Mode // Authentication Mode
p.AuthMode = db.site.getAuthMode() p.AuthMode = m.site.GetAuthMode()
return p return p
} }
@ -317,8 +312,8 @@ func resetToDefaults() {
conf, _ := reader.ReadString('\n') conf, _ := reader.ReadString('\n')
conf = strings.ToUpper(strings.TrimSpace(conf)) conf = strings.ToUpper(strings.TrimSpace(conf))
if strings.HasPrefix(conf, "Y") { if strings.HasPrefix(conf, "Y") {
if def.save() != nil { if err := def.SaveToDB(); err != nil {
errorExit("Error resetting to defaults") errorExit("Error resetting to defaults: " + err.Error())
} }
fmt.Println("Reset to defaults") fmt.Println("Reset to defaults")
} }

View File

@ -15,6 +15,8 @@ type model struct {
site *siteData // Configuration data for the site site *siteData // Configuration data for the site
jam *Gamejam // The currently active gamejam jam *Gamejam // The currently active gamejam
clients []Client // Web clients that have connected to the server clients []Client // Web clients that have connected to the server
clientsUpdated bool
} }
// Update Flags: Which parts of the model need to be updated // Update Flags: Which parts of the model need to be updated
@ -39,20 +41,27 @@ func NewModel() (*model, error) {
} }
// Load the site data // Load the site data
m.site = m.LoadSiteData() m.site = NewSiteData(m)
if err = m.site.LoadFromDB(); err != nil {
// Error loading from the DB, set to defaults
def := NewSiteData(m)
m.site = def
}
// Load the jam data // Load the jam data
m.jam = m.LoadCurrentJam() if m.jam, err = m.LoadCurrentJam(); err != nil {
return nil, errors.New("Unable to load current jam: " + err.Error())
}
// Load web clients // Load web clients
m.clients = m.LoadAllClients() m.clients = m.LoadAllClients()
return &m, nil return m, nil
} }
func (m *model) openDB() error { func (m *model) openDB() error {
m.dbOpened += 1 m.dbOpened += 1
if db.dbOpened == 1 { if m.dbOpened == 1 {
var err error var err error
m.bolt, err = boltease.Create(m.dbFileName, 0600, nil) m.bolt, err = boltease.Create(m.dbFileName, 0600, nil)
if err != nil { if err != nil {
@ -105,20 +114,22 @@ func (m *model) saveChanges() error {
} }
defer m.closeDB() defer m.closeDB()
if m.site.needsSave() { if m.site.NeedsSave() {
if err = m.site.saveToDB(); err != nil { if err = m.site.SaveToDB(); err != nil {
return err return err
} }
} }
if m.jam.needsSave() { if m.jam.IsChanged {
if err = m.jam.saveToDB(); err != nil { if err = m.jam.SaveToDB(); err != nil {
return err return err
} }
m.jam.IsChanged = false
} }
if m.clientsUpdated { if m.clientsUpdated {
if err = m.SaveAllClients(); err != nil { if err = m.SaveAllClients(); err != nil {
return err return err
} }
m.clientsUpdated = false
} }
return nil return nil
} }

View File

@ -1,5 +1,11 @@
package main package main
import (
"errors"
"github.com/pborman/uuid"
)
/** /**
* Client * Client
* A client is a system that is connecting to the web server * A client is a system that is connecting to the web server
@ -14,12 +20,32 @@ type Client struct {
} }
func NewClient(id string) *Client { func NewClient(id string) *Client {
if id == "" {
id = uuid.New()
}
return &Client{ return &Client{
UUID: id, UUID: id,
mPath: []string{"clients", id}, mPath: []string{"clients", id},
} }
} }
func (m *model) AddClient(cl *Client) error {
for i := range m.clients {
if m.clients[i].UUID == cl.UUID {
return errors.New("A client with that ID already exists")
}
if m.clients[i].IP == cl.IP {
return errors.New("A client with that IP already exists")
}
if m.clients[i].Name == cl.Name {
return errors.New("A client with that Name already exists")
}
}
m.clients = append(m.clients, *cl)
m.clientsUpdated = true
return nil
}
/** /**
* DB Functions * DB Functions
* These are generally just called when the app starts up, or when the periodic 'save' runs * These are generally just called when the app starts up, or when the periodic 'save' runs
@ -71,7 +97,7 @@ func (m *model) SaveAllClients() error {
defer m.closeDB() defer m.closeDB()
for _, v := range m.clients { for _, v := range m.clients {
if err = m.SaveClient(v); err != nil { if err = m.SaveClient(&v); err != nil {
return err return err
} }
} }
@ -86,13 +112,13 @@ func (m *model) SaveClient(cl *Client) error {
} }
defer m.closeDB() defer m.closeDB()
if err = db.bolt.SetBool(cl.mPath, "auth", c.Auth); err != nil { if err = m.bolt.SetBool(cl.mPath, "auth", cl.Auth); err != nil {
return err return err
} }
if err = db.bolt.SetValue(cl.mPath, "name", c.Name); err != nil { if err = m.bolt.SetValue(cl.mPath, "name", cl.Name); err != nil {
return err return err
} }
return db.bolt.SetValue(cl.mPath, "ip", c.IP) return m.bolt.SetValue(cl.mPath, "ip", cl.IP)
} }
/** /**
@ -132,7 +158,7 @@ func (m *model) UpdateClient(cl *Client) {
} }
} }
if !found { if !found {
m.clients = append(m.clients, cl) m.clients = append(m.clients, *cl)
} }
m.clientsUpdated = true m.clientsUpdated = true
} }

View File

@ -1,6 +1,10 @@
package main package main
import "time" import (
"errors"
"strings"
"time"
)
/** /**
* Gamejam * Gamejam
@ -13,9 +17,10 @@ type Gamejam struct {
Teams []Team Teams []Team
Votes []Vote Votes []Vote
m *model // The model that holds this gamejam's data m *model // The model that holds this gamejam's data
mPath []string // The path in the db to this gamejam mPath []string // The path in the db to this gamejam
changed bool // Flag to tell if we need to update the db
IsChanged bool // Flag to tell if we need to update the db
} }
func NewGamejam(m *model) *Gamejam { func NewGamejam(m *model) *Gamejam {
@ -30,13 +35,12 @@ func NewGamejam(m *model) *Gamejam {
* These are generally just called when the app starts up, or when the periodic 'save' runs * These are generally just called when the app starts up, or when the periodic 'save' runs
*/ */
func (m *model) LoadCurrentJam() *Gamejam { func (m *model) LoadCurrentJam() (*Gamejam, error) {
if err := m.openDB(); err != nil { if err := m.openDB(); err != nil {
return err return nil, err
} }
defer m.closeDB() defer m.closeDB()
var err error
gj := NewGamejam(m) gj := NewGamejam(m)
gj.Name, _ = m.bolt.GetValue(gj.mPath, "name") gj.Name, _ = m.bolt.GetValue(gj.mPath, "name")
@ -46,52 +50,37 @@ func (m *model) LoadCurrentJam() *Gamejam {
// Load all votes // Load all votes
gj.Votes = gj.LoadAllVotes() gj.Votes = gj.LoadAllVotes()
return gj return gj, nil
} }
// Save everything to the DB whether it's flagged as changed or not // Save everything to the DB whether it's flagged as changed or not
func (gj *Gamejam) saveToDB() error { func (gj *Gamejam) SaveToDB() error {
if err := gj.m.openDB(); err != nil { if err := gj.m.openDB(); err != nil {
return err return err
} }
defer gj.m.closeDB() defer gj.m.closeDB()
} var errs []error
// Save all Teams
/** for _, tm := range gj.Teams {
* In Memory functions if err := gj.SaveTeam(&tm); err != nil {
* This is generally how the app accesses client data errs = append(errs, err)
*/
func (gj *Gamejam) getTeamByUUID(uuid string) *Team {
for i := range gj.Teams {
if gj.Teams[i].UUID == uuid {
return &gj.Teams[i]
} }
} }
// Save all Votes
for _, vt := range gj.Votes {
if err := gj.SaveVote(&vt); err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
var errTxt string
for i := range errs {
errTxt = errTxt + errs[i].Error() + "\n"
}
errTxt = strings.TrimSpace(errTxt)
return errors.New("Error(s) saving to DB: " + errTxt)
}
return nil return nil
} }
// Check if pth is already in updates, if not, add it
func (gj *Gamejam) NeedsUpdate(pth []string) {
var found bool
for _, v := range gj.updates {
if !(len(v) == len(pth)) {
continue
}
// The lengths are the same, do all elements match?
var nxt bool
for i := range pth {
if v[i] != pth[i] {
nxt = true
}
}
if !nxt {
// This pth is already in the 'updates' list
found = true
break
}
}
if !found {
gj.updates = append(gj.updates, pth)
}
}

View File

@ -1,6 +1,10 @@
package main package main
import "errors" import (
"errors"
"github.com/pborman/uuid"
)
/** /**
* Game * Game
@ -17,19 +21,37 @@ type Game struct {
} }
// Create a new game object // Create a new game object
func NewGame(tmId string) *Game { func NewGame(tmId string) (*Game, error) {
if tmId == "" {
return nil, errors.New("Team ID is required")
}
return &Game{ return &Game{
TeamId: tmId, TeamId: tmId,
mPath: []string{"jam", "teams", tmId, "game"}, mPath: []string{"jam", "teams", tmId, "game"},
} }, nil
} }
func (gm *Game) GetScreenshot(ssId string) *Screenshot { func (gm *Game) GetScreenshot(ssId string) (*Screenshot, error) {
for _, ss := range gm.Screenshots { for _, ss := range gm.Screenshots {
if ss.UUID == ssId { if ss.UUID == ssId {
return ss return &ss, nil
} }
} }
return nil, errors.New("Invalid Id")
}
func (gm *Game) RemoveScreenshot(ssId string) error {
idx := -1
for i, ss := range gm.Screenshots {
if ss.UUID == ssId {
idx = i
return nil
}
}
if idx < 0 {
return errors.New("Invalid Id")
}
gm.Screenshots = append(gm.Screenshots[:idx], gm.Screenshots[idx+1:]...)
return nil return nil
} }
@ -44,11 +66,18 @@ type Screenshot struct {
} }
// Create a Screenshot Object // Create a Screenshot Object
func NewScreenshot(tmId, ssId string) *Screenshot { func NewScreenshot(tmId, ssId string) (*Screenshot, error) {
if tmId == "" {
return nil, errors.New("Team ID is required")
}
if ssId == "" {
// Generate a new UUID
ssId = uuid.New()
}
return &Screenshot{ return &Screenshot{
UUID: ssId, UUID: ssId,
mPath: []string{"jam", "teams", tmId, "game", "screenshots", ssId}, mPath: []string{"jam", "teams", tmId, "game", "screenshots", ssId},
} }, nil
} }
/** /**
@ -57,14 +86,17 @@ func NewScreenshot(tmId, ssId string) *Screenshot {
*/ */
// Load a team's game from the DB and return it // Load a team's game from the DB and return it
func (gj *Gamejam) LoadTeamGame(tmId string) *Game { func (gj *Gamejam) LoadTeamGame(tmId string) (*Game, error) {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return nil return nil, err
} }
defer gj.m.closeDB() defer gj.m.closeDB()
gm := NewGame(tmId) gm, err := NewGame(tmId)
if err != nil {
return nil, err
}
if gm.Name, err = gj.m.bolt.GetValue(gm.mPath, "name"); err != nil { if gm.Name, err = gj.m.bolt.GetValue(gm.mPath, "name"); err != nil {
gm.Name = "" gm.Name = ""
} }
@ -74,10 +106,11 @@ func (gj *Gamejam) LoadTeamGame(tmId string) *Game {
if gm.Link, err = gj.m.bolt.GetValue(gm.mPath, "link"); err != nil { if gm.Link, err = gj.m.bolt.GetValue(gm.mPath, "link"); err != nil {
gm.Link = "" gm.Link = ""
} }
// Now get the game screenshots // Now get the game screenshots
gm.Screenshots = gj.LoadTeamGameScreenshots(tmId) gm.Screenshots = gj.LoadTeamGameScreenshots(tmId)
return &gm return gm, nil
} }
// Load a games screenshots from the DB // Load a games screenshots from the DB
@ -89,44 +122,50 @@ func (gj *Gamejam) LoadTeamGameScreenshots(tmId string) []Screenshot {
defer gj.m.closeDB() defer gj.m.closeDB()
var ret []Screenshot var ret []Screenshot
gm := NewGame(tmId) gm, err := NewGame(tmId)
if err != nil {
return ret
}
ssBktPath := append(gm.mPath, "screenshots") ssBktPath := append(gm.mPath, "screenshots")
var ssIds []string var ssIds []string
ssIds, _ = gj.m.bolt.GetBucketList(ssBktPath) ssIds, _ = gj.m.bolt.GetBucketList(ssBktPath)
for _, v := range ssIds { for _, v := range ssIds {
ssLd := gj.LoadTeamGameScreenshot(tmId, v) ssLd, _ := gj.LoadTeamGameScreenshot(tmId, v)
if ssLd != nil { if ssLd != nil {
ret = append(ret, ssLd) ret = append(ret, *ssLd)
} }
} }
return ret return ret
} }
// Load a screenshot from the DB // Load a screenshot from the DB
func (gj *Gamejam) LoadTeamGameScreenshot(tmId, ssId string) *Screenshot { func (gj *Gamejam) LoadTeamGameScreenshot(tmId, ssId string) (*Screenshot, error) {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return nil return nil, err
} }
defer gj.m.closeDB() defer gj.m.closeDB()
ret := NewScreenshot(tmId, ssId) ret, err := NewScreenshot(tmId, ssId)
if err != nil {
return nil, err
}
if ret.Description, err = gj.m.bolt.GetValue(ret.mPath, "description"); err != nil { if ret.Description, err = gj.m.bolt.GetValue(ret.mPath, "description"); err != nil {
return nil return nil, err
} }
if ret.Image, err = gj.m.bolt.GetValue(ret.mPath, "image"); err != nil { if ret.Image, err = gj.m.bolt.GetValue(ret.mPath, "image"); err != nil {
return nil return nil, err
} }
if ret.Thumbnail, err = gj.m.bolt.GetValue(ret.mPath, "thumbnail"); err != nil { if ret.Thumbnail, err = gj.m.bolt.GetValue(ret.mPath, "thumbnail"); err != nil {
return nil return nil, err
} }
if ret.Thumbnail == "" { if ret.Thumbnail == "" {
ret.Thumbnail = ret.Image ret.Thumbnail = ret.Image
} }
if ret.Filetype, err = gj.m.bolt.GetValue(ret.mPath, "filetype"); err != nil { if ret.Filetype, err = gj.m.bolt.GetValue(ret.mPath, "filetype"); err != nil {
return nil return nil, err
} }
return ret return ret, err
} }
// Save a game to the DB // Save a game to the DB
@ -141,6 +180,10 @@ func (gj *Gamejam) SaveGame(gm *Game) error {
return err return err
} }
var tm *Team
if tm, err = gj.GetTeamById(gm.TeamId); err != nil {
return err
}
if gm.Name == "" { if gm.Name == "" {
gm.Name = tm.Name + "'s Game" gm.Name = tm.Name + "'s Game"
} }
@ -169,26 +212,35 @@ func (gj *Gamejam) SaveScreenshots(gm *Game) error {
defer gj.m.closeDB() defer gj.m.closeDB()
for _, ss := range gm.Screenshots { for _, ss := range gm.Screenshots {
if err = gj.SaveScreenshot(gm.TeamId, ss); err != nil { if err = gj.SaveScreenshot(&ss); err != nil {
return err return err
} }
} }
// Now remove unused screenshots // Now remove unused screenshots
ssPath := append(gm.mPath, "screenshots") ssPath := append(gm.mPath, "screenshots")
var ssIds []string
if ssIds, err = gj.m.bolt.GetBucketList(ssPath); err != nil { if ssIds, err = gj.m.bolt.GetBucketList(ssPath); err != nil {
return err return err
} }
for i := range ssIds { for i := range ssIds {
if gm.GetScreenshot(ssIds[i]) == nil { ss, _ := gm.GetScreenshot(ssIds[i])
if err = gj.DeleteScreenshot(NewScreenshot(tm.TeamId, ssIds[i])); err != nil { if ss != nil {
return err // A valid screenshot, next
} continue
}
if ss, err = NewScreenshot(gm.TeamId, ssIds[i]); err != nil {
// Error building screenshot to delete...
continue
}
if err = gj.DeleteScreenshot(ss); err != nil {
return err
} }
} }
return nil
} }
// Save a screenshot // Save a screenshot
func (gj *Gamejam) SaveScreenshot(tmId string, ss *Screenshot) error { func (gj *Gamejam) SaveScreenshot(ss *Screenshot) error {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return err return err
@ -229,12 +281,11 @@ func (gj *Gamejam) DeleteScreenshot(ss *Screenshot) error {
// Set the given team's game to gm // Set the given team's game to gm
func (gj *Gamejam) UpdateGame(tmId string, gm *Game) error { func (gj *Gamejam) UpdateGame(tmId string, gm *Game) error {
var found bool tm, err := gj.GetTeamById(tmId)
tm := gj.GetTeamById(tmId) if err != nil {
if tm == nil { return errors.New("Error getting team: " + err.Error())
return errors.New("Invalid team ID: " + gm.TeamId)
} }
tm.Game = gm tm.Game = gm
gj.NeedsUpdate([]string{"team", tmId, "game"}) gj.IsChanged = true
return nil return nil
} }

View File

@ -1,16 +1,19 @@
package main package main
import "strconv" import (
"errors"
"strconv"
)
/** /**
* SiteData * SiteData
* Contains configuration for the website * Contains configuration for the website
*/ */
type siteData struct { type siteData struct {
title string Title string
port int Port int
sessionName string SessionName string
serverDir string ServerDir string
authMode int authMode int
publicMode int publicMode int
@ -62,10 +65,10 @@ func (s *siteData) LoadFromDB() error {
if port, err := s.m.bolt.GetInt(s.mPath, "port"); err == nil { if port, err := s.m.bolt.GetInt(s.mPath, "port"); err == nil {
s.Port = port s.Port = port
} }
if sessionName, err = s.m.bolt.GetValue(s.mPath, "session-name"); err == nil { if sessionName, err := s.m.bolt.GetValue(s.mPath, "session-name"); err == nil {
s.SessionName = sessionName s.SessionName = sessionName
} }
if serverDir, err = s.m.bolt.GetValue(s.mPath, "server-dir"); err == nil { if serverDir, err := s.m.bolt.GetValue(s.mPath, "server-dir"); err == nil {
s.ServerDir = serverDir s.ServerDir = serverDir
} }
s.changed = false s.changed = false
@ -79,7 +82,8 @@ func (s *siteData) NeedsSave() bool {
// Save the site data into the DB // Save the site data into the DB
func (s *siteData) SaveToDB() error { func (s *siteData) SaveToDB() error {
if err := s.m.openDB(); err != nil { var err error
if err = s.m.openDB(); err != nil {
return err return err
} }
defer s.m.closeDB() defer s.m.closeDB()
@ -108,12 +112,13 @@ func (s *siteData) GetAuthMode() int {
// Set the auth mode // Set the auth mode
func (s *siteData) SetAuthMode(mode int) error { func (s *siteData) SetAuthMode(mode int) error {
if mode < AuthModeAuthentication || mode >= AuthModeError { if mode < AuthModeAuthentication || mode >= AuthModeError {
return errors.Error("Invalid Authentication Mode: " + strconv.Itoa(mode)) return errors.New("Invalid Authentication Mode: " + strconv.Itoa(mode))
} }
if mode != s.authMode { if mode != s.authMode {
s.authMode = mode s.authMode = mode
s.changed = true s.changed = true
} }
return nil
} }
// Return the public site mode // Return the public site mode
@ -124,10 +129,11 @@ func (s *siteData) GetPublicMode() int {
// Set the public site mode // Set the public site mode
func (s *siteData) SetPublicMode(mode int) error { func (s *siteData) SetPublicMode(mode int) error {
if mode < SiteModeWaiting || mode >= SiteModeError { if mode < SiteModeWaiting || mode >= SiteModeError {
return errors.Error("Invalid Public Mode: " + strconv.Itoa(mode)) return errors.New("Invalid Public Mode: " + strconv.Itoa(mode))
} }
if mode != s.publicMode { if mode != s.publicMode {
s.publicMode = mode s.publicMode = mode
s.changed = true s.changed = true
} }
return nil
} }

View File

@ -1,6 +1,10 @@
package main package main
import "errors" import (
"errors"
"github.com/pborman/uuid"
)
/** /**
* Team * Team
@ -16,12 +20,24 @@ type Team struct {
// Create a team // Create a team
func NewTeam(id string) *Team { func NewTeam(id string) *Team {
if id == "" {
id = uuid.New()
}
return &Team{ return &Team{
UUID: id, UUID: id,
mPath: []string{"jam", "teams", id}, mPath: []string{"jam", "teams", id},
} }
} }
func (gj *Gamejam) GetTeamById(id string) (*Team, error) {
for i := range gj.Teams {
if gj.Teams[i].UUID == id {
return &gj.Teams[i], nil
}
}
return nil, errors.New("Invalid Team Id given")
}
type TeamMember struct { type TeamMember struct {
UUID string UUID string
Name string Name string
@ -33,11 +49,53 @@ type TeamMember struct {
} }
// Create a new team member // Create a new team member
func NewTeamMember(tmId, uId string) *TeamMember { func NewTeamMember(tmId, uId string) (*TeamMember, error) {
if tmId == "" {
return nil, errors.New("Team ID is required")
}
if uId == "" {
uId = uuid.New()
}
return &TeamMember{ return &TeamMember{
UUID: uId, UUID: uId,
mPath: []string{"jam", "teams", tmId, "members", uId}, mPath: []string{"jam", "teams", tmId, "members", uId},
}, nil
}
// AddTeamMember adds a new team member
func (tm *Team) AddTeamMember(mbr *TeamMember) error {
lkup, _ := tm.GetTeamMemberById(mbr.UUID)
if lkup != nil {
return errors.New("A Team Member with that Id already exists")
} }
tm.Members = append(tm.Members, *mbr)
return nil
}
// GetTeamMemberById returns a member with the given uuid
// or an error if it couldn't find it
func (tm *Team) GetTeamMemberById(uuid string) (*TeamMember, error) {
for i := range tm.Members {
if tm.Members[i].UUID == uuid {
return &tm.Members[i], nil
}
}
return nil, errors.New("Invalid Team Member Id given")
}
func (tm *Team) RemoveTeamMemberById(id string) error {
idx := -1
for i := range tm.Members {
if tm.Members[i].UUID == id {
idx = i
break
}
}
if idx < 0 {
return errors.New("Invalid Team Member ID given")
}
tm.Members = append(tm.Members[:idx], tm.Members[idx+1:]...)
return nil
} }
/** /**
@ -50,43 +108,47 @@ func (gj *Gamejam) LoadAllTeams() []Team {
var err error var err error
var ret []Team var ret []Team
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return err return ret
} }
defer gj.m.closeDB() defer gj.m.closeDB()
if tmUUIDs, err = m.bolt.GetBucketList(mbrsPath); err != nil { var tmUUIDs []string
tmsPath := append(gj.mPath, "teams")
if tmUUIDs, err = m.bolt.GetBucketList(tmsPath); err != nil {
return ret return ret
} }
for _, v := range tmUUIDs { for _, v := range tmUUIDs {
tm := gj.LoadTeam(v) tm, _ := gj.LoadTeam(v)
if tm != nil { if tm != nil {
ret = append(ret, tm) ret = append(ret, *tm)
} }
} }
return ret return ret
} }
// Load a team out of the database // Load a team out of the database
func (gj *Gamejam) LoadTeam(uuid string) *Team { func (gj *Gamejam) LoadTeam(uuid string) (*Team, error) {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return err return nil, err
} }
defer gj.m.closeDB() defer gj.m.closeDB()
// Team Data // Team Data
tm := NewTeam(uuid) tm := NewTeam(uuid)
if tm.Name, err = gj.m.bolt.GetValue(tm.mPath, "name"); err != nil { if tm.Name, err = gj.m.bolt.GetValue(tm.mPath, "name"); err != nil {
return nil return nil, errors.New("Error loading team: " + err.Error())
} }
// Team Members // Team Members
tm.Members = gj.LoadTeamMembers(uuid) tm.Members = gj.LoadTeamMembers(uuid)
// Team Game // Team Game
tm.Game = gj.LoadTeamGame(uuid) if tm.Game, err = gj.LoadTeamGame(uuid); err != nil {
return nil, errors.New("Error loading team game: " + err.Error())
}
return tm return tm, nil
} }
// Load the members of a team from the DB and return them // Load the members of a team from the DB and return them
@ -104,9 +166,9 @@ func (gj *Gamejam) LoadTeamMembers(tmId string) []TeamMember {
mbrsPath := append(tm.mPath, "members") mbrsPath := append(tm.mPath, "members")
if memberUuids, err = gj.m.bolt.GetBucketList(mbrsPath); err == nil { if memberUuids, err = gj.m.bolt.GetBucketList(mbrsPath); err == nil {
for _, v := range memberUuids { for _, v := range memberUuids {
mbr := gj.LoadTeamMember(tmId, v) mbr, _ := gj.LoadTeamMember(tmId, v)
if mbr != nil { if mbr != nil {
ret = append(ret, mbr) ret = append(ret, *mbr)
} }
} }
} }
@ -114,17 +176,20 @@ func (gj *Gamejam) LoadTeamMembers(tmId string) []TeamMember {
} }
// Load a team member from the DB and return it // Load a team member from the DB and return it
func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) *TeamMember { func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) (*TeamMember, error) {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return nil return nil, err
} }
defer gj.m.closeDB() defer gj.m.closeDB()
mbr := NewTeamMember(tmId, mbrId) mbr, err := NewTeamMember(tmId, mbrId)
if err != nil {
return nil, errors.New("Error loading team member: " + err.Error())
}
// Name is the only required field // Name is the only required field
if mbr.Name, err = gj.m.bolt.GetValue(mbr.mPath, "name"); err != nil { if mbr.Name, err = gj.m.bolt.GetValue(mbr.mPath, "name"); err != nil {
return nil return nil, errors.New("Error loading team member: " + err.Error())
} }
if mbr.SlackId, err = gj.m.bolt.GetValue(mbr.mPath, "slackid"); err != nil { if mbr.SlackId, err = gj.m.bolt.GetValue(mbr.mPath, "slackid"); err != nil {
mbr.SlackId = "" mbr.SlackId = ""
@ -135,7 +200,7 @@ func (gj *Gamejam) LoadTeamMember(tmId, mbrId string) *TeamMember {
if mbr.Email, err = gj.m.bolt.GetValue(mbr.mPath, "email"); err != nil { if mbr.Email, err = gj.m.bolt.GetValue(mbr.mPath, "email"); err != nil {
mbr.Email = "" mbr.Email = ""
} }
return mbr return mbr, nil
} }
func (gj *Gamejam) SaveTeam(tm *Team) error { func (gj *Gamejam) SaveTeam(tm *Team) error {
@ -146,7 +211,7 @@ func (gj *Gamejam) SaveTeam(tm *Team) error {
defer gj.m.closeDB() defer gj.m.closeDB()
// Save team data // Save team data
if err = gj.m.bolt.SetValue(tm.mPath, "name"); err != nil { if err = gj.m.bolt.SetValue(tm.mPath, "name", tm.Name); err != nil {
return err return err
} }
@ -167,10 +232,12 @@ func (gj *Gamejam) SaveTeam(tm *Team) error {
} }
// Save team game // Save team game
return gj.SaveGame(gm) return gj.SaveGame(tm.Game)
} }
// Delete the team tm // Delete the team tm
// TODO: Deletes should be done all at once when syncing memory to the DB
/*
func (gj *Gamejam) DeleteTeam(tm *Team) error { func (gj *Gamejam) DeleteTeam(tm *Team) error {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
@ -183,8 +250,11 @@ func (gj *Gamejam) DeleteTeam(tm *Team) error {
} }
return gj.m.bolt.DeleteBucket(tm.mPath[:len(tm.mPath)-1], tm.UUID) return gj.m.bolt.DeleteBucket(tm.mPath[:len(tm.mPath)-1], tm.UUID)
} }
*/
// Delete the TeamMember mbr from Team tm // Delete the TeamMember mbr from Team tm
// TODO: Deletes should be done all at once when syncing memory to the DB
/*
func (gj *Gamejam) DeleteTeamMember(tm *Team, mbr *TeamMember) error { func (gj *Gamejam) DeleteTeamMember(tm *Team, mbr *TeamMember) error {
var err error var err error
if err = gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
@ -197,28 +267,47 @@ func (gj *Gamejam) DeleteTeamMember(tm *Team, mbr *TeamMember) error {
} }
return gj.m.bolt.DeleteBucket(mbr.mPath[:len(mbr.mPath)-1], mbr.UUID) return gj.m.bolt.DeleteBucket(mbr.mPath[:len(mbr.mPath)-1], mbr.UUID)
} }
*/
/** /**
* In Memory functions * In Memory functions
* This is generally how the app accesses data * This is generally how the app accesses data
*/ */
// Find a team by it's ID // Add a team
func (gj *Gamejam) GetTeamById(id string) *Team { func (gj *Gamejam) AddTeam(tm *Team) error {
for i := range gj.Teams { if _, err := gj.GetTeamById(tm.UUID); err != nil {
if gj.Teams[i].UUID == id { return errors.New("A team with that ID already exists")
return gj.Teams[i]
}
} }
if _, err := gj.GetTeamByName(tm.Name); err != nil {
return errors.New("A team with that Name already exists")
}
gj.Teams = append(gj.Teams, *tm)
return nil return nil
} }
// Find a team by name // Find a team by name
func (gj *Gamejam) GetTeamByName(nm string) *Team { func (gj *Gamejam) GetTeamByName(nm string) (*Team, error) {
for i := range gj.Teams { for i := range gj.Teams {
if gj.Teams[i].Name == nm { if gj.Teams[i].Name == nm {
return gj.Teams[i] return &gj.Teams[i], nil
} }
} }
return nil, errors.New("Invalid team name given")
}
// Remove a team by id
func (gj *Gamejam) RemoveTeamById(id string) error {
idx := -1
for i := range gj.Teams {
if gj.Teams[i].UUID == id {
idx = i
break
}
}
if idx == -1 {
return errors.New("Invalid Team ID given")
}
gj.Teams = append(gj.Teams[:idx], gj.Teams[idx+1:]...)
return nil return nil
} }

View File

@ -3,6 +3,9 @@ package main
import "golang.org/x/crypto/bcrypt" import "golang.org/x/crypto/bcrypt"
// These are all model functions that have to do with users // These are all model functions that have to do with users
// Unlike gamejam functions, we manipulate the DB directly
// We want to make sure that we always use the most up-to-date user
// information.
// Returns true if there are any users in the database // Returns true if there are any users in the database
func (m *model) hasUser() bool { func (m *model) hasUser() bool {

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"errors"
"strconv" "strconv"
"time" "time"
) )
@ -16,30 +17,87 @@ type Vote struct {
Timestamp time.Time Timestamp time.Time
ClientId string // UUID of client ClientId string // UUID of client
Choices []GameChoice Choices []GameChoice
mPath []string // The path in the DB to this team
} }
func NewVote(clId string, tm time.Time) (*Vote, error) {
if clId == "" {
return nil, errors.New("Client ID is required")
}
if tm.IsZero() {
tm = time.Now()
}
vt := new(Vote)
vt.mPath = []string{"jam", "votes", clId, tm.Format(time.RFC3339)}
return vt, nil
}
func (vt *Vote) SetChoices(ch []string) error {
// Clear any previous choices from this vote
vt.Choices = []GameChoice{}
for i, v := range ch {
vt.Choices = append(vt.Choices, GameChoice{Rank: i, Team: v})
}
return nil
}
func (gj *Gamejam) GetVoteWithTimeString(clId, ts string) (*Vote, error) {
timestamp, err := time.Parse(time.RFC3339, ts)
if err != nil {
return nil, err
}
return gj.GetVote(clId, timestamp)
}
func (gj *Gamejam) GetVote(clId string, ts time.Time) (*Vote, error) {
for _, v := range gj.Votes {
if v.ClientId == clId && v.Timestamp == ts {
return &v, nil
}
}
return nil, errors.New("Couldn't find requested vote")
}
func (gj *Gamejam) AddVote(vt *Vote) error {
// Make sure that this isn't a duplicate
if _, err := gj.GetVote(vt.ClientId, vt.Timestamp); err == nil {
return errors.New("Duplicate Vote")
}
gj.Votes = append(gj.Votes, *vt)
return nil
}
/**
* DB Functions
* These are generally just called when the app starts up or when the periodic 'save' runs
*/
// LoadAllVotes loads all votes for the jam out of the database // LoadAllVotes loads all votes for the jam out of the database
func (gj *Gamejam) LoadAllVotes() []Vote { func (gj *Gamejam) LoadAllVotes() []Vote {
var err error
var ret []Vote var ret []Vote
if err := gj.m.openDB(); err != nil { if err = gj.m.openDB(); err != nil {
return err return ret
} }
defer gj.m.closeDB() defer gj.m.closeDB()
votesPath := []string{"jam", "votes"} votesPath := []string{"jam", "votes"}
var cliUUIDs []string
if cliUUIDs, err = m.bolt.GetBucketList(votesPath); err != nil { if cliUUIDs, err = m.bolt.GetBucketList(votesPath); err != nil {
return ret return ret
} }
for _, cId := range cliUUIDs { for _, cId := range cliUUIDs {
vtsPth := append(votesPath, cId) vtsPth := append(votesPath, cId)
if times, err := m.bolt.GetBucketList(vtsPth); err != nil { var times []string
if times, err = m.bolt.GetBucketList(vtsPth); err != nil {
// Error reading this bucket, move on to the next // Error reading this bucket, move on to the next
continue continue
} }
for _, t := range times { for _, t := range times {
vt := gj.LoadVote(cId, t) if vt, err := gj.LoadVote(cId, t); err == nil {
if vt != nil { ret = append(ret, *vt)
ret = append(ret, vt)
} }
} }
} }
@ -47,27 +105,41 @@ func (gj *Gamejam) LoadAllVotes() []Vote {
} }
// Load a vote from the DB and return it // Load a vote from the DB and return it
func (gj *Gamejam) LoadVote(clientId, tm string) *Vote { func (gj *Gamejam) LoadVote(clientId, t string) (*Vote, error) {
var tm time.Time var tm time.Time
var err error
if tm, err = time.Parse(time.RFC3339, t); err != nil { if tm, err = time.Parse(time.RFC3339, t); err != nil {
return nil return nil, errors.New("Error loading vote: " + err.Error())
}
vt, err := NewVote(clientId, tm)
if err != nil {
return nil, errors.New("Error creating vote: " + err.Error())
} }
vt := new(Vote)
vt.Timestamp = tm
vt.ClientId = cId
vtPth := append(vtsPth, t)
var choices []string var choices []string
if choices, err = m.bolt.GetKeyList(vtPth); err != nil { if choices, err = m.bolt.GetKeyList(vt.mPath); err != nil {
return nil return nil, errors.New("Error creating vote: " + err.Error())
} }
for _, v := range choices { for _, v := range choices {
ch := new(GameChoices) ch := new(GameChoice)
var rank int var rank int
if rank, err = strconv.Atoi(v); err == nil { if rank, err = strconv.Atoi(v); err == nil {
ch.Rank = rank ch.Rank = rank
ch.Team, _ = m.bolt.GetValue(vtPth, v) ch.Team, _ = m.bolt.GetValue(vt.mPath, v)
vt.Choices = append(vt.Choices, *ch) vt.Choices = append(vt.Choices, *ch)
} }
} }
return &vt return vt, nil
}
func (gj *Gamejam) SaveVote(vt *Vote) error {
var err error
if err = gj.m.openDB(); err != nil {
return err
}
defer gj.m.closeDB()
for _, v := range vt.Choices {
m.bolt.SetValue(vt.mPath, strconv.Itoa(v.Rank), v.Team)
}
return nil
} }

View File

@ -43,7 +43,7 @@ func (p *pageSession) getClientId() string {
fmt.Println(" Client IP:" + clientIp) fmt.Println(" Client IP:" + clientIp)
if clientIp != "127.0.0.1" { if clientIp != "127.0.0.1" {
fmt.Println(" Pulling data by IP") fmt.Println(" Pulling data by IP")
cli = db.getClientByIp(clientIp) cli = m.GetClientByIp(clientIp)
} }
if cli != nil { if cli != nil {
clientId = cli.UUID clientId = cli.UUID

View File

@ -2,6 +2,7 @@ package main
import ( import (
"encoding/base64" "encoding/base64"
"fmt"
"math/rand" "math/rand"
"net/http" "net/http"
"strings" "strings"
@ -17,7 +18,7 @@ func initPublicPage(w http.ResponseWriter, req *http.Request) *pageData {
func handleMain(w http.ResponseWriter, req *http.Request) { func handleMain(w http.ResponseWriter, req *http.Request) {
page := initPublicPage(w, req) page := initPublicPage(w, req)
if db.getPublicSiteMode() == SiteModeWaiting { if m.site.GetPublicMode() == SiteModeWaiting {
page.SubTitle = "" page.SubTitle = ""
page.show("public-waiting.html", w) page.show("public-waiting.html", w)
} else { } else {
@ -28,7 +29,7 @@ func handleMain(w http.ResponseWriter, req *http.Request) {
func loadVotingPage(w http.ResponseWriter, req *http.Request) { func loadVotingPage(w http.ResponseWriter, req *http.Request) {
page := initPublicPage(w, req) page := initPublicPage(w, req)
// Client authentication required // Client authentication required
if (db.site.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { if (m.site.GetAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth {
page.show("unauthorized.html", w) page.show("unauthorized.html", w)
return return
} }
@ -37,7 +38,7 @@ func loadVotingPage(w http.ResponseWriter, req *http.Request) {
Timestamp string Timestamp string
} }
vpd := new(votingPageData) vpd := new(votingPageData)
tms := db.getAllTeams() tms := m.jam.Teams
// Randomize the team list // Randomize the team list
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
@ -55,7 +56,7 @@ func loadVotingPage(w http.ResponseWriter, req *http.Request) {
func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) { func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) {
page := initPublicPage(w, req) page := initPublicPage(w, req)
// Client authentication required // Client authentication required
if (db.site.getAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth { if (m.site.GetAuthMode() == AuthModeAuthentication) && !page.ClientIsAuth {
page.show("unauthorized.html", w) page.show("unauthorized.html", w)
return return
} }
@ -66,23 +67,37 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) {
ts := req.FormValue("timestamp") ts := req.FormValue("timestamp")
timestamp, err := time.Parse(time.RFC3339, ts) timestamp, err := time.Parse(time.RFC3339, ts)
if err != nil { if err != nil {
page.session.setFlashMessage("Error parsing timestamp: "+ts, "error") page.session.setFlashMessage("Error creating vote", "error")
fmt.Println("Error parsing timestamp: " + ts)
redirect("/", w, req) redirect("/", w, req)
} }
client := db.getClient(page.ClientId) client := m.GetClient(page.ClientId)
if _, err := client.getVote(timestamp); err == nil {
// voteSlice is an ordered string slice of the voters preferences
voteCSV := req.FormValue("uservote")
voteSlice := strings.Split(voteCSV, ",")
if _, err = m.jam.GetVote(client.UUID, timestamp); err == nil {
// Duplicate vote... Cancel it. // Duplicate vote... Cancel it.
page.session.setFlashMessage("Duplicate vote!", "error") page.session.setFlashMessage("Duplicate vote!", "error")
redirect("/", w, req) redirect("/", w, req)
} }
// voteSlice is an ordered string slice of the voters preferences
voteCSV := req.FormValue("uservote") var vt *Vote
voteSlice := strings.Split(voteCSV, ",") if vt, err = NewVote(client.UUID, timestamp); err != nil {
if err := client.saveVote(timestamp, voteSlice); err != nil { fmt.Println("Error creating vote: " + err.Error())
page.session.setFlashMessage("Error Saving Vote: "+err.Error(), "error") page.session.setFlashMessage("Error creating vote", "error")
redirect("/", w, req)
} }
if newVote, err := client.getVote(timestamp); err == nil { if err = vt.SetChoices(voteSlice); err != nil {
site.Votes = append(site.Votes, *newVote) fmt.Println("Error creating vote: " + err.Error())
page.session.setFlashMessage("Error creating vote", "error")
redirect("/", w, req)
}
if err := m.jam.AddVote(vt); err != nil {
fmt.Println("Error adding vote: " + err.Error())
page.session.setFlashMessage("Error creating vote", "error")
redirect("/", w, req)
} }
page.session.setFlashMessage("Vote Saved!", "success large fading") page.session.setFlashMessage("Vote Saved!", "success large fading")
redirect("/", w, req) redirect("/", w, req)
@ -91,19 +106,22 @@ func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) {
func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) { func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) {
// Thumbnail requests are open even without client authentication // Thumbnail requests are open even without client authentication
vars := mux.Vars(req) vars := mux.Vars(req)
tm := db.getTeam(vars["teamid"]) tm, err := m.jam.GetTeamById(vars["teamid"])
if tm == nil { if err != nil {
fmt.Println("handleThumbnailRequest: " + err.Error())
http.Error(w, "Couldn't find image", 404) http.Error(w, "Couldn't find image", 404)
return return
} }
ss := tm.getScreenshot(vars["imageid"]) ss, err := tm.Game.GetScreenshot(vars["imageid"])
if ss == nil { if err != nil {
fmt.Println("handleThumbnailRequest: " + err.Error())
http.Error(w, "Couldn't find image", 404) http.Error(w, "Couldn't find image", 404)
return return
} }
w.Header().Set("Content-Type", "image/"+ss.Filetype) w.Header().Set("Content-Type", "image/"+ss.Filetype)
dat, err := base64.StdEncoding.DecodeString(ss.Thumbnail) dat, err := base64.StdEncoding.DecodeString(ss.Thumbnail)
if err != nil { if err != nil {
fmt.Println("handleThumbnailRequest: " + err.Error())
http.Error(w, "Couldn't find image", 404) http.Error(w, "Couldn't find image", 404)
return return
} }
@ -113,19 +131,22 @@ func handleThumbnailRequest(w http.ResponseWriter, req *http.Request) {
func handleImageRequest(w http.ResponseWriter, req *http.Request) { func handleImageRequest(w http.ResponseWriter, req *http.Request) {
// Image requests are open even without client authentication // Image requests are open even without client authentication
vars := mux.Vars(req) vars := mux.Vars(req)
tm := db.getTeam(vars["teamid"]) tm, err := m.jam.GetTeamById(vars["teamid"])
if tm == nil { if err != nil {
fmt.Println("handleImageRequest: " + err.Error())
http.Error(w, "Couldn't find image", 404) http.Error(w, "Couldn't find image", 404)
return return
} }
ss := tm.getScreenshot(vars["imageid"]) ss, err := tm.Game.GetScreenshot(vars["imageid"])
if ss == nil { if err != nil {
fmt.Println("handleImageRequest: " + err.Error())
http.Error(w, "Couldn't find image", 404) http.Error(w, "Couldn't find image", 404)
return return
} }
w.Header().Set("Content-Type", "image/"+ss.Filetype) w.Header().Set("Content-Type", "image/"+ss.Filetype)
dat, err := base64.StdEncoding.DecodeString(ss.Image) dat, err := base64.StdEncoding.DecodeString(ss.Image)
if err != nil { if err != nil {
fmt.Println("handleImageRequest: " + err.Error())
http.Error(w, "Couldn't find image", 404) http.Error(w, "Couldn't find image", 404)
return return
} }
@ -134,15 +155,15 @@ func handleImageRequest(w http.ResponseWriter, req *http.Request) {
func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) { func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) {
// Team Management pages are open even without client authentication // Team Management pages are open even without client authentication
if db.getPublicSiteMode() == SiteModeVoting { if m.site.GetPublicMode() == SiteModeVoting {
redirect("/", w, req) redirect("/", w, req)
} }
page := initPublicPage(w, req) page := initPublicPage(w, req)
vars := mux.Vars(req) vars := mux.Vars(req)
page.SubTitle = "Team Details" page.SubTitle = "Team Details"
teamId := vars["id"] teamId := vars["id"]
tm := db.getTeam(teamId) tm, err := m.jam.GetTeamById(teamId)
if tm != nil { if err == nil {
// Team self-management functions // Team self-management functions
switch vars["function"] { switch vars["function"] {
case "": case "":
@ -150,41 +171,35 @@ func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) {
page.TemplateData = tm page.TemplateData = tm
page.show("public-teammgmt.html", w) page.show("public-teammgmt.html", w)
case "savemember": case "savemember":
m := newTeamMember(req.FormValue("newmembername")) m, err := NewTeamMember(tm.UUID, "")
if err != nil {
page.session.setFlashMessage("Error adding team member: "+err.Error(), "error")
redirect("/team/"+tm.UUID+"#members", w, req)
}
m.Name = req.FormValue("newmembername")
m.SlackId = req.FormValue("newmemberslackid") m.SlackId = req.FormValue("newmemberslackid")
m.Twitter = req.FormValue("newmembertwitter") m.Twitter = req.FormValue("newmembertwitter")
m.Email = req.FormValue("newmemberemail") m.Email = req.FormValue("newmemberemail")
if err := tm.updateTeamMember(m); err != nil { if err := tm.AddTeamMember(m); err != nil {
page.session.setFlashMessage("Error adding team member: "+err.Error(), "error") page.session.setFlashMessage("Error adding team member: "+err.Error(), "error")
} else { } else {
page.session.setFlashMessage(m.Name+" added to team!", "success") page.session.setFlashMessage(m.Name+" added to team!", "success")
} }
refreshTeamsInMemory()
redirect("/team/"+tm.UUID+"#members", w, req) redirect("/team/"+tm.UUID+"#members", w, req)
case "deletemember": case "deletemember":
mbrId := req.FormValue("memberid") mbrId := req.FormValue("memberid")
m := tm.getTeamMember(mbrId) err := tm.RemoveTeamMemberById(mbrId)
if m != nil { if err != nil {
if err := tm.deleteTeamMember(m); err != nil { page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error")
page.session.setFlashMessage("Error deleting team member: "+err.Error(), "error")
} else {
page.session.setFlashMessage(m.Name+" deleted from team", "success")
}
} else { } else {
page.session.setFlashMessage("Couldn't find member to delete", "error") page.session.setFlashMessage("Team member removed", "success")
} }
refreshTeamsInMemory()
redirect("/team/"+tm.UUID, w, req) redirect("/team/"+tm.UUID, w, req)
case "savegame": case "savegame":
gm := newGame(tm.UUID) tm.Game.Name = req.FormValue("gamename")
gm.Name = req.FormValue("gamename") tm.Game.Link = req.FormValue("gamelink")
gm.Link = req.FormValue("gamelink") tm.Game.Description = req.FormValue("gamedesc")
gm.Description = req.FormValue("gamedesc") page.session.setFlashMessage("Team game updated", "success")
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) redirect("/team/"+tm.UUID, w, req)
case "screenshotupload": case "screenshotupload":
if err := saveScreenshots(tm, req); err != nil { if err := saveScreenshots(tm, req); err != nil {
@ -193,7 +208,7 @@ func handleTeamMgmtRequest(w http.ResponseWriter, req *http.Request) {
redirect("/team/"+tm.UUID, w, req) redirect("/team/"+tm.UUID, w, req)
case "screenshotdelete": case "screenshotdelete":
ssid := vars["subid"] ssid := vars["subid"]
if err := tm.deleteScreenshot(ssid); err != nil { if err := tm.Game.RemoveScreenshot(ssid); err != nil {
page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error") page.session.setFlashMessage("Error deleting screenshot: "+err.Error(), "error")
} }
redirect("/team/"+tm.UUID, w, req) redirect("/team/"+tm.UUID, w, req)