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()
+}