diff --git a/Dockerfile b/Dockerfile index 2b5a31e..b3bac8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,13 @@ FROM golang:1.20 -WORKDIR /app +WORKDIR /jepp COPY go.mod ./ COPY go.sum ./ RUN go mod download -COPY ./cmd/server ./cmd/server +COPY ./cmd/jepp ./cmd/jepp COPY ./data/sqlite/jepp.db ./data/sqlite/jepp.db COPY ./pkg/models ./pkg/models COPY ./pkg/server ./pkg/server @@ -16,8 +16,8 @@ COPY ./pkg/utils ./pkg/utils COPY ./docs ./docs COPY ./static/site ./static/site -RUN go build -o bin/server github.com/ecshreve/jepp/cmd/server +RUN go build -o bin/jepp github.com/ecshreve/jepp/cmd/jepp EXPOSE 8880 -CMD [ "./bin/server" ] \ No newline at end of file +CMD [ "./bin/jepp" ] \ No newline at end of file diff --git a/TODO b/TODO index 7d43f9c..a02837f 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,7 @@ JEPP TODOS: tweaks: - ☐ swap columns on homepage - ☐ add "info" card above example on homepage + ✔ swap columns on homepage @done(23-06-28 23:04) + ✔ add "info" card above example on homepage @done(23-06-28 23:03) features: pagination: @@ -16,12 +16,12 @@ JEPP TODOS: ☐ requests per route rate-limiting: - ☐ add rate limiting to api + ✔ add rate limiting to api @done(23-06-28 23:03) - maybe not needed, rate limiting done via cloudflare chores: ☐ add test coverage - ☐ godoc comments / release package + ✔ godoc comments / release package @done(23-06-28 23:03) - added github action to dump sql and attach to release ☐ give endpoint descriptions a once over ☐ add codecov diff --git a/Taskfile.yml b/Taskfile.yml index 2b336c7..3b2be57 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -4,15 +4,9 @@ dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env'] includes: docker: ./taskfiles/docker-tasks.yml - dumper: + jepp: taskfile: ./taskfiles/go-tasks.yml - vars: {CMD_NAME: dumper} - scraper: - taskfile: ./taskfiles/go-tasks.yml - vars: {CMD_NAME: scrape} - server: - taskfile: ./taskfiles/go-tasks.yml - vars: {CMD_NAME: server} + vars: {CMD_NAME: jepp} tasks: default: @@ -23,65 +17,24 @@ tasks: build: desc: Builds all binaries. deps: - - task: dumper:build - - task: scraper:build - - task: server:build + - task: jepp:build test: desc: Runs all tests. deps: - - task: dumper:test - - task: scraper:test - - task: server:test + - task: jepp:test cmds: - go test ./... - scrape: - desc: Runs the scraper. - generates: - - bin/scraper - sources: - - cmd/scrape/* - - pkg/scrape/* - - pkg/models/* - - pkg/utils/* - cmds: - - task: scraper:run - serve: desc: Runs the server. deps: - - task: server:build + - task: jepp:build sources: - - cmd/server/* + - cmd/jepp/* - pkg/server/*.go - pkg/server/templates/**/* - pkg/models/* - pkg/utils/* cmds: - - ./bin/server - - genswag: - desc: Generates swagger docs for the API. - generates: - - docs/* - sources: - - cmd/server/* - - pkg/server/* - - pkg/models/* - - pkg/utils/* - cmds: - - swag fmt -d cmd/server,pkg/server,pkg/models,pkg/utils - - swag init -d cmd/server,pkg/server,pkg/models,pkg/utils - - sql:dump: - desc: WIP Dumps the database to a file. - cmds: - - mysqldump -u ${DB_USER} -p${DB_PASS} -h ${DB_HOST} -P ${DB_PORT} --skip-comments --column-statistics=0 --databases ${DB_NAME} > data/${DB_NAME}_dump.sql - - gzip data/${DB_NAME}_dump.sql - - sql:restore: - desc: WIP Restores the database from a file. - cmds: - - gunzip data/${DB_NAME}_dump.sql.gz - - mysql -u${DB_USER} -p${DB_PASS} -h ${DB_HOST} -P ${DB_PORT} < data/${DB_NAME}_dump.sql + - ./bin/jepp diff --git a/_internal/dumper/README.md b/_internal/dumper/README.md new file mode 100644 index 0000000..587f9fb --- /dev/null +++ b/_internal/dumper/README.md @@ -0,0 +1 @@ +# NOT MAINTAINED \ No newline at end of file diff --git a/cmd/dumper/main.go b/_internal/dumper/main.go similarity index 100% rename from cmd/dumper/main.go rename to _internal/dumper/main.go diff --git a/_internal/scraper/README.md b/_internal/scraper/README.md new file mode 100644 index 0000000..587f9fb --- /dev/null +++ b/_internal/scraper/README.md @@ -0,0 +1 @@ +# NOT MAINTAINED \ No newline at end of file diff --git a/pkg/scraper/game.go b/_internal/scraper/game.go similarity index 100% rename from pkg/scraper/game.go rename to _internal/scraper/game.go diff --git a/cmd/scrape/main.go b/_internal/scraper/main.go similarity index 79% rename from cmd/scrape/main.go rename to _internal/scraper/main.go index 114396f..c8675cc 100644 --- a/cmd/scrape/main.go +++ b/_internal/scraper/main.go @@ -1,10 +1,9 @@ -package main +package scraper import ( "os" "github.com/ecshreve/jepp/pkg/models" - "github.com/ecshreve/jepp/pkg/scraper" log "github.com/sirupsen/logrus" ) @@ -16,8 +15,8 @@ func main() { log.Info("Starting Jepp scraper...") // jdb := models.NewJeppDB("data/jepp.db") - jdb := models.NewJeppDB("data/sqlite/scrape.db") - num := scraper.ScrapeAndFillCluesForGame(jdb, 3109) + jdb := models.NewJeppDB() + num := ScrapeAndFillCluesForGame(jdb, 3109) log.Infof("inserted %d clues", num) // Change loop values to scrape different seasons. // for i := 25; i > 24; i-- { diff --git a/pkg/scraper/scraper.go b/_internal/scraper/scraper.go similarity index 100% rename from pkg/scraper/scraper.go rename to _internal/scraper/scraper.go diff --git a/pkg/scraper/season.go b/_internal/scraper/season.go similarity index 100% rename from pkg/scraper/season.go rename to _internal/scraper/season.go diff --git a/cmd/server/main.go b/cmd/jepp/main.go similarity index 51% rename from cmd/server/main.go rename to cmd/jepp/main.go index d905d71..9091bb8 100644 --- a/cmd/server/main.go +++ b/cmd/jepp/main.go @@ -4,20 +4,19 @@ import ( "os" "github.com/ecshreve/jepp/docs" - "github.com/ecshreve/jepp/pkg/models" "github.com/ecshreve/jepp/pkg/server" log "github.com/sirupsen/logrus" ) -// @title Jepp API Documentation -// @description This is a simple api to access jeopardy data. -// @version 1.0 -// @BasePath /api +// @title Jepp API Documentation +// @description This is a simple api to access jeopardy data. +// @version 1.0 +// @BasePath /api // -// @contact.name shreve +// @contact.name shreve // -// @license.name MIT License -// @license.url https://github.com/ecshreve/jepp/blob/main/LICENSE +// @license.name MIT License +// @license.url https://github.com/ecshreve/jepp/blob/main/LICENSE func main() { if os.Getenv("JEPP_ENV") == "prod" { docs.SwaggerInfo.Host = "jepp.app" @@ -26,9 +25,7 @@ func main() { log.SetLevel(log.DebugLevel) log.Info("Starting Jepp API server...") - jdb := models.NewJeppDB("data/sqlite/jepp.db") - - srv := server.NewServer(jdb) + srv := server.NewServer() if err := srv.Router.Run(":8880"); err != nil { log.Fatal(err) } diff --git a/pkg/models/category.go b/pkg/models/category.go index 384465e..c76ef76 100644 --- a/pkg/models/category.go +++ b/pkg/models/category.go @@ -21,8 +21,7 @@ func (c *Category) Dump() []string { } // GetCategoryGameCount returns the number of games a category has appeared in. -func (db *JeppDB) GetCategoryGameCount(categoryID int64) (int64, error) { - +func GetCategoryGameCount(categoryID int64) (int64, error) { var count int64 err := db.Get(&count, "SELECT COUNT(DISTINCT game_id) FROM clue WHERE category_id = ?", categoryID) @@ -34,7 +33,7 @@ func (db *JeppDB) GetCategoryGameCount(categoryID int64) (int64, error) { } // GetCategoryClueCount returns the number of clues a category has appeared in. -func (db *JeppDB) GetCategoryClueCount(categoryID int64) (int64, error) { +func GetCategoryClueCount(categoryID int64) (int64, error) { var count int64 @@ -47,7 +46,7 @@ func (db *JeppDB) GetCategoryClueCount(categoryID int64) (int64, error) { } // InsertCategory inserts a category into the database. -func (db *JeppDB) InsertCategory(name string) (*Category, error) { +func InsertCategory(name string) (*Category, error) { if name == "" { return nil, oops.Errorf("cannot insert empty category") @@ -74,7 +73,7 @@ func (db *JeppDB) InsertCategory(name string) (*Category, error) { } // UpdateCategory updates a category in the database. -func (db *JeppDB) UpdateCategory(c *Category) error { +func UpdateCategory(c *Category) error { if c == nil { return nil @@ -95,7 +94,7 @@ func (db *JeppDB) UpdateCategory(c *Category) error { } // GetCategories returns all categories in the database. -func (db *JeppDB) GetCategories(limit int64) ([]Category, error) { +func GetCategories(limit int64) ([]Category, error) { query := fmt.Sprintf("SELECT * FROM category ORDER BY category_id ASC LIMIT %d", limit) var cc []Category @@ -107,7 +106,7 @@ func (db *JeppDB) GetCategories(limit int64) ([]Category, error) { } // GetCategory returns the category with the given ID. -func (db *JeppDB) GetCategory(categoryID int64) (*Category, error) { +func GetCategory(categoryID int64) (*Category, error) { query := fmt.Sprintf("SELECT * FROM category WHERE category_id=%d ORDER BY category_id DESC LIMIT 1", categoryID) c := Category{} @@ -118,7 +117,7 @@ func (db *JeppDB) GetCategory(categoryID int64) (*Category, error) { return &c, nil } -func (db *JeppDB) GetCategoryByName(categoryName string) (*Category, error) { +func GetCategoryByName(categoryName string) (*Category, error) { query := "SELECT * FROM category WHERE name = ? LIMIT 1" c := Category{} @@ -131,7 +130,7 @@ func (db *JeppDB) GetCategoryByName(categoryName string) (*Category, error) { } // GetRandomCategory returns a single category from the database. -func (db *JeppDB) GetRandomCategory() (*Category, error) { +func GetRandomCategory() (*Category, error) { c := Category{} if err := db.Get(&c, "SELECT * FROM category ORDER BY RAND() LIMIT 1"); err != nil { return nil, oops.Wrapf(err, "getting random category") @@ -141,7 +140,7 @@ func (db *JeppDB) GetRandomCategory() (*Category, error) { } // GetRandomCategoryMany returns `limit` random categories from the database. -func (db *JeppDB) GetRandomCategoryMany(limit int64) ([]Category, error) { +func GetRandomCategoryMany(limit int64) ([]Category, error) { query := fmt.Sprintf("SELECT * FROM category ORDER BY RANDOM() LIMIT %d", limit) var cc []Category @@ -153,7 +152,7 @@ func (db *JeppDB) GetRandomCategoryMany(limit int64) ([]Category, error) { } // GetCategoriesForGame returns all categories for a given game. -func (db *JeppDB) GetCategoriesForGame(gameID int64) ([]Category, error) { +func GetCategoriesForGame(gameID int64) ([]Category, error) { var cc []Category if err := db.Select(&cc, "SELECT clue.category_id, category.name FROM clue JOIN category ON clue.category_id = category.category_id WHERE game_id=? GROUP BY category_id LIMIT 20", gameID); err != nil { return nil, oops.Wrapf(err, "could not get categories for game %d", gameID) diff --git a/pkg/models/clue.go b/pkg/models/clue.go index 650a3a5..2c508ed 100644 --- a/pkg/models/clue.go +++ b/pkg/models/clue.go @@ -50,7 +50,7 @@ func (c *Clue) String() string { } // InsertClue inserts a clue into the database. -func (db *JeppDB) InsertClue(c *Clue) error { +func InsertClue(c *Clue) error { if c == nil { return nil } @@ -70,7 +70,7 @@ func (db *JeppDB) InsertClue(c *Clue) error { } // UpdateClue updates a category in the database. -func (db *JeppDB) UpdateClue(c *Clue) error { +func UpdateClue(c *Clue) error { if c == nil { return nil } @@ -98,7 +98,7 @@ type CluesParams struct { // ListClues returns a list of clues in the database, defaults to returning // values ordered by game id descending. -func (db *JeppDB) GetClues(params CluesParams) ([]Clue, error) { +func GetClues(params CluesParams) ([]Clue, error) { query := "SELECT * FROM clue" if params.GameID != 0 { @@ -125,7 +125,7 @@ func (db *JeppDB) GetClues(params CluesParams) ([]Clue, error) { } // GetClue returns a single clue from the database. -func (db *JeppDB) GetClue(clueID int64) (*Clue, error) { +func GetClue(clueID int64) (*Clue, error) { query := fmt.Sprintf("SELECT * FROM clue WHERE clue_id=%d ORDER BY clue_id DESC LIMIT 1", clueID) c := Clue{} @@ -137,7 +137,7 @@ func (db *JeppDB) GetClue(clueID int64) (*Clue, error) { } // GetClue returns a single clue from the database. -func (db *JeppDB) GetRandomClue() (*Clue, error) { +func GetRandomClue() (*Clue, error) { c := Clue{} if err := db.Get(&c, "SELECT * FROM clue ORDER BY RANDOM() LIMIT 1"); err != nil { return nil, oops.Wrapf(err, "getting random clue") @@ -147,7 +147,7 @@ func (db *JeppDB) GetRandomClue() (*Clue, error) { } // GetRandomClueMany returns `limit` random clues from the database. -func (db *JeppDB) GetRandomClueMany(limit int64) ([]Clue, error) { +func GetRandomClueMany(limit int64) ([]Clue, error) { query := fmt.Sprintf("SELECT * FROM clue ORDER BY RANDOM() LIMIT %d", limit) var cc []Clue @@ -159,7 +159,7 @@ func (db *JeppDB) GetRandomClueMany(limit int64) ([]Clue, error) { } // CountClues returns the number of clues in the database. -func (db *JeppDB) CountClues() (int64, error) { +func CountClues() (int64, error) { var count int64 if err := db.Get(&count, "SELECT COUNT(*) FROM clue"); err != nil { return -1, oops.Wrapf(err, "could not get count of clues") diff --git a/pkg/models/data/jepp.db b/pkg/models/data/jepp.db new file mode 100644 index 0000000..bd7103c Binary files /dev/null and b/pkg/models/data/jepp.db differ diff --git a/pkg/models/game.go b/pkg/models/game.go index e9c9deb..73da738 100644 --- a/pkg/models/game.go +++ b/pkg/models/game.go @@ -37,7 +37,7 @@ func (g Game) String() string { } // InsertGame inserts a game into the database. -func (db *JeppDB) InsertGame(g *Game) error { +func InsertGame(g *Game) error { if g == nil { return nil } @@ -61,7 +61,7 @@ func (db *JeppDB) InsertGame(g *Game) error { // values ordered by game date, with most recent first, limited to 100 results. // // TODO: have this take a "lastClueID" arg or something for dumb pagination. -func (db *JeppDB) GetGames(limit int64) ([]Game, error) { +func GetGames(limit int64) ([]Game, error) { query := fmt.Sprintf("SELECT * FROM game ORDER BY game_date DESC LIMIT %d", limit) games := []Game{} @@ -73,7 +73,7 @@ func (db *JeppDB) GetGames(limit int64) ([]Game, error) { } // GetGamesBySeason returns a list of games in the database for a given season. -func (db *JeppDB) GetGamesBySeason(seasonID int64) ([]Game, error) { +func GetGamesBySeason(seasonID int64) ([]Game, error) { query := fmt.Sprintf("SELECT * FROM game WHERE season_id=%d ORDER BY game_date DESC LIMIT 300", seasonID) var gg []Game @@ -85,7 +85,7 @@ func (db *JeppDB) GetGamesBySeason(seasonID int64) ([]Game, error) { } // GetGame returns a single game from the database. -func (db *JeppDB) GetGame(gameID int64) (*Game, error) { +func GetGame(gameID int64) (*Game, error) { query := fmt.Sprintf("SELECT * FROM game WHERE game_id=%d LIMIT 1", gameID) g := Game{} @@ -97,7 +97,7 @@ func (db *JeppDB) GetGame(gameID int64) (*Game, error) { } // GetRandomGame returns a single game from the database. -func (db *JeppDB) GetRandomGame() (*Game, error) { +func GetRandomGame() (*Game, error) { g := Game{} if err := db.Get(&g, "SELECT * FROM game ORDER BY RANDOM() LIMIT 1"); err != nil { return nil, oops.Wrapf(err, "getting gid") @@ -107,7 +107,7 @@ func (db *JeppDB) GetRandomGame() (*Game, error) { } // GetRandomGameMany returns `limit` random games from the database. -func (db *JeppDB) GetRandomGameMany(limit int64) ([]Game, error) { +func GetRandomGameMany(limit int64) ([]Game, error) { query := fmt.Sprintf("SELECT * FROM game ORDER BY RANDOM() LIMIT %d", limit) var gg []Game diff --git a/pkg/models/models.go b/pkg/models/models.go index bfe82e3..631d491 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -1,15 +1,32 @@ package models import ( + "os" + "github.com/jmoiron/sqlx" _ "github.com/mattn/go-sqlite3" + log "github.com/sirupsen/logrus" ) type JeppDB struct { *sqlx.DB } -func NewJeppDB(dbfile string) *JeppDB { - db := sqlx.MustOpen("sqlite3", dbfile) +var db *sqlx.DB + +func NewJeppDB() *JeppDB { + dbfile := "data/sqlite/jepp.db" + db = sqlx.MustOpen("sqlite3", dbfile) + log.Debugf("NEW -- %v", db != nil) + log.Debug(os.Getenv("PWD")) + + return &JeppDB{DB: db} +} + +func NewTestJeppDB() *JeppDB { + db = sqlx.MustOpen("sqlite3", "testdata/jepptest.db") + log.Debugf("NEW -- %v", db != nil) + log.Debug(os.Getenv("PWD")) + return &JeppDB{DB: db} } diff --git a/pkg/models/season.go b/pkg/models/season.go index efd05bd..9231505 100644 --- a/pkg/models/season.go +++ b/pkg/models/season.go @@ -28,7 +28,7 @@ func (s Season) String() string { } // InsertSeason inserts a season into the database. -func (db *JeppDB) InsertSeason(s *Season) error { +func InsertSeason(s *Season) error { if s == nil { return nil } @@ -50,7 +50,7 @@ func (db *JeppDB) InsertSeason(s *Season) error { // GetSeasons returns a list of seasons in the database, defaults to returning // values ordered by season id, with most recent first. -func (db *JeppDB) GetSeasons() ([]Season, error) { +func GetSeasons() ([]Season, error) { seasons := []Season{} if err := db.Select(&seasons, "SELECT * FROM season ORDER BY season_id DESC LIMIT 100"); err != nil { return nil, oops.Wrapf(err, "could not list seasons") diff --git a/pkg/models/season_test.go b/pkg/models/season_test.go index 9e0686f..06a9c43 100644 --- a/pkg/models/season_test.go +++ b/pkg/models/season_test.go @@ -8,9 +8,9 @@ import ( ) func TestGetSeasons(t *testing.T) { - jdb := models.NewJeppDB("testdata/jepptest.db") + models.NewTestJeppDB() - seasons, err := jdb.GetSeasons() + seasons, err := models.GetSeasons() assert.NoError(t, err) assert.NotEmpty(t, seasons) assert.Equal(t, 39, len(seasons)) diff --git a/pkg/server/category.go b/pkg/server/category.go index f5e7ff9..a161c5e 100644 --- a/pkg/server/category.go +++ b/pkg/server/category.go @@ -35,7 +35,7 @@ func CategoryHandler(c *gin.Context) { } if filter.Random != nil { - category, err := db.GetRandomCategoryMany(*filter.Limit) + category, err := models.GetRandomCategoryMany(*filter.Limit) if err != nil { log.Error(oops.Wrapf(err, "unable to get random category")) utils.NewError(c, http.StatusBadRequest, err) @@ -47,7 +47,7 @@ func CategoryHandler(c *gin.Context) { } if filter.ID != nil { - category, err := db.GetCategory(*filter.ID) + category, err := models.GetCategory(*filter.ID) if err != nil { log.Error(oops.Wrapf(err, "unable to get category %d", *filter.ID)) utils.NewError(c, http.StatusBadRequest, err) @@ -58,7 +58,7 @@ func CategoryHandler(c *gin.Context) { return } - cats, err := db.GetCategories(*filter.Limit) + cats, err := models.GetCategories(*filter.Limit) if err != nil { log.Error(oops.Wrapf(err, "unable to get categories")) utils.NewError(c, http.StatusBadRequest, err) diff --git a/pkg/server/clue.go b/pkg/server/clue.go index a577c29..6aee931 100644 --- a/pkg/server/clue.go +++ b/pkg/server/clue.go @@ -44,7 +44,7 @@ func ClueHandler(c *gin.Context) { } if filter.Random != nil { - clues, err := db.GetRandomClueMany(*filter.Limit) + clues, err := models.GetRandomClueMany(*filter.Limit) if err != nil { log.Error(oops.Wrapf(err, "unable to get random clue")) utils.NewError(c, http.StatusBadRequest, err) @@ -56,7 +56,7 @@ func ClueHandler(c *gin.Context) { } if filter.ID != nil { - clue, err := db.GetClue(*filter.ID) + clue, err := models.GetClue(*filter.ID) if err != nil { log.Error(oops.Wrapf(err, "unable to get clue %d", *filter.ID)) utils.NewError(c, http.StatusBadRequest, err) @@ -75,7 +75,7 @@ func ClueHandler(c *gin.Context) { Limit: *filter.Limit, } - clues, err := db.GetClues(*cluesParams) + clues, err := models.GetClues(*cluesParams) if err != nil { log.Error(oops.Wrapf(err, "unable to get clues")) utils.NewError(c, http.StatusBadRequest, err) diff --git a/pkg/server/dev_handlers.go b/pkg/server/dev_handlers.go deleted file mode 100644 index 03aaa8b..0000000 --- a/pkg/server/dev_handlers.go +++ /dev/null @@ -1,315 +0,0 @@ -package server - -import ( - "strconv" - - "github.com/ecshreve/jepp/pkg/models" - "github.com/gin-gonic/gin" -) - -// registerDevHandlers registers route handlers for development. -func registerDevHandlers() { - r := gin.Default() - - r.LoadHTMLGlob("pkg/server/templates/**/*") - - r.GET("/debug/:clueID", DebugUIHandler) - // s.Router.GET("/:clueID", s.ClueUIHandler) - // s.Router.POST("/:clueID", s.ClueUIPOSTHandler) - // s.Router.GET("/quiz", s.QuizHandler) - // s.Router.POST("/quiz", s.QuizHandler) -} - -func DebugUIHandler(c *gin.Context) { - clueID, err := strconv.ParseInt(c.Param("clueID"), 10, 64) - if err != nil { - c.JSON(400, gin.H{"error": "invalid clueID"}) - return - } - - clue, _ := db.GetClue(clueID) - game, _ := db.GetGame(clue.GameID) - category, _ := db.GetCategory(clue.CategoryID) - - dat := struct { - *models.Clue - *models.Game - *models.Category - }{ - Clue: clue, - Game: game, - Category: category, - } - c.HTML(200, "debug.html.tpl", dat) -} - -// func QuizHandler(c *gin.Context) { -// if c.Request.Method == "POST" { -// cor := c.PostForm("correct") -// inc := c.PostForm("incorrect") - -// correct := len(cor) > len(inc) -// if correct { -// s.QZ.Correct++ -// } else { -// s.QZ.Incorrect++ -// } -// s.QZ.Total++ -// } - -// clue, err := db.GetRandomClue(nil) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch random clue"}) -// return -// } - -// cat, err := db.GetCategory(clue.CategoryID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch category for clue"}) -// return -// } - -// game, err := db.GetGame(clue.GameID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch game for clue"}) -// return -// } - -// s.QZ.Clues = append(s.QZ.Clues, clue) - -// c.HTML(200, "quiz.html.tpl", gin.H{ -// "Clue": clue, -// "Category": cat, -// "Session": s.QZ, -// "Game": game, -// "Viz": Viz(*s.QZ), -// }) -// } - -// // ClueUIPOSTHandler godoc -// func ClueUIPOSTHandler(c *gin.Context) { -// clueIDStr := c.Param("clueID") -// clueID, err := strconv.ParseInt(clueIDStr, 10, 64) -// if err != nil { -// c.JSON(400, gin.H{"error": "invalid clueID"}) -// return -// } - -// clue, err := db.GetClue(clueID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch clue for clueID"}) -// return -// } - -// categoryIDStr := c.PostForm("cat-sel") -// categoryID, err := strconv.ParseInt(categoryIDStr, 10, 64) -// if err != nil { -// c.JSON(400, gin.H{"error": "invalid categoryID"}) -// return -// } - -// clues, err := db.ListClues(models.CluesParams{GameID: clue.GameID, CategoryID: categoryID}) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch clues for categoryID"}) -// return -// } -// log.Infof("clues: %s", pretty.Sprint(clues)) - -// newClueIDStr := fmt.Sprintf("%d", clues[0].ClueID) -// c.Params = gin.Params{gin.Param{Key: "clueIDStr", Value: newClueIDStr}} -// c.Request.Method = "GET" -// c.Redirect(302, "/"+newClueIDStr) -// } - -// // ClueUIHandler godoc -// func ClueUIHandler(c *gin.Context) { -// if c.Request.Method == "POST" { -// s.ClueUIPOSTHandler(c) -// return -// } - -// clueID, err := strconv.ParseInt(c.Param("clueID"), 10, 64) -// if err != nil { -// c.JSON(400, gin.H{"error": "invalid clueID"}) -// return -// } - -// clue, err := db.GetClue(clueID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch clue for clueID"}) -// return -// } - -// game, err := db.GetGame(clue.GameID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch game for clue"}) -// return -// } - -// category, err := db.GetCategory(clue.CategoryID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch category for clue"}) -// return -// } - -// clueJSON := s.jsonHelper(clue) - -// categoriesForGame, err := db.GetCategoriesForGame(clue.GameID) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch categories for game"}) -// return -// } - -// catOpts := []models.Option{} -// for _, c := range categoriesForGame { -// catOpts = append(catOpts, models.Option{ -// OptionKey: fmt.Sprintf("%d", c.CategoryID), -// OptionVal: c.Name, -// Selected: c.CategoryID == clue.CategoryID, -// }) -// } - -// cluesForGame, err := db.ListClues(models.CluesParams{GameID: clue.GameID}) -// if err != nil { -// c.JSON(400, gin.H{"error": "couldn't fetch clues for game"}) -// return -// } - -// nextClue := models.Clue{} -// prevClue := models.Clue{} -// for i, c := range cluesForGame { -// if c.ClueID == clue.ClueID { -// if i > 0 { -// prevClue = *cluesForGame[i+1] -// } -// if i < len(cluesForGame)-1 { -// nextClue = *cluesForGame[i-1] -// } -// } -// } - -// navLinks := models.NavLinks{ -// NextClue: fmt.Sprintf("%d", nextClue.ClueID), -// PrevClue: fmt.Sprintf("%d", prevClue.ClueID), -// } - -// options := &models.Options{ -// ClueID: clue.ClueID, -// Links: navLinks, -// CategoryOptions: catOpts, -// } - -// debug := struct { -// Clue *models.Clue -// Game *models.Game -// Category *models.Category -// Stats *models.Stats -// Options *models.Options -// ClueJSON string -// }{ -// Clue: clue, -// Game: game, -// Category: category, -// Stats: s.Stats, -// Options: options, -// ClueJSON: pretty.Sprint(clueJSON), -// } -// c.HTML(200, "clue-base.html.tpl", debug) -// } - -// func jsonHelper(clue *models.Clue) map[string]interface{} { -// cat, _ := db.GetCategory(clue.CategoryID) -// game, _ := db.GetGame(clue.GameID) - -// return map[string]interface{}{ -// "clueID": clue.ClueID, -// "categoryID": clue.CategoryID, -// "categoryName": cat.Name, -// "gameID": clue.GameID, -// "gameDate": game.GameDate, -// "question": clue.Question, -// "answer": clue.Answer, -// } -// } - -// // adapted from -// // https://github.com/go-echarts/go-echarts/blob/master/templates/base.go -// // https://github.com/go-echarts/go-echarts/blob/master/templates/header.go -// var baseTpl = ` -//
-//
-//
-// {{- range .JSAssets.Values }} -// -// {{- end }} -// -// ` - -// type snippetRenderer struct { -// c interface{} -// before []func() -// } - -// func newSnippetRenderer(c interface{}, before ...func()) chartrender.Renderer { -// return &snippetRenderer{c: c, before: before} -// } - -// func (r *snippetRenderer) Render(w io.Writer) error { -// const tplName = "chart" -// for _, fn := range r.before { -// fn() -// } - -// tpl := template. -// Must(template.New(tplName). -// Funcs(template.FuncMap{ -// "safeJS": func(s interface{}) template.JS { -// return template.JS(fmt.Sprint(s)) -// }, -// }). -// Parse(baseTpl), -// ) - -// err := tpl.ExecuteTemplate(w, tplName, r.c) -// return err -// } - -// func renderToHtml(c interface{}) template.HTML { -// var buf bytes.Buffer -// r := c.(chartrender.Renderer) -// err := r.Render(&buf) -// if err != nil { -// log.Printf("Failed to render chart: %s", err) -// return "" -// } - -// return template.HTML(buf.String()) -// } - -// func Viz(qz QuizSession) template.HTML { -// // initialize chart -// pie := charts.NewPie() -// pie.Renderer = newSnippetRenderer(pie, pie.Validate) - -// // preformat data -// pieData := []opts.PieData{ -// {Name: "Correct", Value: qz.Correct, ItemStyle: &opts.ItemStyle{Color: "#55bf32"}}, -// {Name: "Incorrect", Value: qz.Incorrect, ItemStyle: &opts.ItemStyle{Color: "#eb4034"}}, -// } - -// // put data into chart -// pie.AddSeries("Answers", pieData).SetSeriesOptions( -// charts.WithLabelOpts(opts.Label{Show: false, Formatter: "{b}: {c}"}), -// ) - -// var htmlSnippet template.HTML = renderToHtml(pie) -// return htmlSnippet -// } diff --git a/pkg/server/game.go b/pkg/server/game.go index 6ea13b2..52ef66a 100644 --- a/pkg/server/game.go +++ b/pkg/server/game.go @@ -35,7 +35,7 @@ func GameHandler(c *gin.Context) { } if filter.Random != nil { - games, err := db.GetRandomGameMany(*filter.Limit) + games, err := models.GetRandomGameMany(*filter.Limit) if err != nil { log.Error(oops.Wrapf(err, "unable to get random game")) utils.NewError(c, http.StatusBadRequest, err) @@ -47,7 +47,7 @@ func GameHandler(c *gin.Context) { } if filter.ID != nil { - game, err := db.GetGame(*filter.ID) + game, err := models.GetGame(*filter.ID) if err != nil { log.Error(oops.Wrapf(err, "unable to get game %d", *filter.ID)) utils.NewError(c, http.StatusBadRequest, err) @@ -59,7 +59,7 @@ func GameHandler(c *gin.Context) { return } - games, err := db.GetGames(*filter.Limit) + games, err := models.GetGames(*filter.Limit) if err != nil { log.Error(oops.Wrapf(err, "unable to get games")) utils.NewError(c, http.StatusBadRequest, err) diff --git a/pkg/server/server.go b/pkg/server/server.go index 1113473..c9aec19 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -11,8 +11,6 @@ import ( ginSwagger "github.com/swaggo/gin-swagger" ) -var db *models.JeppDB - // Server is the API server. type Server struct { ID string @@ -21,12 +19,12 @@ type Server struct { } // NewServer returns a new API server. -func NewServer(jdb *models.JeppDB) *Server { +func NewServer() *Server { s := &Server{ ID: "SERVER", Clock: clock.New(), } - db = jdb + models.NewJeppDB() s.Router = registerHandlers() // TODO: fix this diff --git a/pkg/server/ui.go b/pkg/server/ui.go index 63efeb9..8397692 100644 --- a/pkg/server/ui.go +++ b/pkg/server/ui.go @@ -4,6 +4,7 @@ import ( "encoding/json" "net/http" + "github.com/ecshreve/jepp/pkg/models" "github.com/ecshreve/jepp/pkg/utils" "github.com/gin-gonic/gin" "github.com/samsarahq/go/oops" @@ -11,19 +12,19 @@ import ( // BaseUIHandler handles the base UI route. func BaseUIHandler(c *gin.Context) { - clue, err := db.GetRandomClue() + clue, err := models.GetRandomClue() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't fetch random clue"}) return } - cat, err := db.GetCategory(clue.CategoryID) + cat, err := models.GetCategory(clue.CategoryID) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't fetch category for clue"}) return } - game, err := db.GetGame(clue.GameID) + game, err := models.GetGame(clue.GameID) if err != nil { utils.NewError(c, http.StatusBadRequest, oops.Wrapf(err, "couldn't fetch game for clue")) return @@ -31,7 +32,7 @@ func BaseUIHandler(c *gin.Context) { // TODO: validation clueJSON, _ := json.Marshal(clue) - numClues, _ := db.CountClues() + numClues, _ := models.CountClues() c.HTML(200, "base.html.tpl", gin.H{ "NumClues": numClues, diff --git a/taskfiles/docker-tasks.yml b/taskfiles/docker-tasks.yml index a4d3dee..e331932 100644 --- a/taskfiles/docker-tasks.yml +++ b/taskfiles/docker-tasks.yml @@ -9,14 +9,14 @@ tasks: run: desc: Runs the docker compose project. deps: - - docker:build + - task: build cmds: - docker compose up -d push-digital-ocean: desc: Pushes the docker image to the digital ocean container registry. deps: - - docker:build + - task: build cmds: - docker push registry.digitalocean.com/shreggie/jepp:custom diff --git a/taskfiles/experimental.yml b/taskfiles/experimental.yml new file mode 100644 index 0000000..a6d46ec --- /dev/null +++ b/taskfiles/experimental.yml @@ -0,0 +1,34 @@ +version: "3" + # dumper: + # taskfile: ./taskfiles/go-tasks.yml + # vars: {CMD_NAME: dumper} + # scraper: + # taskfile: ./taskfiles/go-tasks.yml + # vars: {CMD_NAME: scrape} + # server: + # taskfile: ./taskfiles/go-tasks.yml + # vars: {CMD_NAME: server} + genswag: + desc: Generates swagger docs for the API. + generates: + - docs/* + sources: + - cmd/server/* + - pkg/server/* + - pkg/models/* + - pkg/utils/* + cmds: + - swag fmt -d cmd/server,pkg/server,pkg/models,pkg/utils + - swag init -d cmd/server,pkg/server,pkg/models,pkg/utils + + sql:dump: + desc: WIP Dumps the database to a file. + cmds: + - mysqldump -u ${DB_USER} -p${DB_PASS} -h ${DB_HOST} -P ${DB_PORT} --skip-comments --column-statistics=0 --databases ${DB_NAME} > data/${DB_NAME}_dump.sql + - gzip data/${DB_NAME}_dump.sql + + sql:restore: + desc: WIP Restores the database from a file. + cmds: + - gunzip data/${DB_NAME}_dump.sql.gz + - mysql -u${DB_USER} -p${DB_PASS} -h ${DB_HOST} -P ${DB_PORT} < data/${DB_NAME}_dump.sql