mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
- 所有 CSP 的 connect-src 添加 https://sentry.io - *.sentry.io 通配符不匹配基础域名 sentry.io - showReportDialog 提交反馈时 XHR 被阻止,已修复
195 lines
6.6 KiB
TOML
195 lines
6.6 KiB
TOML
[build]
|
||
# 构建命令
|
||
command = "npm run build --silent"
|
||
# 发布目录:仅暴露构建后的 dist 输出
|
||
publish = "dist"
|
||
# Functions目录
|
||
functions = "netlify/functions"
|
||
# Edge Functions 目录
|
||
edge_functions = "netlify/edge-functions"
|
||
|
||
[build.environment]
|
||
# 固定 Node 版本,避免二进制依赖(如 sharp)在 Node 22 下编译耗时
|
||
NODE_VERSION = "20.12.2"
|
||
# 安装时减少无关输出与提示
|
||
NPM_FLAGS = "--no-audit --no-fund --omit=optional"
|
||
|
||
# 重定向和重写规则
|
||
[[redirects]]
|
||
# Giffgaff eSIM页面 - 模块化版本
|
||
from = "/giffgaff"
|
||
to = "/src/giffgaff/giffgaff_modular.html"
|
||
status = 200
|
||
|
||
[[redirects]]
|
||
# Simyo eSIM页面 - 模块化版本
|
||
from = "/simyo"
|
||
to = "/src/simyo/simyo_modular.html"
|
||
status = 200
|
||
|
||
[[redirects]]
|
||
# 根路径重定向到选择页面
|
||
from = "/"
|
||
to = "/index.html"
|
||
status = 200
|
||
|
||
# API代理重定向(用于解决CORS问题)
|
||
# 通用 Simyo API 代理 (webapi - 新版API)
|
||
[[redirects]]
|
||
from = "/api/simyo/*"
|
||
to = "https://appapi.simyo.nl/webapi/api/v1/:splat"
|
||
status = 200
|
||
force = true
|
||
headers = {X-Forwarded-Host = "appapi.simyo.nl"}
|
||
|
||
# v2 API 代理(用于特定端点如可用验证方式查询)
|
||
[[redirects]]
|
||
from = "/api/simyo/esim.availableValidationMethods"
|
||
to = "https://appapi.simyo.nl/webapi/api/v2/esim.availableValidationMethods"
|
||
status = 200
|
||
force = true
|
||
headers = {X-Forwarded-Host = "appapi.simyo.nl"}
|
||
|
||
# 保留旧版 simyoapi 路径的兼容性(如果需要)
|
||
[[redirects]]
|
||
from = "/api/simyo/legacy/*"
|
||
to = "https://appapi.simyo.nl/simyoapi/api/v1/:splat"
|
||
status = 200
|
||
force = true
|
||
headers = {X-Forwarded-Host = "appapi.simyo.nl"}
|
||
|
||
[[headers]]
|
||
# 限制 Simyo 代理仅允许指定站点跨域访问
|
||
for = "/api/simyo/*"
|
||
[headers.values]
|
||
Access-Control-Allow-Origin = "https://esim.cosr.eu.org"
|
||
Access-Control-Allow-Headers = "Content-Type, Authorization"
|
||
Access-Control-Allow-Methods = "GET, POST, OPTIONS"
|
||
Vary = "Origin"
|
||
|
||
# 将 /bff/* 映射到 Edge Function bff-proxy
|
||
[[edge_functions]]
|
||
path = "/bff/*"
|
||
function = "bff-proxy"
|
||
|
||
# Markdown for Agents:检测 Accept: text/markdown 请求
|
||
[[edge_functions]]
|
||
path = "/"
|
||
function = "markdown-negotiation"
|
||
|
||
[[redirects]]
|
||
from = "/api/giffgaff/*"
|
||
to = "https://api.giffgaff.com/:splat"
|
||
status = 200
|
||
force = true
|
||
headers = {X-Forwarded-Host = "api.giffgaff.com"}
|
||
|
||
[[headers]]
|
||
for = "/api/giffgaff/*"
|
||
[headers.values]
|
||
# giffgaff 直连不受此限制,保持通用放行
|
||
Access-Control-Allow-Origin = "*"
|
||
Access-Control-Allow-Headers = "Content-Type, Authorization"
|
||
Access-Control-Allow-Methods = "GET, POST, OPTIONS"
|
||
|
||
[[redirects]]
|
||
from = "/api/giffgaff-id/*"
|
||
to = "https://id.giffgaff.com/:splat"
|
||
status = 200
|
||
force = true
|
||
headers = {X-Forwarded-Host = "id.giffgaff.com"}
|
||
|
||
[[headers]]
|
||
for = "/api/giffgaff-id/*"
|
||
[headers.values]
|
||
Access-Control-Allow-Origin = "*"
|
||
Access-Control-Allow-Headers = "Content-Type, Authorization"
|
||
Access-Control-Allow-Methods = "GET, POST, OPTIONS"
|
||
|
||
[[redirects]]
|
||
from = "/api/giffgaff-public/*"
|
||
to = "https://publicapi.giffgaff.com/:splat"
|
||
status = 200
|
||
force = true
|
||
headers = {X-Forwarded-Host = "publicapi.giffgaff.com"}
|
||
|
||
[[headers]]
|
||
for = "/api/giffgaff-public/*"
|
||
[headers.values]
|
||
Access-Control-Allow-Origin = "*"
|
||
Access-Control-Allow-Headers = "Content-Type, Authorization"
|
||
Access-Control-Allow-Methods = "GET, POST, OPTIONS"
|
||
# favicon 根路径重定向(浏览器默认请求 /favicon.ico)
|
||
[[redirects]]
|
||
from = "/favicon.ico"
|
||
to = "/src/assets/favicon.ico"
|
||
status = 200
|
||
|
||
# OG 社交分享图片重定向
|
||
[[redirects]]
|
||
from = "/og-image.png"
|
||
to = "/src/assets/og-image.png"
|
||
status = 200
|
||
|
||
# 处理SPA路由(兜底 404)
|
||
[[redirects]]
|
||
from = "/*"
|
||
to = "/index.html"
|
||
status = 404
|
||
|
||
# 静态资源长期缓存(CSS/JS/字体/图片)
|
||
[[headers]]
|
||
for = "/src/styles/*"
|
||
[headers.values]
|
||
Cache-Control = "public, max-age=31536000, immutable"
|
||
|
||
[[headers]]
|
||
for = "/src/js/*"
|
||
[headers.values]
|
||
Cache-Control = "public, max-age=31536000, immutable"
|
||
|
||
[[headers]]
|
||
for = "/src/assets/*"
|
||
[headers.values]
|
||
Cache-Control = "public, max-age=31536000, immutable"
|
||
|
||
[[headers]]
|
||
for = "/dist/*"
|
||
[headers.values]
|
||
Cache-Control = "public, max-age=31536000, immutable"
|
||
|
||
# HTML 页面:短缓存 + stale-while-revalidate
|
||
[[headers]]
|
||
for = "/*.html"
|
||
[headers.values]
|
||
Cache-Control = "public, max-age=300, stale-while-revalidate=86400"
|
||
|
||
# 安全头部
|
||
[[headers]]
|
||
for = "/*"
|
||
[headers.values]
|
||
X-Frame-Options = "DENY"
|
||
X-XSS-Protection = "1; mode=block"
|
||
X-Content-Type-Options = "nosniff"
|
||
Referrer-Policy = "strict-origin-when-cross-origin"
|
||
# 生产 CSP:包含 unsafe-inline 是因为部分页面仍有内联样式和脚本(如内联 CSS、runtime 兜底脚本),
|
||
# 迁移至 nonce/hash 策略需模板注入机制支持,当前暂保留 unsafe-inline
|
||
Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.googletagmanager.com https://www.google.com https://www.gstatic.com https://browser.sentry-cdn.com https://sentry.io https://*.sentry.io https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com; img-src 'self' data: https:; connect-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://browser.sentry-cdn.com https://www.google-analytics.com https://analytics.google.com https://stats.g.doubleclick.net https://www.googletagmanager.com https://qrcode.show https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com https://www.google.com https://www.gstatic.com https://challenges.cloudflare.com https://sentry.io https://*.sentry.io; font-src 'self' data: https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.gstatic.com; frame-src 'self' https://challenges.cloudflare.com https://www.google.com https://*.sentry.io; worker-src 'self' blob:; child-src 'self' blob:; manifest-src 'self';"
|
||
|
||
# Agent 发现 Link 响应头 (RFC 8288)
|
||
[[headers]]
|
||
for = "/"
|
||
[headers.values]
|
||
Link = "</.well-known/api-catalog>; rel=\"api-catalog\", </.well-known/oauth-protected-resource>; rel=\"oauth-protected-resource\", </.well-known/mcp/server-card.json>; rel=\"mcp-server-card\", </.well-known/agent-skills/index.json>; rel=\"agent-skills\""
|
||
|
||
# Markdown for Agents 支持头部
|
||
[[headers]]
|
||
for = "/"
|
||
[headers.values]
|
||
Vary = "Accept"
|
||
|
||
[[headers]]
|
||
for = "/manifest.webmanifest"
|
||
[headers.values]
|
||
Content-Type = "application/manifest+json; charset=utf-8"
|