refactor: extract session helpers for player identity resolution
Add GetPlayerID, GetUserID, GetNickname to the sessions package. Remove the inline player-ID-from-session pattern duplicated across every handler in c4game and snakegame, and the local getPlayerID helper in snakegame.
This commit is contained in:
@@ -18,10 +18,10 @@ import (
|
|||||||
"github.com/ryanhamamura/c4/features/c4game/components"
|
"github.com/ryanhamamura/c4/features/c4game/components"
|
||||||
"github.com/ryanhamamura/c4/features/c4game/pages"
|
"github.com/ryanhamamura/c4/features/c4game/pages"
|
||||||
"github.com/ryanhamamura/c4/game"
|
"github.com/ryanhamamura/c4/game"
|
||||||
"github.com/ryanhamamura/c4/player"
|
"github.com/ryanhamamura/c4/sessions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleGamePage(store *game.GameStore, sessions *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
func HandleGamePage(store *game.GameStore, sm *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
@@ -31,29 +31,20 @@ func HandleGamePage(store *game.GameStore, sessions *scs.SessionManager, queries
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
if playerID == "" {
|
userID := sessions.GetUserID(sm, r)
|
||||||
playerID = player.ID(player.GenerateID(8))
|
nickname := sessions.GetNickname(sm, r)
|
||||||
sessions.Put(r.Context(), "player_id", string(playerID))
|
|
||||||
}
|
|
||||||
|
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
|
||||||
if userID != "" {
|
|
||||||
playerID = player.ID(userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
nickname := sessions.GetString(r.Context(), "nickname")
|
|
||||||
|
|
||||||
// Auto-join if player has a nickname but isn't in the game yet
|
// Auto-join if player has a nickname but isn't in the game yet
|
||||||
if nickname != "" && gi.GetPlayerColor(playerID) == 0 {
|
if nickname != "" && gi.GetPlayerColor(playerID) == 0 {
|
||||||
player := &game.Player{
|
p := &game.Player{
|
||||||
ID: playerID,
|
ID: playerID,
|
||||||
Nickname: nickname,
|
Nickname: nickname,
|
||||||
}
|
}
|
||||||
if userID != "" {
|
if userID != "" {
|
||||||
player.UserID = &userID
|
p.UserID = &userID
|
||||||
}
|
}
|
||||||
gi.Join(&game.PlayerSession{Player: player})
|
gi.Join(&game.PlayerSession{Player: p})
|
||||||
}
|
}
|
||||||
|
|
||||||
myColor := gi.GetPlayerColor(playerID)
|
myColor := gi.GetPlayerColor(playerID)
|
||||||
@@ -86,7 +77,7 @@ func HandleGamePage(store *game.GameStore, sessions *scs.SessionManager, queries
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sm *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
@@ -96,12 +87,7 @@ func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.Sessio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
|
||||||
if userID != "" {
|
|
||||||
playerID = player.ID(userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
myColor := gi.GetPlayerColor(playerID)
|
myColor := gi.GetPlayerColor(playerID)
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r, datastar.WithCompression(
|
sse := datastar.NewSSE(w, r, datastar.WithCompression(
|
||||||
@@ -169,7 +155,7 @@ func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.Sessio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleDropPiece(store *game.GameStore, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleDropPiece(store *game.GameStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
@@ -186,12 +172,7 @@ func HandleDropPiece(store *game.GameStore, sessions *scs.SessionManager) http.H
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
|
||||||
if userID != "" {
|
|
||||||
playerID = player.ID(userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
myColor := gi.GetPlayerColor(playerID)
|
myColor := gi.GetPlayerColor(playerID)
|
||||||
if myColor == 0 {
|
if myColor == 0 {
|
||||||
http.Error(w, "not in game", http.StatusForbidden)
|
http.Error(w, "not in game", http.StatusForbidden)
|
||||||
@@ -206,7 +187,7 @@ func HandleDropPiece(store *game.GameStore, sessions *scs.SessionManager) http.H
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSendChat(store *game.GameStore, nc *nats.Conn, sessions *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
func HandleSendChat(store *game.GameStore, nc *nats.Conn, sm *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
@@ -230,12 +211,7 @@ func HandleSendChat(store *game.GameStore, nc *nats.Conn, sessions *scs.SessionM
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
|
||||||
if userID != "" {
|
|
||||||
playerID = player.ID(userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
myColor := gi.GetPlayerColor(playerID)
|
myColor := gi.GetPlayerColor(playerID)
|
||||||
if myColor == 0 {
|
if myColor == 0 {
|
||||||
datastar.NewSSE(w, r)
|
datastar.NewSSE(w, r)
|
||||||
@@ -272,7 +248,7 @@ func HandleSendChat(store *game.GameStore, nc *nats.Conn, sessions *scs.SessionM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSetNickname(store *game.GameStore, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleSetNickname(store *game.GameStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
@@ -297,23 +273,20 @@ func HandleSetNickname(store *game.GameStore, sessions *scs.SessionManager) http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sessions.Put(r.Context(), "nickname", signals.Nickname)
|
sm.Put(r.Context(), "nickname", signals.Nickname)
|
||||||
|
|
||||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
userID := sessions.GetUserID(sm, r)
|
||||||
if userID != "" {
|
|
||||||
playerID = player.ID(userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if gi.GetPlayerColor(playerID) == 0 {
|
if gi.GetPlayerColor(playerID) == 0 {
|
||||||
player := &game.Player{
|
p := &game.Player{
|
||||||
ID: playerID,
|
ID: playerID,
|
||||||
Nickname: signals.Nickname,
|
Nickname: signals.Nickname,
|
||||||
}
|
}
|
||||||
if userID != "" {
|
if userID != "" {
|
||||||
player.UserID = &userID
|
p.UserID = &userID
|
||||||
}
|
}
|
||||||
gi.Join(&game.PlayerSession{Player: player})
|
gi.Join(&game.PlayerSession{Player: p})
|
||||||
}
|
}
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r)
|
||||||
@@ -321,7 +294,7 @@ func HandleSetNickname(store *game.GameStore, sessions *scs.SessionManager) http
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleRematch(store *game.GameStore, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleRematch(store *game.GameStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
|||||||
@@ -13,24 +13,11 @@ import (
|
|||||||
|
|
||||||
"github.com/ryanhamamura/c4/features/snakegame/components"
|
"github.com/ryanhamamura/c4/features/snakegame/components"
|
||||||
"github.com/ryanhamamura/c4/features/snakegame/pages"
|
"github.com/ryanhamamura/c4/features/snakegame/pages"
|
||||||
"github.com/ryanhamamura/c4/player"
|
"github.com/ryanhamamura/c4/sessions"
|
||||||
"github.com/ryanhamamura/c4/snake"
|
"github.com/ryanhamamura/c4/snake"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPlayerID(sessions *scs.SessionManager, r *http.Request) player.ID {
|
func HandleSnakePage(snakeStore *snake.SnakeStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
pid := sessions.GetString(r.Context(), "player_id")
|
|
||||||
if pid == "" {
|
|
||||||
pid = player.GenerateID(8)
|
|
||||||
sessions.Put(r.Context(), "player_id", pid)
|
|
||||||
}
|
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
|
||||||
if userID != "" {
|
|
||||||
return player.ID(userID)
|
|
||||||
}
|
|
||||||
return player.ID(pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleSnakePage(snakeStore *snake.SnakeStore, sessions *scs.SessionManager) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
si, ok := snakeStore.Get(gameID)
|
si, ok := snakeStore.Get(gameID)
|
||||||
@@ -39,9 +26,9 @@ func HandleSnakePage(snakeStore *snake.SnakeStore, sessions *scs.SessionManager)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := getPlayerID(sessions, r)
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
nickname := sessions.GetString(r.Context(), "nickname")
|
nickname := sessions.GetNickname(sm, r)
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
userID := sessions.GetUserID(sm, r)
|
||||||
|
|
||||||
// Auto-join if nickname exists and not already in game
|
// Auto-join if nickname exists and not already in game
|
||||||
if nickname != "" && si.GetPlayerSlot(playerID) < 0 {
|
if nickname != "" && si.GetPlayerSlot(playerID) < 0 {
|
||||||
@@ -79,7 +66,7 @@ func HandleSnakePage(snakeStore *snake.SnakeStore, sessions *scs.SessionManager)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
si, ok := snakeStore.Get(gameID)
|
si, ok := snakeStore.Get(gameID)
|
||||||
@@ -88,7 +75,7 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *sc
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := getPlayerID(sessions, r)
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
mySlot := si.GetPlayerSlot(playerID)
|
mySlot := si.GetPlayerSlot(playerID)
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r, datastar.WithCompression(
|
sse := datastar.NewSSE(w, r, datastar.WithCompression(
|
||||||
@@ -187,7 +174,7 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *sc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSetDirection(snakeStore *snake.SnakeStore, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleSetDirection(snakeStore *snake.SnakeStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
si, ok := snakeStore.Get(gameID)
|
si, ok := snakeStore.Get(gameID)
|
||||||
@@ -196,7 +183,7 @@ func HandleSetDirection(snakeStore *snake.SnakeStore, sessions *scs.SessionManag
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := getPlayerID(sessions, r)
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
slot := si.GetPlayerSlot(playerID)
|
slot := si.GetPlayerSlot(playerID)
|
||||||
if slot < 0 {
|
if slot < 0 {
|
||||||
http.Error(w, "not in game", http.StatusForbidden)
|
http.Error(w, "not in game", http.StatusForbidden)
|
||||||
@@ -219,7 +206,7 @@ type chatSignals struct {
|
|||||||
ChatMsg string `json:"chatMsg"`
|
ChatMsg string `json:"chatMsg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSendChat(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleSendChat(snakeStore *snake.SnakeStore, nc *nats.Conn, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
si, ok := snakeStore.Get(gameID)
|
si, ok := snakeStore.Get(gameID)
|
||||||
@@ -238,7 +225,7 @@ func HandleSendChat(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *scs.S
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID := getPlayerID(sessions, r)
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
slot := si.GetPlayerSlot(playerID)
|
slot := si.GetPlayerSlot(playerID)
|
||||||
if slot < 0 {
|
if slot < 0 {
|
||||||
http.Error(w, "not in game", http.StatusForbidden)
|
http.Error(w, "not in game", http.StatusForbidden)
|
||||||
@@ -266,7 +253,7 @@ type nicknameSignals struct {
|
|||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSetNickname(snakeStore *snake.SnakeStore, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleSetNickname(snakeStore *snake.SnakeStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
si, ok := snakeStore.Get(gameID)
|
si, ok := snakeStore.Get(gameID)
|
||||||
@@ -285,10 +272,10 @@ func HandleSetNickname(snakeStore *snake.SnakeStore, sessions *scs.SessionManage
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sessions.Put(r.Context(), "nickname", signals.Nickname)
|
sm.Put(r.Context(), "nickname", signals.Nickname)
|
||||||
|
|
||||||
playerID := getPlayerID(sessions, r)
|
playerID := sessions.GetPlayerID(sm, r)
|
||||||
userID := sessions.GetString(r.Context(), "user_id")
|
userID := sessions.GetUserID(sm, r)
|
||||||
|
|
||||||
if si.GetPlayerSlot(playerID) < 0 {
|
if si.GetPlayerSlot(playerID) < 0 {
|
||||||
player := &snake.Player{
|
player := &snake.Player{
|
||||||
@@ -306,7 +293,7 @@ func HandleSetNickname(snakeStore *snake.SnakeStore, sessions *scs.SessionManage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleRematch(snakeStore *snake.SnakeStore, sessions *scs.SessionManager) http.HandlerFunc {
|
func HandleRematch(snakeStore *snake.SnakeStore, sm *scs.SessionManager) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
gameID := chi.URLParam(r, "id")
|
gameID := chi.URLParam(r, "id")
|
||||||
si, ok := snakeStore.Get(gameID)
|
si, ok := snakeStore.Get(gameID)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// Package sessions configures the SCS session manager backed by SQLite.
|
// Package sessions configures the SCS session manager and provides
|
||||||
|
// helpers for resolving player identity from the session.
|
||||||
package sessions
|
package sessions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,6 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ryanhamamura/c4/player"
|
||||||
|
|
||||||
"github.com/alexedwards/scs/sqlite3store"
|
"github.com/alexedwards/scs/sqlite3store"
|
||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
)
|
)
|
||||||
@@ -30,3 +33,28 @@ func SetupSessionManager(db *sql.DB) (*scs.SessionManager, func()) {
|
|||||||
|
|
||||||
return sessionManager, cleanup
|
return sessionManager, cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPlayerID returns the current player's identity from the session.
|
||||||
|
// Authenticated users get their user UUID; guests get a random ID that
|
||||||
|
// is generated and persisted on first access.
|
||||||
|
func GetPlayerID(sm *scs.SessionManager, r *http.Request) player.ID {
|
||||||
|
pid := sm.GetString(r.Context(), "player_id")
|
||||||
|
if pid == "" {
|
||||||
|
pid = player.GenerateID(8)
|
||||||
|
sm.Put(r.Context(), "player_id", pid)
|
||||||
|
}
|
||||||
|
if userID := sm.GetString(r.Context(), "user_id"); userID != "" {
|
||||||
|
return player.ID(userID)
|
||||||
|
}
|
||||||
|
return player.ID(pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserID returns the authenticated user's UUID, or empty string for guests.
|
||||||
|
func GetUserID(sm *scs.SessionManager, r *http.Request) string {
|
||||||
|
return sm.GetString(r.Context(), "user_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNickname returns the player's display name from the session.
|
||||||
|
func GetNickname(sm *scs.SessionManager, r *http.Request) string {
|
||||||
|
return sm.GetString(r.Context(), "nickname")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user