Files
eSIM-Tools/index.html
Abner 5a29e64ec7 feat: 重构部署流程并移除 Service Worker
- 新增部署工具脚本:添加 `build-static.js`、`deploy-prepare.js`、`deploy-analyze.js` 和 `test-deploy-config.js` 标准化构建部署流程
- 配置 Netlify 发布目录:修改 `netlify.toml` 将 `publish` 从根目录切换到 `dist` 目录以增强安全性
- 移除 Service Worker 相关代码:删除 `sw.js` 并清理相关注册逻辑,简化性能优化策略
- 增强服务器静态文件服务:重构静态资源中间件,增加环境检查和错误处理
- 改进 API 密钥处理逻辑:统一 Netlify Functions 的密钥验证流程,增加配置缺失提示
- 添加 Serena AI 配置文件:包含项目元数据、语言服务器设置和常用命令记忆
- 重构 HTML 页面路由:使用配置化的路由表替代硬编码路径,提高可维护性
2025-11-23 19:44:45 +08:00

433 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN" data-page="home">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="eSIM 工具集,支持 Giffgaff 与 Simyo 的 eSIM 申请与管理、二维码生成及验证码处理。">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src 'self' data:; connect-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.google-analytics.com 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://challenges.cloudflare.com; img-src 'self' data: https:; frame-src 'self' https://challenges.cloudflare.com;">
<title>eSIM Tools - Giffgaff & Simyo</title>
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#6366f1">
<!-- 性能优化:预连接与预加载关键资源 -->
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
<link rel="preconnect" href="https://qrcode.show" crossorigin>
<link rel="preconnect" href="https://api.giffgaff.com" crossorigin>
<link rel="preconnect" href="https://appapi.simyo.nl" crossorigin>
<!-- Favicon -->
<link rel="icon" href="/src/assets/favicon.ico" type="image/x-icon">
<!-- 关键CSS优先preload 后备 rel=stylesheet -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" as="style" onload="this.rel='stylesheet'">
<noscript><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"></noscript>
<!-- Font Awesome直接引入避免 onload 触发器被 CSP 阻断导致样式未生效 -->
<link rel="stylesheet" href="/src/assets/fontawesome/all.min.css">
<!-- Font Awesome 资源路径woff2在 /src/assets/fontawesome/,确保已发布且 CSP 允许 font-src 'self' -->
<!-- 本地样式文件 -->
<link rel="preload" href="/src/styles/design-system.css" as="style" onload="this.rel='stylesheet'">
<link rel="preload" href="/src/styles/animations.css" as="style" onload="this.rel='stylesheet'">
<link rel="stylesheet" href="/src/styles/fa-local.css">
<meta name="build-rev" content="v1">
<style>
/* Design System: 语义化令牌与统一规范 */
:root{
/* Brand */
--brand-start:#6366f1; /* indigo-500 */
--brand-end:#8b5cf6; /* violet-500 */
--accent-ok:#10b981; /* emerald-500 */
--accent-warn:#f59e0b; /* amber-500 */
--accent-danger:#ef4444; /* red-500 */
/* Surface & Text */
/* 背景改为淡色 #f2f5c4 */
--bg-grad-start:#f2f5c4;
--bg-grad-end:#f1f5bb;
--surface:#ffffff;
--surface-alpha:92%;
--text-primary:#1f2937; /* slate-800 */
--text-secondary:#374151; /* gray-700 */
--text-muted:#6b7280; /* gray-500 */
/* Radius & Elevation & Spacing */
--radius-lg:18px;
--shadow-sm:0 4px 10px rgba(0,0,0,.08);
--shadow-md:0 10px 30px rgba(0,0,0,.10);
--shadow-lg:0 18px 40px rgba(0,0,0,.15);
--ring:0 0 0 3px rgba(99,102,241,.35);
--s1:8px; --s2:12px; --s3:16px; --s4:20px; --s5:24px; --s6:32px;
/* Motion */
--ease-standard:cubic-bezier(.2,.8,.2,1);
--duration-fast:.18s;
--duration-normal:.28s;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, var(--bg-grad-start) 0%, var(--bg-grad-end) 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: var(--s5);
color: var(--text-primary);
}
.main-container{
background: color-mix(in oklab, white var(--surface-alpha), transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: var(--radius-lg);
padding: clamp(20px, 4vw, 40px);
box-shadow: var(--shadow-lg);
max-width: 960px;
width: 100%;
text-align: center;
border: 1px solid color-mix(in oklab, white 70%, transparent);
}
.language-switcher{
display:flex;
justify-content:flex-end;
width:100%;
margin-bottom: var(--s3);
}
.lang-switch{
display:inline-flex;
align-items:center;
gap:6px;
padding:6px 16px;
border-radius:999px;
border:1px solid rgba(99,102,241,.25);
background: rgba(255,255,255,.7);
color: var(--text-primary);
font-weight:600;
cursor:pointer;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.lang-switch:hover{
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.lang-switch:focus-visible{
outline:none;
box-shadow: var(--ring);
}
.header{ margin-bottom: var(--s6); }
.header h1{
font-size: clamp(1.9rem, 3vw, 2.6rem);
font-weight: 800;
color: var(--text-primary);
margin-bottom: var(--s2);
letter-spacing: .2px;
display:flex;align-items:center;justify-content:center;gap:10px;
}
.header .subtitle{
font-size: clamp(.95rem, 1.9vw, 1.12rem);
color: var(--text-muted);
margin: 0;
}
.github-btn{
background: #24292e;
border: 1px solid rgba(255,255,255,.18);
font-size: 16px;
color:#fff;
cursor:pointer;
padding: 10px 18px;
display:inline-flex;
align-items:center;
justify-content:center;
border-radius: 999px;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard);
gap: 8px;
box-shadow: var(--shadow-sm);
text-decoration: none;
margin-top: var(--s3);
outline: none;
will-change: transform;
}
.github-btn:hover{ transform: translateY(-1px); box-shadow: var(--shadow-md); text-decoration: none; color:#fff; background:#111; }
.github-btn:focus-visible{ outline: none; box-shadow: var(--ring); }
.tools-grid{
display:grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: clamp(16px, 3vw, 28px);
margin-top: var(--s6);
}
@media (max-width: 720px){ .tools-grid{ grid-template-columns: 1fr; } }
.tool-card{
background: var(--surface);
border-radius: var(--radius-lg);
padding: clamp(20px, 3.2vw, 28px);
box-shadow: var(--shadow-md);
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
border: 2px solid transparent;
position: relative;
overflow: hidden;
will-change: transform, box-shadow;
}
.tool-card::before{
content: '';
position: absolute;
inset: 0 0 auto 0;
height: 4px;
background: linear-gradient(90deg, var(--primary), var(--secondary));
}
.tool-card:hover{
transform: translateY(-6px);
box-shadow: var(--shadow-lg);
border-color: color-mix(in oklab, var(--primary) 40%, transparent);
}
.tool-card:focus-within{ box-shadow: var(--ring); }
/* 品牌色注入 */
.giffgaff-card{ --primary:#ff6b00; --secondary:#ff8c42; }
.simyo-card{ --primary:#e74c3c; --secondary:#c0392b; }
.tool-icon{
font-size: clamp(2.2rem, 3.2vw, 2.8rem);
color: var(--primary);
margin-bottom: var(--s3);
}
.tool-title{
font-size: clamp(1.2rem, 2.2vw, 1.4rem);
font-weight: 800;
color: var(--text-primary);
margin-bottom: var(--s2);
}
.tool-description{
color: var(--text-muted);
margin-bottom: var(--s4);
line-height: 1.65;
}
.tool-features{
list-style: none;
padding: 0;
margin-bottom: var(--s4);
text-align: left;
}
.tool-features li{
display: flex;
align-items: baseline;
gap: 10px;
padding: 6px 0;
color: var(--text-secondary);
}
.tool-features li i{ color: var(--primary); flex: 0 0 auto; }
.tool-features li span{ flex: 1 1 auto; }
.tool-btn{
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: #fff;
border: none;
padding: 12px 22px;
border-radius: 999px;
font-size: 1rem;
font-weight: 800;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 10px;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), filter var(--duration-fast) var(--ease-standard);
box-shadow: 0 6px 16px color-mix(in oklab, var(--primary) 35%, transparent);
outline: none;
will-change: transform, filter;
}
.tool-btn:hover{ transform: translateY(-2px); text-decoration: none; color: #fff; filter: brightness(1.03); }
.tool-btn:focus-visible{ box-shadow: var(--ring); }
.footer{
margin-top: var(--s6);
padding-top: var(--s5);
border-top: 1px solid color-mix(in oklab, #ecf0f1 85%, transparent);
color: #6b7280;
font-size: .94rem;
line-height: 1.6;
text-align: center;
}
/* 入场动画IntersectionObserver 触发,避免 setTimeout 抖动 */
.reveal{ opacity: 0; transform: translateY(12px); transition: opacity .5s var(--ease-standard), transform .5s var(--ease-standard); will-change: opacity, transform; }
.reveal.show{ opacity: 1; transform: translateY(0); }
/* 降低动效:尊重用户的减少动态偏好 */
@media (prefers-reduced-motion: reduce){
.reveal{ transition: none; transform: none; opacity: 1; }
.tool-card, .github-btn, .tool-btn{ transition: none!important; }
}
/* 小屏进一步优化 */
@media (max-width: 360px){
.github-btn{ padding: 9px 14px; font-size: 14px; }
.tool-btn{ padding: 10px 18px; font-size: .95rem; }
}
</style>
<!-- Google tag (gtag.js) 延迟加载 -->
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-HGX38HMNL1"></script>
</head>
<body aria-label="eSIM 工具选择">
<div class="main-container" role="region" aria-label="eSIM 工具选择">
<div class="language-switcher">
<button id="languageSwitcher" class="lang-switch" type="button" aria-label="切换语言到英文">🌐 English</button>
</div>
<div class="header">
<h1><i class="fas fa-sim-card" aria-hidden="true"></i> <span class="header-title-text">eSIM 工具集</span></h1>
<p class="subtitle">轻松管理您的 Giffgaff 与 Simyo eSIM 配置</p>
<a href="https://github.com/Silentely/eSIM-Tools" target="_blank" rel="noopener noreferrer" class="github-btn" aria-label="在新窗口打开 GitHub 仓库">
<i class="fab fa-github" aria-hidden="true"></i> GitHub
</a>
</div>
<div class="tools-grid" aria-live="polite">
<!-- Giffgaff工具卡片 -->
<section class="tool-card giffgaff-card reveal" tabindex="-1" aria-labelledby="giffgaff-title" aria-describedby="giffgaff-desc">
<div class="tool-icon" aria-hidden="true">
<i class="fas fa-mobile-alt"></i>
</div>
<h2 id="giffgaff-title" class="tool-title"><span class="tool-title-text">Giffgaff eSIM 工具</span></h2>
<p id="giffgaff-desc" class="tool-description">
<span class="tool-description-text">完整的 Giffgaff eSIM 申请与管理,支持 OAuth 认证、MFA 验证与 GraphQL API 调用</span>
</p>
<ul class="tool-features">
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>OAuth 2.0 PKCE 认证</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>邮件验证码与 MFA 支持</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>GraphQL API 集成</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>自动生成 eSIM 二维码</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>完善的错误处理</span></li>
</ul>
<a href="/giffgaff" class="tool-btn" aria-describedby="giffgaff-desc">
<i class="fas fa-arrow-right" aria-hidden="true"></i><span>使用 Giffgaff 工具</span>
</a>
</section>
<!-- Simyo工具卡片 -->
<section class="tool-card simyo-card reveal" tabindex="-1" aria-labelledby="simyo-title" aria-describedby="simyo-desc">
<div class="tool-icon" aria-hidden="true">
<i class="fas fa-sim-card"></i>
</div>
<h2 id="simyo-title" class="tool-title"><span class="tool-title-text">Simyo 工具</span></h2>
<p id="simyo-desc" class="tool-description">
<span class="tool-description-text">专为 Simyo NL 用户的 eSIM 管理,支持设备更换与短信验证码处理</span>
</p>
<ul class="tool-features">
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>简单快捷的登录验证</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>设备更换完整流程</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>短信验证码支持</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>一键生成二维码</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>安装确认功能</span></li>
</ul>
<a href="/simyo" class="tool-btn" aria-describedby="simyo-desc">
<i class="fas fa-arrow-right" aria-hidden="true"></i><span>使用 Simyo 工具</span>
</a>
</section>
</div>
<footer class="footer" aria-label="免责声明与隐私说明">
<p>
<i class="fas fa-info-circle me-2" aria-hidden="true"></i>
本工具仅供个人使用,请遵守相关服务条款。<br>
<i class="fas fa-shield-alt me-2" aria-hidden="true"></i>
所有数据处理均在本地进行,保护您的隐私安全。
</p>
</footer>
</div>
<!-- 将 bootstrap 脚本 defer减少阻塞 -->
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<!-- 首页逻辑脚本(无内联) -->
<script defer src="/src/js/home.js"></script>
<!-- 统一版权页脚(自动注入年份/品牌) -->
<script type="module" src="/src/js/bootstrap-footer.js"></script>
<!-- 国际化初始化 -->
<script type="module" src="/src/js/init-home-i18n.js"></script>
<!-- 性能优化脚本 -->
<script src="/src/js/performance.js"></script>
<!-- 网络状态指示器样式 -->
<style>
.network-status {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 8px;
color: white;
font-weight: 500;
z-index: 10000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateX(100%);
transition: transform 0.3s ease;
}
.network-status.success {
background: linear-gradient(135deg, #10b981, #059669);
}
.network-status.warning {
background: linear-gradient(135deg, #f59e0b, #d97706);
}
.network-status.show {
transform: translateX(0);
}
.update-notification {
position: fixed;
bottom: 20px;
right: 20px;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: white;
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 8px 25px rgba(99, 102, 241, 0.3);
z-index: 10000;
max-width: 300px;
}
.notification-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.touch-active {
transform: scale(0.98) !important;
transition: transform 0.1s ease !important;
}
/* 加载状态样式 */
.loading-content {
background: white;
padding: 30px;
border-radius: 12px;
text-align: center;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.loading-content p {
margin: 0;
color: #6b7280;
font-weight: 500;
}
</style>
</body>
</html>