CLAUDE: Add team ownership to auth flow (CRIT-002)
Backend: - Add get_teams_by_owner() to SBA API client - Update /api/auth/me to return user's teams from SBA API - Add sba_current_season config setting (default: 13) Frontend: - Replace hardcoded myTeamId with computed from auth store teams - Fix isMyTurn logic to check actual team ownership - Update REFACTORING_PLAN.json Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f2ce91a239
commit
c5869f5ba2
@ -9,6 +9,7 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
from app.config import get_settings
|
from app.config import get_settings
|
||||||
from app.utils.auth import create_token, verify_token
|
from app.utils.auth import create_token, verify_token
|
||||||
|
from app.services.sba_api_client import sba_api_client
|
||||||
from app.utils.cookies import (
|
from app.utils.cookies import (
|
||||||
ACCESS_TOKEN_COOKIE,
|
ACCESS_TOKEN_COOKIE,
|
||||||
REFRESH_TOKEN_COOKIE,
|
REFRESH_TOKEN_COOKIE,
|
||||||
@ -484,10 +485,22 @@ async def get_current_user_info(
|
|||||||
discriminator="0", # Discord removed discriminators
|
discriminator="0", # Discord removed discriminators
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: Load user's teams from database
|
# Load user's teams from SBA API
|
||||||
teams = []
|
discord_id = payload["discord_id"]
|
||||||
|
sba_teams = await sba_api_client.get_teams_by_owner(
|
||||||
|
discord_id, season=settings.sba_current_season
|
||||||
|
)
|
||||||
|
teams = [
|
||||||
|
{
|
||||||
|
"id": team["id"],
|
||||||
|
"name": team.get("lname", team.get("sname", "Unknown")),
|
||||||
|
"owner_id": discord_id,
|
||||||
|
"league_id": "sba",
|
||||||
|
}
|
||||||
|
for team in sba_teams
|
||||||
|
]
|
||||||
|
|
||||||
logger.info(f"User info retrieved for {user.username}")
|
logger.info(f"User info retrieved for {user.username}, {len(teams)} teams")
|
||||||
|
|
||||||
return UserInfoResponse(user=user, teams=teams)
|
return UserInfoResponse(user=user, teams=teams)
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class Settings(BaseSettings):
|
|||||||
# League APIs
|
# League APIs
|
||||||
sba_api_url: str
|
sba_api_url: str
|
||||||
sba_api_key: str
|
sba_api_key: str
|
||||||
|
sba_current_season: int = 13 # Current SBA season number
|
||||||
pd_api_url: str
|
pd_api_url: str
|
||||||
pd_api_key: str
|
pd_api_key: str
|
||||||
|
|
||||||
|
|||||||
@ -98,6 +98,43 @@ class SbaApiClient:
|
|||||||
logger.error(f"Unexpected error fetching teams: {e}")
|
logger.error(f"Unexpected error fetching teams: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
async def get_teams_by_owner(self, discord_id: str, season: int) -> list[dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Fetch teams owned by a specific Discord user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
discord_id: Discord user ID (gmid or gmid2)
|
||||||
|
season: Season number
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of team dictionaries owned by this user
|
||||||
|
|
||||||
|
Example:
|
||||||
|
teams = await client.get_teams_by_owner("485217045408120833", season=13)
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/teams"
|
||||||
|
params = {"season": season, "owner_id": discord_id}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||||
|
response = await client.get(
|
||||||
|
url, headers=self._get_headers(), params=params
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
teams = data.get("teams", [])
|
||||||
|
|
||||||
|
logger.info(f"Found {len(teams)} teams for owner {discord_id[:8]}...")
|
||||||
|
return teams
|
||||||
|
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"Failed to fetch teams for owner {discord_id[:8]}...: {e}")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error fetching teams by owner: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
async def get_team_by_id(self, team_id: int, season: int = 3) -> dict[str, Any] | None:
|
async def get_team_by_id(self, team_id: int, season: int = 3) -> dict[str, Any] | None:
|
||||||
"""
|
"""
|
||||||
Get a single team by ID, using cache when available.
|
Get a single team by ID, using cache when available.
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"totalEstimatedHours": 52.75,
|
"totalEstimatedHours": 52.75,
|
||||||
"criticalBlockersHours": 6.5,
|
"criticalBlockersHours": 6.5,
|
||||||
"totalTasks": 47,
|
"totalTasks": 47,
|
||||||
"completedTasks": 1
|
"completedTasks": 3
|
||||||
},
|
},
|
||||||
"categories": {
|
"categories": {
|
||||||
"critical": "Must fix before any production use",
|
"critical": "Must fix before any production use",
|
||||||
|
|||||||
@ -403,7 +403,22 @@ const currentDecisionPrompt = computed(() => gameStore.currentDecisionPrompt)
|
|||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const connectionStatus = ref<'connecting' | 'connected' | 'disconnected'>('connecting')
|
const connectionStatus = ref<'connecting' | 'connected' | 'disconnected'>('connecting')
|
||||||
const showSubstitutions = ref(false)
|
const showSubstitutions = ref(false)
|
||||||
const myTeamId = ref<number | null>(null) // TODO: Get from auth/game state
|
|
||||||
|
// Determine which team (if any) the current user owns in this game
|
||||||
|
const myTeamId = computed(() => {
|
||||||
|
if (!gameState.value) return null
|
||||||
|
|
||||||
|
const userTeamIds = authStore.userTeams.map(t => t.id)
|
||||||
|
|
||||||
|
if (userTeamIds.includes(gameState.value.home_team_id)) {
|
||||||
|
return gameState.value.home_team_id
|
||||||
|
}
|
||||||
|
if (userTeamIds.includes(gameState.value.away_team_id)) {
|
||||||
|
return gameState.value.away_team_id
|
||||||
|
}
|
||||||
|
|
||||||
|
return null // Spectator - doesn't own either team
|
||||||
|
})
|
||||||
|
|
||||||
// Dynamic ScoreBoard height tracking
|
// Dynamic ScoreBoard height tracking
|
||||||
const scoreBoardRef = ref<HTMLElement | null>(null)
|
const scoreBoardRef = ref<HTMLElement | null>(null)
|
||||||
@ -435,9 +450,28 @@ const currentTeam = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isMyTurn = computed(() => {
|
const isMyTurn = computed(() => {
|
||||||
// TODO: Implement actual team ownership logic
|
if (!myTeamId.value || !gameState.value) return false
|
||||||
// For now, assume it's always the player's turn for testing
|
|
||||||
return true
|
// Determine which team needs to act based on decision phase
|
||||||
|
if (needsDefensiveDecision.value) {
|
||||||
|
// Fielding team makes defensive decisions
|
||||||
|
// Top of inning: home fields, Bottom: away fields
|
||||||
|
const fieldingTeamId = gameState.value.half === 'top'
|
||||||
|
? gameState.value.home_team_id
|
||||||
|
: gameState.value.away_team_id
|
||||||
|
return myTeamId.value === fieldingTeamId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsOffensiveDecision.value) {
|
||||||
|
// Batting team makes offensive decisions
|
||||||
|
// Top of inning: away bats, Bottom: home bats
|
||||||
|
const battingTeamId = gameState.value.half === 'top'
|
||||||
|
? gameState.value.away_team_id
|
||||||
|
: gameState.value.home_team_id
|
||||||
|
return myTeamId.value === battingTeamId
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
const decisionPhase = computed(() => {
|
const decisionPhase = computed(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user