fix
This commit is contained in:
+17
@@ -1533,6 +1533,23 @@ function allowedApiTarget(req) {
|
|||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pathname === '/api/tss/teams/search') {
|
||||||
|
const keys = [...params.keys()]
|
||||||
|
const query = params.get('q') || params.get('name') || ''
|
||||||
|
const limit = Number(params.get('limit') || 10)
|
||||||
|
if (
|
||||||
|
keys.some((key) => !['q', 'name', 'limit'].includes(key)) ||
|
||||||
|
query.length < 2 ||
|
||||||
|
query.length > MAX_TEAM_NAME_LENGTH ||
|
||||||
|
!Number.isInteger(limit) ||
|
||||||
|
limit < 1 ||
|
||||||
|
limit > 20
|
||||||
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
const teamMatch = pathname.match(/^\/api\/tss\/teams\/([^/]+)(?:\/(history|games))?$/)
|
const teamMatch = pathname.match(/^\/api\/tss\/teams\/([^/]+)(?:\/(history|games))?$/)
|
||||||
if (!teamMatch || [...params.keys()].length) return null
|
if (!teamMatch || [...params.keys()].length) return null
|
||||||
|
|
||||||
|
|||||||
+27
-4
@@ -19,6 +19,7 @@ const apiEndpoints = {
|
|||||||
teams: '/api/tss/leaderboard/teams?limit=100',
|
teams: '/api/tss/leaderboard/teams?limit=100',
|
||||||
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
|
teamsHealth: '/api/tss/leaderboard/teams?limit=1',
|
||||||
resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`,
|
resolve: (name) => `/api/tss/teams/resolve?name=${encodeURIComponent(name)}`,
|
||||||
|
searchTeams: (name) => `/api/tss/teams/search?q=${encodeURIComponent(name)}&limit=10`,
|
||||||
detail: (name) => `/api/tss/teams/${encodeURIComponent(name)}`,
|
detail: (name) => `/api/tss/teams/${encodeURIComponent(name)}`,
|
||||||
history: (name) => `/api/tss/teams/${encodeURIComponent(name)}/history`,
|
history: (name) => `/api/tss/teams/${encodeURIComponent(name)}/history`,
|
||||||
games: (name) => `/api/tss/teams/${encodeURIComponent(name)}/games`,
|
games: (name) => `/api/tss/teams/${encodeURIComponent(name)}/games`,
|
||||||
@@ -547,6 +548,7 @@ function AppContent() {
|
|||||||
const [showFloatingNav, setShowFloatingNav] = useState(() => window.scrollY > 40)
|
const [showFloatingNav, setShowFloatingNav] = useState(() => window.scrollY > 40)
|
||||||
const [teamQuery, setTeamQuery] = useState('')
|
const [teamQuery, setTeamQuery] = useState('')
|
||||||
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
|
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
|
||||||
|
const [teamSearchResults, setTeamSearchResults] = useState([])
|
||||||
const [profile, setProfile] = useState({
|
const [profile, setProfile] = useState({
|
||||||
teamName: '',
|
teamName: '',
|
||||||
detail: { status: 'idle', data: null, error: null },
|
detail: { status: 'idle', data: null, error: null },
|
||||||
@@ -733,22 +735,42 @@ function AppContent() {
|
|||||||
const query = teamQuery.trim()
|
const query = teamQuery.trim()
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
setSearchHint({ status: 'idle', name: '' })
|
setSearchHint({ status: 'idle', name: '' })
|
||||||
|
setTeamSearchResults([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
const timer = window.setTimeout(() => {
|
const timer = window.setTimeout(() => {
|
||||||
setSearchHint({ status: 'loading', name: '' })
|
setSearchHint({ status: 'loading', name: '' })
|
||||||
fetchJson(apiEndpoints.resolve(query), controller.signal)
|
fetchJson(apiEndpoints.searchTeams(query), controller.signal)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const name = data.tag_name || data.short_name || data.long_name || query
|
const results = (data.teams || data.results || [])
|
||||||
setSearchHint({ status: 'ready', name })
|
.map((team) => ({
|
||||||
|
name: bestTeamName(team),
|
||||||
|
detail: team.long_name || team.short_name || '',
|
||||||
|
aliases: [team.tag_name, team.short_name, team.long_name].filter(Boolean),
|
||||||
|
}))
|
||||||
|
.filter((team) => team.name)
|
||||||
|
.slice(0, 10)
|
||||||
|
setTeamSearchResults(results)
|
||||||
|
setSearchHint(results.length ? { status: 'ready', name: results[0].name } : { status: 'error', name: '' })
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
if (!controller.signal.aborted) {
|
if (!controller.signal.aborted) {
|
||||||
|
fetchJson(apiEndpoints.resolve(query), controller.signal)
|
||||||
|
.then((data) => {
|
||||||
|
const name = data.tag_name || data.short_name || data.long_name || ''
|
||||||
|
setTeamSearchResults(name ? [{ name, detail: data.long_name || data.short_name || '', aliases: [name] }] : [])
|
||||||
|
setSearchHint(name ? { status: 'ready', name } : { status: 'error', name: '' })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (!controller.signal.aborted) {
|
||||||
|
setTeamSearchResults([])
|
||||||
setSearchHint({ status: 'error', name: '' })
|
setSearchHint({ status: 'error', name: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}, 350)
|
}, 350)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -1060,7 +1082,7 @@ function AppContent() {
|
|||||||
}, [route.page, route.teamName])
|
}, [route.page, route.teamName])
|
||||||
|
|
||||||
const topTeamName = bestTeamName(teams[0])
|
const topTeamName = bestTeamName(teams[0])
|
||||||
const teamSuggestions = useMemo(() => {
|
const localTeamSuggestions = useMemo(() => {
|
||||||
const query = teamQuery.trim().toLowerCase()
|
const query = teamQuery.trim().toLowerCase()
|
||||||
const seen = new Set()
|
const seen = new Set()
|
||||||
|
|
||||||
@@ -1078,6 +1100,7 @@ function AppContent() {
|
|||||||
})
|
})
|
||||||
.slice(0, 10)
|
.slice(0, 10)
|
||||||
}, [teamQuery, teams])
|
}, [teamQuery, teams])
|
||||||
|
const teamSuggestions = teamSearchResults.length ? teamSearchResults : localTeamSuggestions
|
||||||
const searchPlaceholder =
|
const searchPlaceholder =
|
||||||
searchHint.status === 'ready'
|
searchHint.status === 'ready'
|
||||||
? `Found ${searchHint.name}`
|
? `Found ${searchHint.name}`
|
||||||
|
|||||||
@@ -92,6 +92,20 @@ function isAllowedApiUrl(req) {
|
|||||||
return keys.every((key) => key === 'name') && name.length >= 2 && name.length <= MAX_TEAM_NAME_LENGTH
|
return keys.every((key) => key === 'name') && name.length >= 2 && name.length <= MAX_TEAM_NAME_LENGTH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (url.pathname === '/api/tss/teams/search') {
|
||||||
|
const keys = [...params.keys()]
|
||||||
|
const query = params.get('q') || params.get('name') || ''
|
||||||
|
const limit = Number(params.get('limit') || 10)
|
||||||
|
return (
|
||||||
|
keys.every((key) => ['q', 'name', 'limit'].includes(key)) &&
|
||||||
|
query.length >= 2 &&
|
||||||
|
query.length <= MAX_TEAM_NAME_LENGTH &&
|
||||||
|
Number.isInteger(limit) &&
|
||||||
|
limit >= 1 &&
|
||||||
|
limit <= 20
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if ([...params.keys()].length) return false
|
if ([...params.keys()].length) return false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user