mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-07 08:26:00 +00:00
fix(web): avoid CSP errors when downloading skills
This commit is contained in:
parent
316a86628c
commit
330f64cedd
2 changed files with 20 additions and 39 deletions
|
|
@ -37,9 +37,8 @@ export { ApiError }
|
|||
|
||||
export const WEB_API_PREFIX = '/api/web'
|
||||
|
||||
export type DownloadedFile = {
|
||||
blob: Blob
|
||||
fileName?: string
|
||||
export type SkillDownloadRequest = {
|
||||
url: string
|
||||
}
|
||||
|
||||
type RuntimeConfig = {
|
||||
|
|
@ -278,20 +277,6 @@ function ensureTrailingSlash(value: string): string {
|
|||
return value.endsWith('/') ? value : `${value}/`
|
||||
}
|
||||
|
||||
function parseDownloadFileName(contentDisposition: string | null): string | undefined {
|
||||
if (!contentDisposition) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i)
|
||||
if (utf8Match) {
|
||||
return decodeURIComponent(utf8Match[1])
|
||||
}
|
||||
|
||||
const basicMatch = contentDisposition.match(/filename="?([^";]+)"?/i)
|
||||
return basicMatch?.[1]
|
||||
}
|
||||
|
||||
export async function getCurrentUser(): Promise<User | null> {
|
||||
try {
|
||||
const user = await unwrap<User>(client.GET('/api/v1/auth/me', {
|
||||
|
|
@ -442,23 +427,25 @@ export const accountApi = {
|
|||
}
|
||||
|
||||
export const skillDownloadApi = {
|
||||
async downloadVersion(namespace: string, slug: string, version: string): Promise<DownloadedFile> {
|
||||
async downloadVersion(namespace: string, slug: string, version: string): Promise<SkillDownloadRequest> {
|
||||
const cleanNamespace = namespace.startsWith('@') ? namespace.slice(1) : namespace
|
||||
const response = await fetch(
|
||||
withBaseUrl(`${WEB_API_PREFIX}/skills/${cleanNamespace}/${slug}/versions/${version}/download`),
|
||||
{
|
||||
headers: withRequestHeaders(),
|
||||
},
|
||||
)
|
||||
const requestUrl = withBaseUrl(`${WEB_API_PREFIX}/skills/${cleanNamespace}/${slug}/versions/${version}/download`)
|
||||
const url = typeof requestUrl === 'string' ? requestUrl : requestUrl.toString()
|
||||
|
||||
if (!response.ok) {
|
||||
const response = await fetch(requestUrl, {
|
||||
headers: withRequestHeaders(),
|
||||
redirect: 'manual',
|
||||
})
|
||||
|
||||
if (response.type === 'opaqueredirect') {
|
||||
return { url }
|
||||
}
|
||||
|
||||
if (!response.ok && response.status !== 0 && response.status !== 302) {
|
||||
throw new ApiError(`HTTP ${response.status}`, response.status)
|
||||
}
|
||||
|
||||
return {
|
||||
blob: await response.blob(),
|
||||
fileName: parseDownloadFileName(response.headers.get('content-disposition')),
|
||||
}
|
||||
return { url }
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -137,15 +137,12 @@ export function SkillDetailPage() {
|
|||
const submitPromotionMutation = useSubmitPromotion()
|
||||
const reportMutation = useSubmitSkillReport(namespace, slug)
|
||||
|
||||
const triggerBrowserDownload = (blob: Blob, fileName: string) => {
|
||||
const objectUrl = window.URL.createObjectURL(blob)
|
||||
const triggerBrowserDownload = (url: string) => {
|
||||
const link = document.createElement('a')
|
||||
link.href = objectUrl
|
||||
link.download = fileName
|
||||
link.href = url
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
link.remove()
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(objectUrl), 0)
|
||||
}
|
||||
|
||||
const handleDownload = async () => {
|
||||
|
|
@ -158,11 +155,8 @@ export function SkillDetailPage() {
|
|||
}
|
||||
|
||||
try {
|
||||
const downloadedFile = await skillDownloadApi.downloadVersion(namespace, slug, selectedVersionEntry.version)
|
||||
triggerBrowserDownload(
|
||||
downloadedFile.blob,
|
||||
downloadedFile.fileName ?? `${slug}-${selectedVersionEntry.version}.zip`,
|
||||
)
|
||||
const downloadRequest = await skillDownloadApi.downloadVersion(namespace, slug, selectedVersionEntry.version)
|
||||
triggerBrowserDownload(downloadRequest.url)
|
||||
incrementSkillDownloadCount(queryClient, { namespace, slug })
|
||||
queryClient.invalidateQueries({ queryKey: ['skills', namespace, slug] })
|
||||
queryClient.invalidateQueries({ queryKey: ['skills', 'my'] })
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue