diff --git a/docs/SECURITY.md b/docs/SECURITY.md
index 95ecb40..2b2a181 100644
--- a/docs/SECURITY.md
+++ b/docs/SECURITY.md
@@ -66,7 +66,7 @@ app.use(helmet({
scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https:", "http:"],
- connectSrc: ["'self'", "https://qrcode.show", "https://api.qrserver.com", "https://appapi.simyo.nl", "https://api.giffgaff.com", "https://id.giffgaff.com", "https://publicapi.giffgaff.com", "https://cdn.jsdelivr.net", "https://*.sentry.io"],
+ connectSrc: ["'self'", "https://appapi.simyo.nl", "https://api.giffgaff.com", "https://id.giffgaff.com", "https://publicapi.giffgaff.com", "https://cdn.jsdelivr.net", "https://*.sentry.io"],
fontSrc: ["'self'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://fonts.gstatic.com"]
}
}
diff --git a/index.html b/index.html
index 0f7c3f2..4ab41cb 100644
--- a/index.html
+++ b/index.html
@@ -10,7 +10,7 @@
-
+
eSIM Tools - Giffgaff & Simyo eSIM 在线管理工具 | 设备更换、激活与二维码生成
@@ -67,7 +67,6 @@
-
diff --git a/netlify/functions/qrcode-generate.js b/netlify/functions/qrcode-generate.js
index 7fb2d86..6f41509 100644
--- a/netlify/functions/qrcode-generate.js
+++ b/netlify/functions/qrcode-generate.js
@@ -48,6 +48,8 @@ function withTimeout(promise, timeoutMs) {
}
exports.handler = withAuth(async (event, context, { body }) => {
+ const startTime = Date.now();
+
// 405 优先于 Schema 验证(确保 HTTP 方法错误优先返回)
if (event.httpMethod !== 'POST') {
throw new AuthError('Method Not Allowed', 405);
@@ -58,6 +60,9 @@ exports.handler = withAuth(async (event, context, { body }) => {
const size = normalizeSize(body.size);
+ // 记录请求开始(不记录 body.data,避免 LPA 激活信息泄露)
+ console.log(`[qrcode-generate] Request received: size=${size}, dataLength=${body.data ? body.data.length : 0}`);
+
try {
const qrcode = await withTimeout(QRCode.toDataURL(body.data, {
errorCorrectionLevel: 'M',
@@ -66,6 +71,11 @@ exports.handler = withAuth(async (event, context, { body }) => {
width: size
}), QR_TIMEOUT_MS);
+ const duration = Date.now() - startTime;
+
+ // 记录成功(不记录 QR 码内容)
+ console.log(`[qrcode-generate] Success: size=${size}, duration=${duration}ms, qrcodeLength=${qrcode.length}`);
+
return {
statusCode: 200,
body: JSON.stringify({
@@ -74,8 +84,10 @@ exports.handler = withAuth(async (event, context, { body }) => {
})
};
} catch (error) {
+ const duration = Date.now() - startTime;
+
// 不记录 data,避免 LPA 激活信息进入日志或 Sentry。
- console.error('[qrcode-generate] QR code generation failed:', error.message);
+ console.error(`[qrcode-generate] Failed: error=${error.message}, duration=${duration}ms`);
// 创建一个不含敏感数据的错误对象,防止 LPA 字符串通过 Sentry 泄露
const sanitizedError = new Error(error.message);
diff --git a/server.js b/server.js
index 68bfc71..d732488 100644
--- a/server.js
+++ b/server.js
@@ -62,7 +62,7 @@ app.use(helmet({
scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https:", "http:"],
- connectSrc: ["'self'", "https://qrcode.show", "https://api.qrserver.com", "https://appapi.simyo.nl", "https://api.giffgaff.com", "https://id.giffgaff.com", "https://publicapi.giffgaff.com", "https://cdn.jsdelivr.net", "https://*.sentry.io"],
+ connectSrc: ["'self'", "https://appapi.simyo.nl", "https://api.giffgaff.com", "https://id.giffgaff.com", "https://publicapi.giffgaff.com", "https://cdn.jsdelivr.net", "https://*.sentry.io"],
fontSrc: ["'self'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://fonts.gstatic.com"]
}
}
diff --git a/src/giffgaff/giffgaff_modular.html b/src/giffgaff/giffgaff_modular.html
index 004d926..ef1cf38 100644
--- a/src/giffgaff/giffgaff_modular.html
+++ b/src/giffgaff/giffgaff_modular.html
@@ -15,13 +15,12 @@
-
-
+
Giffgaff eSIM 工具
diff --git a/src/giffgaff/js/modules/api-config.js b/src/giffgaff/js/modules/api-config.js
index 63f8239..c2b4e5c 100644
--- a/src/giffgaff/js/modules/api-config.js
+++ b/src/giffgaff/js/modules/api-config.js
@@ -34,8 +34,7 @@ export function getApiEndpoints() {
: "https://publicapi.giffgaff.com/gateway/graphql",
cookieVerify: "/bff/verify-cookie",
autoActivate: "/bff/auto-activate-esim",
- smsActivate: "/bff/giffgaff-sms-activate",
- qrcode: "https://qrcode.show/"
+ smsActivate: "/bff/giffgaff-sms-activate"
};
}
diff --git a/src/js/modules/qrcode-generator.js b/src/js/modules/qrcode-generator.js
index e918bbb..b054013 100644
--- a/src/js/modules/qrcode-generator.js
+++ b/src/js/modules/qrcode-generator.js
@@ -1,7 +1,5 @@
'use strict';
-import { tl } from './i18n.js';
-
// CDN 多源备用列表(全部已验证 HTTP 200 + 浏览器 UMD 全局变量)
// 统一使用 qrcode-generator 包(UMD 格式,设置 window.qrcode 全局变量)
// 注意:jsdelivr 的 qrcode@1.5.4/lib/browser.js 是 CommonJS 模块,浏览器无法使用
@@ -16,6 +14,9 @@ const MAX_QR_SIZE = 600;
const MAX_QR_DATA_LENGTH = 2048;
const BACKEND_ENDPOINT = '/bff/qrcode-generate';
const BACKEND_TIMEOUT_MS = 10000;
+const CDN_LOAD_TIMEOUT_MS = 5000;
+const QR_MARGIN_MODULES = 8;
+const LARGE_PREVIEW_SIZE = 400;
let qrCodeLibraryPromise = null;
@@ -30,8 +31,11 @@ let qrCodeLibraryPromise = null;
*/
function trackQRCodeEvent({ type, source, success, duration, error }) {
try {
+ const isBrowser = typeof window !== 'undefined';
+ if (!isBrowser) return;
+
// 1. 上报到 Sentry(错误事件)
- if (!success && typeof window !== 'undefined' && window.Sentry) {
+ if (!success && window.Sentry) {
window.Sentry.captureMessage(`QR Code ${type} failed`, {
level: 'warning',
tags: {
@@ -47,16 +51,14 @@ function trackQRCodeEvent({ type, source, success, duration, error }) {
}
// 2. 上报到 Analytics(成功和失败都上报)
- if (typeof window !== 'undefined') {
- window.__esimAnalytics = window.__esimAnalytics || [];
- window.__esimAnalytics.push({
- event: type,
- source,
- success,
- duration,
- error: error || null
- });
- }
+ window.__esimAnalytics = window.__esimAnalytics || [];
+ window.__esimAnalytics.push({
+ event: type,
+ source,
+ success,
+ duration,
+ error: error || null
+ });
// 3. 控制台日志(开发调试)
if (!success) {
@@ -136,12 +138,14 @@ export async function loadQRCodeLibrary() {
* @returns {Promise