diff --git a/deploy/web/default.conf b/deploy/web/default.conf index 1e7cb26..6da25f6 100644 --- a/deploy/web/default.conf +++ b/deploy/web/default.conf @@ -17,6 +17,12 @@ server { add_header Cache-Control "public, immutable"; } + # 推广素材:文件必须真实存在,禁止落到 SPA index.html(否则前端误判「静态存在」、视频拿到 HTML) + location ^~ /promotion/ { + try_files $uri =404; + add_header Cache-Control "public, max-age=86400"; + } + location = / { try_files /index.html =404; } diff --git a/nginx/web.conf b/nginx/web.conf index 3fd038b..f5ffd8c 100644 --- a/nginx/web.conf +++ b/nginx/web.conf @@ -15,6 +15,11 @@ server { add_header Cache-Control "public, immutable"; } + location ^~ /promotion/ { + try_files $uri =404; + add_header Cache-Control "public, max-age=86400"; + } + location = / { try_files /index.html =404; } diff --git a/web/src/data/promotionVideos.js b/web/src/data/promotionVideos.js index 537f0aa..55cbcb7 100644 --- a/web/src/data/promotionVideos.js +++ b/web/src/data/promotionVideos.js @@ -44,8 +44,20 @@ export const PROMOTION_VIDEOS_BASE = [ } ] +function responseLooksLikeSpaHtml(res) { + const ct = (res.headers.get('content-type') || '').toLowerCase() + return ct.includes('text/html') +} + +function responseIsUsableAsset(res) { + if (res.ok) return !responseLooksLikeSpaHtml(res) + return res.status === 206 +} + /** - * 检测同域静态 /promotion/ 文件是否可访问(优先 HEAD,405 时用 Range GET) + * 检测同域静态 /promotion/ 文件是否真实存在。 + * - 需配合 Nginx:`location ^~ /promotion/ { try_files $uri =404; }`,避免缺失时返回 index.html 误判为 200。 + * - HEAD 非 2xx 或疑似 SPA 回退时,再用 Range GET 探测(部分环境对 .mov 的 HEAD 不友好)。 * @param {string} url * @returns {Promise} */ @@ -57,18 +69,18 @@ export async function promotionStaticUrlExists(url) { credentials: 'same-origin', cache: 'default' }) - if (head.ok) return true - if (head.status === 405) { - const r = await fetch(url, { - method: 'GET', - headers: { Range: 'bytes=0-0' }, - mode: 'same-origin', - credentials: 'same-origin', - cache: 'default' - }) - return r.ok || r.status === 206 - } - return false + if (head.ok && !responseLooksLikeSpaHtml(head)) return true + + const r = await fetch(url, { + method: 'GET', + headers: { Range: 'bytes=0-0' }, + mode: 'same-origin', + credentials: 'same-origin', + cache: 'default' + }) + if (!responseIsUsableAsset(r)) return false + if (responseLooksLikeSpaHtml(r)) return false + return true } catch { return false }