diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..c6bc382 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/db/test.sqlite + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..ed8593d --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/api/common/base.go b/api/common/base.go index 5f56ac3..70f1d1e 100644 --- a/api/common/base.go +++ b/api/common/base.go @@ -1,33 +1,29 @@ package common import ( + "database/sql" + "github.com/onestay/MarathonTools-API/marathon" "net/http" "github.com/go-redis/redis" "gopkg.in/mgo.v2" - "github.com/onestay/MarathonTools-API/api/models" "github.com/onestay/MarathonTools-API/ws" ) // Controller is the base struct for any controller. It's used to manage state and other things. type Controller struct { WS *ws.Hub - MGS *mgo.Session - Col *mgo.Collection - RunIndex int - CurrentRun *models.Run - NextRun *models.Run - PrevRun *models.Run - UpNext *models.Run RedisClient *redis.Client TimerState TimerState TimerTime float64 HTTPClient http.Client + Marathon marathon.Marathon // SocialUpdatesChan is used to communicate with the socialController on Twitter and twitch updates SocialUpdatesChan chan int CL *Checklist Settings *SettingsProvider + db *sql.DB } type httpResponse struct { @@ -55,26 +51,24 @@ const ( ) // NewController returns a new base controller -func NewController(hub *ws.Hub, mgs *mgo.Session, crIndex int, rc *redis.Client) *Controller { - var runs []models.Run - err := mgs.DB("marathon").C("runs").Find(nil).All(&runs) +func NewController(hub *ws.Hub, mgs *mgo.Session, crIndex int, rc *redis.Client) (*Controller, error) { + db, err := sql.Open("sqlite3", "../../db/test.sqlite") if err != nil { - return nil + return nil, err } + c := &Controller{ WS: hub, - MGS: mgs, - RunIndex: crIndex, - Col: mgs.DB("marathon").C("runs"), RedisClient: rc, TimerState: 2, TimerTime: 0, HTTPClient: http.Client{}, SocialUpdatesChan: make(chan int, 1), + db: db, } c.CL = NewChecklist(c) c.Settings = InitSettings(c) c.UpdateActiveRuns() c.UpdateUpNext() - return c + return c, nil } diff --git a/api/models/marathon.go b/api/models/marathon.go deleted file mode 100644 index b9bfba7..0000000 --- a/api/models/marathon.go +++ /dev/null @@ -1,10 +0,0 @@ -package models - -// Marathon represents the general Marathon -type Marathon struct { - Name string `json:"name" bson:"name"` - Runs []Run `json:"runs" bson:"runs"` - RunCount int `json:"runCount" bson:"runCount"` - CurrentRun string `json:"currentRun" bson:"currentRun"` - IsRunning bool `bson:"isRunning"` -} diff --git a/api/models/player.go b/api/models/player.go new file mode 100644 index 0000000..863939a --- /dev/null +++ b/api/models/player.go @@ -0,0 +1,42 @@ +package models + +import "database/sql" + +type PlayerInfo struct { + Id int64 `json:"id,omitempty"` + DisplayName string `json:"displayName,omitempty"` + Country string `json:"country,omitempty"` + TwitterName string `json:"twitterName,omitempty"` + TwitchName string `json:"twitchName,omitempty"` + YoutubeName string `json:"youtubeName,omitempty"` +} + +func AddPlayer(player PlayerInfo, db *sql.DB) (int64, error) { + stmt, err := db.Prepare("INSERT INTO players(display_name, country, twitter_name, twitch_name, youtube_name) VALUES (?, ?, ?, ?, ?)") + if err != nil { + return 0, err + } + defer stmt.Close() + + result, err := stmt.Exec(player.DisplayName, player.Country, player.TwitterName, player.TwitchName, player.YoutubeName) + if err != nil { + return 0, err + } + + id, err := result.LastInsertId() + if err != nil { + return 0, err + } + + return id, nil +} + +func GetPlayerById(id int64, db *sql.DB) (*PlayerInfo, error) { + var player PlayerInfo + err := db.QueryRow("SELECT * FROM players WHERE id=?", id).Scan(&player.Id, &player.DisplayName, &player.Country, &player.TwitterName, &player.TwitchName, &player.YoutubeName) + if err != nil { + return nil, err + } + + return &player, nil +} diff --git a/api/models/player_test.go b/api/models/player_test.go new file mode 100644 index 0000000..9de5d03 --- /dev/null +++ b/api/models/player_test.go @@ -0,0 +1,45 @@ +package models + +import ( + "database/sql" + "fmt" + _ "github.com/mattn/go-sqlite3" + "testing" +) + +func TestPlayer(t *testing.T) { + db, err := sql.Open("sqlite3", "../../db/test.sqlite") + if err != nil { + t.Fail() + } + + for i := 0; i < 10; i++ { + p := PlayerInfo{ + Id: 0, + DisplayName: fmt.Sprintf("onestay%d", i), + Country: "de", + TwitterName: "@onest4y", + TwitchName: "onestay", + YoutubeName: "", + } + + _, err = AddPlayer(p, db) + if err != nil { + panic(err) + } + } + + player, err := GetPlayerById(1, db) + if err != nil { + panic(err) + } + + if player.DisplayName != "onestay0" { + t.Errorf("Expected DisplayName to be %s but got %s", "onestay0", player.DisplayName) + } + + _, err = GetPlayerById(10000, db) + if err == nil { + t.Errorf("Expected GetPlayerById with id 10000 to fail") + } +} diff --git a/api/models/run.go b/api/models/run.go index a6953be..59f67ad 100644 --- a/api/models/run.go +++ b/api/models/run.go @@ -1,38 +1,305 @@ package models import ( - "gopkg.in/mgo.v2/bson" + "database/sql" + "fmt" +) + +const ( + RunIdEmptyRun = -1 + RunIdNotInit = -2 ) // Run represents a single run +// TODO: look into making more fields private here type Run struct { - RunID bson.ObjectId `json:"runID" bson:"_id"` - GameInfo GameInfo `json:"gameInfo" bson:"gameInfo"` - RunInfo runInfo `json:"runInfo" bson:"runInfo"` - Players []PlayerInfo `json:"players" bson:"playerInfo"` + Id int64 `json:"runID"` + GameInfo GameInfo `json:"gameInfo"` + RunInfo RunInfo `json:"runInfo"` + Players []*PlayerInfo `json:"players"` + RunTime runTime `json:"runTime"` + PlayerRunTime map[int64]*runTime `json:"playerRunTime"` } type GameInfo struct { - GameName string `json:"gameName" bson:"gameName"` - ReleaseYear int `json:"releaseYear" bson:"releaseYear"` + GameName string `json:"gameName"` + ReleaseYear int `json:"releaseYear"` +} + +type RunInfo struct { + Estimate int64 `json:"estimate"` + Category string `json:"category"` + Platform string `json:"platform"` +} + +type runTime struct { + Finished bool `json:"finished"` + Time float64 `json:"time"` +} + +func CreateRun(gi GameInfo, ri RunInfo, players []*PlayerInfo) Run { + var run Run + + run.GameInfo = gi + run.Players = players + run.RunInfo = ri + run.Id = RunIdNotInit + + run.RunTime = runTime{} + run.PlayerRunTime = make(map[int64]*runTime, len(players)) + + return run +} + +// EmptyRun is a run identified by id 0. +func EmptyRun() *Run { + return &Run{Id: RunIdEmptyRun} } -type runInfo struct { - Estimate string `json:"estimate" bson:"estimate"` - Category string `json:"category" bson:"category"` - Platform string `json:"platform" bso:"platform"` +func (r Run) IsEmptyRun() bool { + return r.Id == RunIdEmptyRun } -type PlayerInfo struct { - DisplayName string `json:"displayName" bson:"displayName"` - Country string `json:"country" bson:"country"` - TwitterName string `json:"twitterName" bson:"twitterName"` - TwitchName string `json:"twitchName" bson:"twitchName"` - YoutubeName string `json:"youtubeName" bson:"youtubeName"` - Timer timerPlayerInfo `json:"timer" bson:"timer"` +func (r *Run) SetID(id int64) error { + if id <= 0 { + return fmt.Errorf("invalid id %d", id) + } + + r.Id = id + + return nil } -type timerPlayerInfo struct { - Finished bool `json:"finished" bson:"finished"` - Time float64 `json:"time" bson:"time"` +func AddRun(run Run, db *sql.DB) (int64, error) { + id, err := insertRunIntoDb(&run, db) + if err != nil { + return 0, err + } + + err = run.SetID(id) + if err != nil { + return 0, err + } + + // FIXME: if this fails we probably want to delete the run from the db too since otherwise it messes with stuff + err = AppendRunToSchedule(id, db) + if err != nil { + return 0, err + } + + return id, nil +} + +func insertRunIntoDb(run *Run, db *sql.DB) (int64, error) { + stmt, err := db.Prepare("INSERT INTO runs(game_name, release_year, estimate, category, platform, finished, time) VALUES (?, ?, ?, ?, ?, 0, 0)") + if err != nil { + return 0, err + } + defer stmt.Close() + + result, err := stmt.Exec(run.GameInfo.GameName, run.GameInfo.ReleaseYear, run.RunInfo.Estimate, run.RunInfo.Category, run.RunInfo.Platform) + if err != nil { + return 0, err + } + + id, err := result.LastInsertId() + if err != nil { + return 0, err + } + + err = run.SetID(id) + if err != nil { + return 0, err + } + + // FIXME: delete original entry from DB on error + err = insertRunPlayerRelation(run, db) + if err != nil { + return 0, err + } + return id, nil +} + +func insertRunPlayerRelation(run *Run, db *sql.DB) error { + tx, err := db.Begin() + if err != nil { + return err + } + stmt, err := tx.Prepare("INSERT INTO run_players (run_id, player_id, finished, time) VALUES (?, ?, ?, ?)") + if err != nil { + return err + } + + for _, player := range run.Players { + _, err := stmt.Exec(run.Id, player.Id, false, 0) + if err != nil { + // FIXME: cleanup needed + return err + } + } + err = tx.Commit() + if err != nil { + return err + } + + return nil +} + +func GetRuns(db *sql.DB) ([]*Run, error) { + sqlStmt := ` SELECT game_name, release_year, estimate, category, platform, r.finished, r.time, r.id, p.id, display_name, country, twitter_name, twitch_name, youtube_name, run_players.finished, run_players.time FROM run_players + INNER JOIN runs r ON r.id=run_players.run_id + INNER JOIN players p on p.id = run_players.player_id + INNER JOIN schedule s on r.id = s.run_id + ORDER BY row_number() OVER ()` + rows, err := db.Query(sqlStmt) + if err != nil { + return nil, err + } + var runs []*Run + + var prevRunId int64 = -1 + var prevRun *Run = nil + for rows.Next() { + var gi GameInfo + var ri RunInfo + var pi PlayerInfo + var ti runTime + var playerTimeInfo runTime + var run Run + var runId int64 + err = rows.Scan(&gi.GameName, &gi.ReleaseYear, &ri.Estimate, &ri.Category, &ri.Platform, &ti.Finished, &ti.Time, &runId, &pi.Id, &pi.DisplayName, &pi.Country, &pi.TwitterName, &pi.TwitchName, &pi.YoutubeName, &playerTimeInfo.Finished, &playerTimeInfo.Time) + if err != nil { + return nil, err + } + + if prevRunId == runId { + prevRun.Players = append(prevRun.Players, &pi) + prevRun.PlayerRunTime[pi.Id] = &playerTimeInfo + } else { + run.Id = runId + run.GameInfo = gi + run.RunInfo = ri + run.Players = make([]*PlayerInfo, 1) + run.PlayerRunTime = make(map[int64]*runTime) + run.PlayerRunTime[pi.Id] = &playerTimeInfo + run.Players[0] = &pi + run.RunTime = ti + + prevRun = &run + prevRunId = runId + + runs = append(runs, &run) + } + } + + return runs, nil +} + +func getRunByRunID(runId int64, db *sql.DB) (*Run, error) { + sqlStmt := ` SELECT game_name, release_year, estimate, category, platform, r.finished, r.time, r.id, p.id, display_name, country, twitter_name, twitch_name, youtube_name, run_players.finished, run_players.time FROM run_players + INNER JOIN runs r ON r.id=run_players.run_id + INNER JOIN players p on p.id = run_players.player_id + WHERE r.id=?` + rows, err := db.Query(sqlStmt, runId) + if err != nil { + return nil, err + } + + var run *Run = nil + for rows.Next() { + var gi GameInfo + var ri RunInfo + var pi PlayerInfo + var ti runTime + var playerTimeInfo runTime + var currentRun Run + var runId int64 + err = rows.Scan(&gi.GameName, &gi.ReleaseYear, &ri.Estimate, &ri.Category, &ri.Platform, &ti.Finished, &ti.Time, &runId, &pi.Id, &pi.DisplayName, &pi.Country, &pi.TwitterName, &pi.TwitchName, &pi.YoutubeName, &playerTimeInfo.Finished, &playerTimeInfo.Time) + if err != nil { + return nil, err + } + + if run != nil { + run.Players = append(currentRun.Players, &pi) + run.PlayerRunTime[pi.Id] = &playerTimeInfo + } else { + currentRun.Id = runId + currentRun.GameInfo = gi + currentRun.RunInfo = ri + currentRun.Players = make([]*PlayerInfo, 1) + currentRun.PlayerRunTime = make(map[int64]*runTime) + currentRun.PlayerRunTime[pi.Id] = &playerTimeInfo + currentRun.Players[0] = &pi + currentRun.RunTime = ti + + run = ¤tRun + } + } + + return run, nil + +} + +func getSchedulePositionFromRunId(runId int64, db *sql.DB) (int64, error) { + if runId < 0 { + return 0, fmt.Errorf("invalid runId provided") + } + sqlStmt := `SELECT schedule.pos FROM schedule WHERE schedule.run_id == ?` + + var currentPos int64 + err := db.QueryRow(sqlStmt, runId).Scan(¤tPos) + if err != nil { + return 0, err + } + + return currentPos, nil +} + +func getRunBySchedulePosition(position int64, db *sql.DB) (*Run, error) { + sqlStmt := `SELECT schedule.run_id FROM schedule WHERE pos=?` + + var runId int64 + err := db.QueryRow(sqlStmt, position).Scan(&runId) + if err != nil { + return nil, err + } + + return getRunByRunID(position, db) +} + +func GetNextRun(runId int64, db *sql.DB) (*Run, error) { + currentPos, err := getSchedulePositionFromRunId(runId, db) + if err != nil { + return nil, err + } + + return getRunBySchedulePosition(currentPos+1, db) +} + +func GetPrevRun(runId int64, db *sql.DB) (*Run, error) { + currentPos, err := getSchedulePositionFromRunId(runId, db) + if err != nil { + return nil, err + } + + return getRunBySchedulePosition(currentPos-1, db) +} + +func DeleteRun(runId int64, db *sql.DB) error { + _, err := db.Exec("DELETE FROM schedule WHERE run_id=?", runId) + if err != nil { + return err + } + + _, err = db.Exec("DELETE FROM run_players WHERE run_id=?", runId) + if err != nil { + return err + } + + _, err = db.Exec("DELETE FROM runs WHERE id=?", runId) + if err != nil { + return err + } + + return nil } diff --git a/api/models/run_test.go b/api/models/run_test.go new file mode 100644 index 0000000..fef5cee --- /dev/null +++ b/api/models/run_test.go @@ -0,0 +1,61 @@ +package models + +import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" + "testing" +) + +func TestAddRun(t *testing.T) { + db, err := sql.Open("sqlite3", "../../db/test.sqlite") + if err != nil { + panic(err) + } + + gi1 := GameInfo{GameName: "Portal 1"} + gi2 := GameInfo{GameName: "Portal 2"} + gi3 := GameInfo{GameName: "Portal 3"} + gi4 := GameInfo{GameName: "Portal 4"} + gi5 := GameInfo{GameName: "Portal 5"} + + p1, _ := GetPlayerById(1, db) + p2, _ := GetPlayerById(2, db) + p3, _ := GetPlayerById(3, db) + p4, _ := GetPlayerById(4, db) + p5, _ := GetPlayerById(5, db) + p6, _ := GetPlayerById(6, db) + p7, _ := GetPlayerById(7, db) + p8, _ := GetPlayerById(8, db) + p9, _ := GetPlayerById(9, db) + + var pi1 = []*PlayerInfo{p1} + var pi2 = []*PlayerInfo{p2, p6} + var pi3 = []*PlayerInfo{p4, p5} + var pi4 = []*PlayerInfo{p8, p1, p9, p3} + var pi5 = []*PlayerInfo{p7} + + AddRun(CreateRun(gi1, RunInfo{}, pi1), db) + AddRun(CreateRun(gi2, RunInfo{}, pi2), db) + AddRun(CreateRun(gi3, RunInfo{}, pi3), db) + AddRun(CreateRun(gi4, RunInfo{}, pi4), db) + AddRun(CreateRun(gi5, RunInfo{}, pi5), db) + + runs, err := GetRuns(db) + if err != nil { + t.Fatalf("Error %v", err) + } + + if len(runs) != 5 { + t.Fatalf("invalid run length expected 5 but got %d", len(runs)) + } + + aRun, err := getRunBySchedulePosition(1, db) + if err != nil { + panic(err) + } + + if aRun.GameInfo.GameName != "Portal 1" { + t.Fatalf("Expected game name at schedule position 1 to be \"Portal3\" but got %s", aRun.GameInfo.GameName) + } + +} diff --git a/api/models/schedule.go b/api/models/schedule.go new file mode 100644 index 0000000..5ceba02 --- /dev/null +++ b/api/models/schedule.go @@ -0,0 +1,19 @@ +package models + +import "database/sql" + +func AppendRunToSchedule(runID int64, db *sql.DB) error { + stmt, err := db.Prepare("INSERT INTO schedule(run_id) VALUES (?)") + if err != nil { + return err + } + + defer stmt.Close() + + _, err = stmt.Exec(runID) + if err != nil { + return err + } + + return nil +} diff --git a/api/routes/runs/runs.go b/api/routes/runs/runs.go index c2c98f9..757a982 100644 --- a/api/routes/runs/runs.go +++ b/api/routes/runs/runs.go @@ -57,7 +57,7 @@ func (rc RunController) AddRun(w http.ResponseWriter, r *http.Request, _ httprou run := models.Run{} json.NewDecoder(r.Body).Decode(&run) - run.RunID = bson.NewObjectId() + run.id = bson.NewObjectId() err := rc.base.MGS.DB("marathon").C("runs").Insert(run) if err != nil { @@ -65,7 +65,7 @@ func (rc RunController) AddRun(w http.ResponseWriter, r *http.Request, _ httprou return } - rc.base.Response(run.RunID.Hex(), "", http.StatusOK, w) + rc.base.Response(run.id.Hex(), "", http.StatusOK, w) go rc.base.WSRunsOnlyUpdate() go rc.base.UpdateActiveRuns() @@ -158,7 +158,7 @@ func (rc RunController) UpdateRun(w http.ResponseWriter, r *http.Request, ps htt log.Printf("Error in UpdateRun: %v", err) return } - updatedRun.RunID = bson.ObjectIdHex(runID) + updatedRun.id = bson.ObjectIdHex(runID) err = rc.base.MGS.DB("marathon").C("runs").UpdateId(bson.ObjectIdHex(runID), updatedRun) if err != nil { @@ -169,7 +169,7 @@ func (rc RunController) UpdateRun(w http.ResponseWriter, r *http.Request, ps htt w.WriteHeader(http.StatusNoContent) rc.base.WSRunsOnlyUpdate() - if updatedRun.RunID == rc.base.CurrentRun.RunID { + if updatedRun.id == rc.base.CurrentRun.id { rc.base.CurrentRun = &updatedRun rc.base.WSCurrentUpdate() } @@ -201,9 +201,9 @@ func (rc RunController) MoveRun(w http.ResponseWriter, _ *http.Request, ps httpr var indexToInsert int for i := 0; i < len(runs); i++ { - if runs[i].RunID == bson.ObjectIdHex(runID) { + if runs[i].id == bson.ObjectIdHex(runID) { index = i - } else if runs[i].RunID == bson.ObjectIdHex(after) { + } else if runs[i].id == bson.ObjectIdHex(after) { indexToInsert = i } } @@ -275,7 +275,7 @@ func (rc *RunController) UploadRunJSON(w http.ResponseWriter, r *http.Request, _ rc.base.MGS.DB("marathon").C("runs").RemoveAll(nil) for _, run := range runs { - run.RunID = bson.NewObjectId() + run.id = bson.NewObjectId() err := rc.base.MGS.DB("marathon").C("runs").Insert(run) if err != nil { panic("error adding run from UploadRunJSON into db") diff --git a/go.mod b/go.mod index aad8fbb..4a569b2 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,19 @@ module github.com/onestay/MarathonTools-API go 1.17 require ( - github.com/PuerkitoBio/goquery v1.7.1 + github.com/PuerkitoBio/goquery v1.8.0 github.com/dghubble/oauth1 v0.7.0 github.com/go-redis/redis v6.15.9+incompatible github.com/gorilla/websocket v1.4.2 github.com/joho/godotenv v1.4.0 github.com/julienschmidt/httprouter v1.3.0 - golang.org/x/net v0.0.0-20211008194852-3b03d305991f + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 ) require ( github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/mattn/go-sqlite3 v1.14.9 // indirect github.com/onsi/ginkgo v1.16.4 // indirect github.com/onsi/gomega v1.16.0 // indirect github.com/stretchr/testify v1.7.0 // indirect diff --git a/go.sum b/go.sum index 8e8afd1..41999d7 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ -github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4= -github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY= -github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= +github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= +github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -39,6 +38,8 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -62,17 +63,15 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f h1:1scJEYZBaF48BaG6tYbtxmLcXqwYGSfGcMoStTqkkIw= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/main.go b/main.go index ca17345..a69122c 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "database/sql" "log" "net/http" "os" @@ -51,6 +52,7 @@ func init() { log.Println("Error loading .env file.") } parseEnvVars() + openDB("./db") log.Printf("Connecting to mgo server at %v", mgoURL) mgs = getMongoSession() log.Printf("Connecting to redis server at %v", redisURL) @@ -65,7 +67,11 @@ func startHTTPServer() { r := httprouter.New() hub := ws.NewHub() log.Println("Initializing base controller...") - baseController := common.NewController(hub, mgs, 0, redisClient) + baseController, err := common.NewController(hub, mgs, 0, redisClient) + if err != nil { + log.Fatalf("Error initializing base controller %v", err) + + } log.Println("Initializing social controller...") social.NewSocialController(twitchClientID, twitchClientSecret, twitchCallback, twitterKey, twitterSecret, twitterCallback, socialAuthURL, socialAuthKey, featuredChannelsKey, baseController, r) log.Println("Initializing time controller...") @@ -75,19 +81,19 @@ func startHTTPServer() { var donProv donations.DonationProvider donationsEnabled := true - var err error + var donationProviderError error if os.Getenv("DONATION_PROVIDER") == "gdq" { log.Println("Creating new GDQ donation provider") - donProv, err = donationProviders.NewGDQDonationProvider(gdqURL, gdqEventID, gdqUsername, gdqPassword) - if err != nil { + donProv, donationProviderError = donationProviders.NewGDQDonationProvider(gdqURL, gdqEventID, gdqUsername, gdqPassword) + if donationProviderError != nil { log.Printf("Error during gdq donation provider creation: %v", err) donationsEnabled = false } } else if os.Getenv("DONATION_PROVIDER") == "srcom" { log.Println("Creating new speedrun.com donation provider") - donProv, err = donationProviders.NewSRComDonationProvider(marathonSlug) - if err != nil { + donProv, donationProviderError = donationProviders.NewSRComDonationProvider(marathonSlug) + if donationProviderError != nil { log.Printf("Error during donation provider creation: %v", err) donationsEnabled = false } @@ -136,6 +142,15 @@ func getMongoSession() *mgo.Session { return s } +func openDB(path string) (*sql.DB, error) { + db, err := sql.Open("sqlite3", "./db/test.db") + if err != nil { + return nil, err + } + + return db, nil +} + func getRedisClient() *redis.Client { client := redis.NewClient(&redis.Options{ Addr: redisURL + ":6379", diff --git a/marathon/marathon.go b/marathon/marathon.go new file mode 100644 index 0000000..f1bb2f3 --- /dev/null +++ b/marathon/marathon.go @@ -0,0 +1,31 @@ +package marathon + +import ( + "github.com/onestay/MarathonTools-API/api/models" + "sync" +) + +const InitialRunCap = 30 + +// Marathon represents the general Marathon +type Marathon struct { + name string + runState *RunState + marathonMutex *sync.Mutex +} + +func NewMarathon(name string, index int32) *Marathon { + marathon := Marathon{ + name: name, + marathonMutex: &sync.Mutex{}, + runState: &RunState{ + index: index, + current: models.EmptyRun(), + next: models.EmptyRun(), + prev: models.EmptyRun(), + upNext: models.EmptyRun(), + }, + } + + return &marathon +} diff --git a/marathon/runState.go b/marathon/runState.go new file mode 100644 index 0000000..a7012d8 --- /dev/null +++ b/marathon/runState.go @@ -0,0 +1,39 @@ +package marathon + +import "github.com/onestay/MarathonTools-API/api/models" + +type RunState struct { + index int32 + current *models.Run + next *models.Run + prev *models.Run + upNext *models.Run +} + +func (m Marathon) GetState() RunState { + return *m.runState +} + +func (m Marathon) SetCurrentRun(run *models.Run) { + m.marathonMutex.Lock() + m.runState.current = run + m.marathonMutex.Unlock() +} + +func (m Marathon) SetNextRun(run *models.Run) { + m.marathonMutex.Lock() + m.runState.next = run + m.marathonMutex.Unlock() +} + +func (m Marathon) SetPrevRun(run *models.Run) { + m.marathonMutex.Lock() + m.runState.prev = run + m.marathonMutex.Unlock() +} + +func (m Marathon) SetUpNextRun(run *models.Run) { + m.marathonMutex.Lock() + m.runState.upNext = run + m.marathonMutex.Unlock() +}