package handlers import ( "context" "fmt" "net/http" "strconv" "git.haelnorr.com/h/golib/hws" "git.haelnorr.com/h/oslstats/internal/db" "git.haelnorr.com/h/oslstats/internal/throw" playersview "git.haelnorr.com/h/oslstats/internal/view/playersview" "github.com/pkg/errors" "github.com/uptrace/bun" ) // ProfileRedirect redirects the authenticated user to their own player page. func ProfileRedirect( s *hws.Server, ) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := db.CurrentUser(r.Context()) if user == nil { throw.Unauthorized(s, w, r, "You must be logged in to view your profile", errors.New("user not authenticated")) return } if user.Player == nil { throw.InternalServiceError(s, w, r, "Player profile not found", errors.New("user has no linked player")) return } http.Redirect(w, r, fmt.Sprintf("/players/%d", user.Player.ID), http.StatusSeeOther) }) } // resolvePlayerAndOwner is a helper that resolves the player from the URL path // and determines if the current user is the owner of the player. // Returns false from the outer handler if resolution failed (404 already thrown). func resolvePlayerAndOwner( s *hws.Server, conn *db.DB, w http.ResponseWriter, r *http.Request, ) (player *db.Player, isOwner bool, ok bool) { playerIDStr := r.PathValue("player_id") playerID, err := strconv.Atoi(playerIDStr) if err != nil { throw.NotFound(s, w, r, r.URL.Path) return nil, false, false } if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error player, err = db.GetPlayer(ctx, tx, playerID) if err != nil { if db.IsBadRequest(err) { throw.NotFound(s, w, r, r.URL.Path) return false, nil } return false, errors.Wrap(err, "db.GetPlayer") } user := db.CurrentUser(ctx) if user != nil && player.UserID != nil && *player.UserID == user.ID { isOwner = true } return true, nil }); !ok { return nil, false, false } // If player has no SlapID and viewer is not the owner, show 404 if player.SlapID == nil && !isOwner { throw.NotFound(s, w, r, r.URL.Path) return nil, false, false } return player, isOwner, true } // PlayerViewStats renders the player profile page with the stats tab active. // GET renders the full page layout. POST renders just the tab content. func PlayerViewStats( s *hws.Server, conn *db.DB, ) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { player, isOwner, ok := resolvePlayerAndOwner(s, conn, w, r) if !ok { return } var stats *db.PlayerAllTimeStats var seasons []*db.Season var teams []*db.Team if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error stats, err = db.GetPlayerAllTimeStats(ctx, tx, player.ID) if err != nil { return false, errors.Wrap(err, "db.GetPlayerAllTimeStats") } seasons, err = db.GetPlayerSeasonsList(ctx, tx, player.ID) if err != nil { return false, errors.Wrap(err, "db.GetPlayerSeasonsList") } teams, err = db.GetPlayerTeamsList(ctx, tx, player.ID) if err != nil { return false, errors.Wrap(err, "db.GetPlayerTeamsList") } return true, nil }); !ok { return } if r.Method == "GET" { renderSafely(playersview.PlayerStatsPage(player, isOwner, stats, seasons, teams), s, r, w) } else { renderSafely(playersview.PlayerStatsTab(player, stats, seasons, teams, "", 0), s, r, w) } }) } // PlayerViewTeams renders the teams tab of the player profile page. func PlayerViewTeams( s *hws.Server, conn *db.DB, ) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { player, isOwner, ok := resolvePlayerAndOwner(s, conn, w, r) if !ok { return } var teamInfos []*db.PlayerTeamInfo if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error teamInfos, err = db.GetPlayerTeams(ctx, tx, player.ID) if err != nil { return false, errors.Wrap(err, "db.GetPlayerTeams") } return true, nil }); !ok { return } if r.Method == "GET" { renderSafely(playersview.PlayerTeamsPage(player, isOwner, teamInfos), s, r, w) } else { renderSafely(playersview.PlayerTeamsTab(teamInfos), s, r, w) } }) } // PlayerViewSeasons renders the seasons tab of the player profile page. func PlayerViewSeasons( s *hws.Server, conn *db.DB, ) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { player, isOwner, ok := resolvePlayerAndOwner(s, conn, w, r) if !ok { return } var seasonInfos []*db.PlayerSeasonInfo if ok := conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error seasonInfos, err = db.GetPlayerSeasons(ctx, tx, player.ID) if err != nil { return false, errors.Wrap(err, "db.GetPlayerSeasons") } return true, nil }); !ok { return } if r.Method == "GET" { renderSafely(playersview.PlayerSeasonsPage(player, isOwner, seasonInfos), s, r, w) } else { renderSafely(playersview.PlayerSeasonsTab(seasonInfos), s, r, w) } }) }