refactor: adopt portigo infrastructure patterns

Add config package with build-tag-switched dev/prod environments,
structured logging via zerolog, Taskfile for dev workflow, golangci-lint
config, testutil package, and improved DB setup with proper SQLite
pragmas and cleanup. Rename sqlc output package from gen to repository.

Switch to allowlist .gitignore, Alpine+UPX+scratch Dockerfile, and
CI pipeline with test/lint gates before deploy.
This commit is contained in:
Ryan Hamamura
2026-03-02 11:48:47 -10:00
parent 6d4f3eb821
commit 2df20c2840
27 changed files with 694 additions and 143 deletions

View File

@@ -0,0 +1,278 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: snake_games.sql
package repository
import (
"context"
"database/sql"
)
const createSnakeGame = `-- name: CreateSnakeGame :one
INSERT INTO games (id, board, current_turn, status, game_type, grid_width, grid_height, max_players, game_mode, snake_speed)
VALUES (?, ?, 0, ?, 'snake', ?, ?, 8, ?, ?)
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed
`
type CreateSnakeGameParams struct {
ID string
Board string
Status int64
GridWidth sql.NullInt64
GridHeight sql.NullInt64
GameMode int64
SnakeSpeed int64
}
func (q *Queries) CreateSnakeGame(ctx context.Context, arg CreateSnakeGameParams) (Game, error) {
row := q.db.QueryRowContext(ctx, createSnakeGame,
arg.ID,
arg.Board,
arg.Status,
arg.GridWidth,
arg.GridHeight,
arg.GameMode,
arg.SnakeSpeed,
)
var i Game
err := row.Scan(
&i.ID,
&i.Board,
&i.CurrentTurn,
&i.Status,
&i.WinnerUserID,
&i.WinningCells,
&i.CreatedAt,
&i.UpdatedAt,
&i.RematchGameID,
&i.GameType,
&i.GridWidth,
&i.GridHeight,
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
const createSnakePlayer = `-- name: CreateSnakePlayer :exec
INSERT INTO game_players (game_id, user_id, guest_player_id, nickname, color, slot)
VALUES (?, ?, ?, ?, ?, ?)
`
type CreateSnakePlayerParams struct {
GameID string
UserID sql.NullString
GuestPlayerID sql.NullString
Nickname string
Color int64
Slot int64
}
func (q *Queries) CreateSnakePlayer(ctx context.Context, arg CreateSnakePlayerParams) error {
_, err := q.db.ExecContext(ctx, createSnakePlayer,
arg.GameID,
arg.UserID,
arg.GuestPlayerID,
arg.Nickname,
arg.Color,
arg.Slot,
)
return err
}
const deleteSnakeGame = `-- name: DeleteSnakeGame :exec
DELETE FROM games WHERE id = ? AND game_type = 'snake'
`
func (q *Queries) DeleteSnakeGame(ctx context.Context, id string) error {
_, err := q.db.ExecContext(ctx, deleteSnakeGame, id)
return err
}
const getActiveSnakeGames = `-- name: GetActiveSnakeGames :many
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE game_type = 'snake' AND status < 2 AND game_mode = 0
`
func (q *Queries) GetActiveSnakeGames(ctx context.Context) ([]Game, error) {
rows, err := q.db.QueryContext(ctx, getActiveSnakeGames)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Game
for rows.Next() {
var i Game
if err := rows.Scan(
&i.ID,
&i.Board,
&i.CurrentTurn,
&i.Status,
&i.WinnerUserID,
&i.WinningCells,
&i.CreatedAt,
&i.UpdatedAt,
&i.RematchGameID,
&i.GameType,
&i.GridWidth,
&i.GridHeight,
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getSnakeGame = `-- name: GetSnakeGame :one
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE id = ? AND game_type = 'snake'
`
func (q *Queries) GetSnakeGame(ctx context.Context, id string) (Game, error) {
row := q.db.QueryRowContext(ctx, getSnakeGame, id)
var i Game
err := row.Scan(
&i.ID,
&i.Board,
&i.CurrentTurn,
&i.Status,
&i.WinnerUserID,
&i.WinningCells,
&i.CreatedAt,
&i.UpdatedAt,
&i.RematchGameID,
&i.GameType,
&i.GridWidth,
&i.GridHeight,
&i.MaxPlayers,
&i.GameMode,
&i.Score,
&i.SnakeSpeed,
)
return i, err
}
const getSnakePlayers = `-- name: GetSnakePlayers :many
SELECT game_id, user_id, guest_player_id, nickname, color, slot, created_at FROM game_players WHERE game_id = ? ORDER BY slot
`
func (q *Queries) GetSnakePlayers(ctx context.Context, gameID string) ([]GamePlayer, error) {
rows, err := q.db.QueryContext(ctx, getSnakePlayers, gameID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GamePlayer
for rows.Next() {
var i GamePlayer
if err := rows.Scan(
&i.GameID,
&i.UserID,
&i.GuestPlayerID,
&i.Nickname,
&i.Color,
&i.Slot,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUserActiveSnakeGames = `-- name: GetUserActiveSnakeGames :many
SELECT
g.id,
g.status,
g.grid_width,
g.grid_height,
g.updated_at
FROM games g
JOIN game_players gp_user ON g.id = gp_user.game_id AND gp_user.user_id = ?
WHERE g.game_type = 'snake' AND g.status < 3
ORDER BY g.updated_at DESC
`
type GetUserActiveSnakeGamesRow struct {
ID string
Status int64
GridWidth sql.NullInt64
GridHeight sql.NullInt64
UpdatedAt sql.NullTime
}
func (q *Queries) GetUserActiveSnakeGames(ctx context.Context, userID sql.NullString) ([]GetUserActiveSnakeGamesRow, error) {
rows, err := q.db.QueryContext(ctx, getUserActiveSnakeGames, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserActiveSnakeGamesRow
for rows.Next() {
var i GetUserActiveSnakeGamesRow
if err := rows.Scan(
&i.ID,
&i.Status,
&i.GridWidth,
&i.GridHeight,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateSnakeGame = `-- name: UpdateSnakeGame :exec
UPDATE games
SET board = ?, status = ?, winner_user_id = ?, rematch_game_id = ?, score = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND game_type = 'snake'
`
type UpdateSnakeGameParams struct {
Board string
Status int64
WinnerUserID sql.NullString
RematchGameID sql.NullString
Score int64
ID string
}
func (q *Queries) UpdateSnakeGame(ctx context.Context, arg UpdateSnakeGameParams) error {
_, err := q.db.ExecContext(ctx, updateSnakeGame,
arg.Board,
arg.Status,
arg.WinnerUserID,
arg.RematchGameID,
arg.Score,
arg.ID,
)
return err
}