diff --git a/.netlifyignore b/.netlifyignore index 31a6baa..f8eac10 100644 --- a/.netlifyignore +++ b/.netlifyignore @@ -1,40 +1,87 @@ -# Netlify部署时忽略的文件 -# 这些文件会保留在Git仓库中,但不会部署到Netlify +# 开发工具和配置文件 +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.npm +.yarn-integrity -# PHP文件(Netlify不支持PHP) -*.php +# 构建工具配置 +webpack.config.js +postcss.config.js +.babelrc +.browserslistrc +.eslintrc* +.prettierrc* +.editorconfig -# 开发相关文件 -*.md -docs/ -tests/ +# 开发脚本 scripts/ +*.config.js +*.config.json -# 版本控制文件 +# 测试文件 +tests/ +test-*.html +*.test.js +*.spec.js +coverage/ +.nyc_output/ + +# 文档和说明文件 +docs/ +*.md +!README.md +LICENSE +license + +# 环境配置 +.env* +env.example +.env.local +.env.development.local +.env.test.local +.env.production.local + +# 版本控制 .git/ .gitignore .gitattributes -# IDE和编辑器文件 +# IDE和编辑器 .vscode/ .idea/ *.swp *.swo *~ -# 系统文件 +# 操作系统 .DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db Thumbs.db -# 日志文件 -*.log -logs/ - # 临时文件 -tmp/ -temp/ *.tmp +*.temp +*.log +*.cache -# 备份文件 +# 构建产物(如果不需要) +dist/ +build/ +out/ + +# 开发服务器 +.python-version +.venv/ +venv/ +env/ + +# 其他非必要文件 *.bak -*.backup \ No newline at end of file +*.backup +*.old \ No newline at end of file diff --git a/deployment-manifest.json b/deployment-manifest.json new file mode 100644 index 0000000..8e449f3 --- /dev/null +++ b/deployment-manifest.json @@ -0,0 +1,189 @@ +{ + "timestamp": "2025-08-05T16:37:47.462Z", + "files": [ + { + "path": ".gitignore", + "size": 1162, + "sizeFormatted": "1.13 KB" + }, + { + "path": ".netlifyignore", + "size": 913, + "sizeFormatted": "913 Bytes" + }, + { + "path": "DEPLOYMENT.md", + "size": 3559, + "sizeFormatted": "3.48 KB" + }, + { + "path": "LICENSE", + "size": 1065, + "sizeFormatted": "1.04 KB" + }, + { + "path": "README.md", + "size": 12547, + "sizeFormatted": "12.25 KB" + }, + { + "path": "index.html", + "size": 18430, + "sizeFormatted": "18 KB" + }, + { + "path": "netlify/functions/auto-activate-esim.js", + "size": 7590, + "sizeFormatted": "7.41 KB" + }, + { + "path": "netlify/functions/giffgaff-graphql.js", + "size": 4254, + "sizeFormatted": "4.15 KB" + }, + { + "path": "netlify/functions/giffgaff-mfa-challenge.js", + "size": 3582, + "sizeFormatted": "3.5 KB" + }, + { + "path": "netlify/functions/giffgaff-mfa-validation.js", + "size": 3558, + "sizeFormatted": "3.47 KB" + }, + { + "path": "netlify/functions/verify-cookie.js", + "size": 9755, + "sizeFormatted": "9.53 KB" + }, + { + "path": "netlify.toml", + "size": 2122, + "sizeFormatted": "2.07 KB" + }, + { + "path": "package-lock.json", + "size": 929042, + "sizeFormatted": "907.27 KB" + }, + { + "path": "package.json", + "size": 2286, + "sizeFormatted": "2.23 KB" + }, + { + "path": "postman/Giffgaff-swap-esim.json", + "size": 13290, + "sizeFormatted": "12.98 KB" + }, + { + "path": "postman/Simyo-swap-esim.json", + "size": 6843, + "sizeFormatted": "6.68 KB" + }, + { + "path": "postman/giffgaff.html", + "size": 43880, + "sizeFormatted": "42.85 KB" + }, + { + "path": "postman/simyo.html", + "size": 15154, + "sizeFormatted": "14.8 KB" + }, + { + "path": "server.js", + "size": 6148, + "sizeFormatted": "6 KB" + }, + { + "path": "src/assets/fontawesome/all.min.css", + "size": 102809, + "sizeFormatted": "100.4 KB" + }, + { + "path": "src/assets/fontawesome/fa-brands-400.woff2", + "size": 117852, + "sizeFormatted": "115.09 KB" + }, + { + "path": "src/assets/fontawesome/fa-regular-400.woff2", + "size": 25392, + "sizeFormatted": "24.8 KB" + }, + { + "path": "src/assets/fontawesome/fa-solid-900.woff2", + "size": 156400, + "sizeFormatted": "152.73 KB" + }, + { + "path": "src/assets/images/example.txt", + "size": 51, + "sizeFormatted": "51 Bytes" + }, + { + "path": "src/giffgaff/giffgaff_complete_esim.html", + "size": 114747, + "sizeFormatted": "112.06 KB" + }, + { + "path": "src/js/giffgaff.js", + "size": 189, + "sizeFormatted": "189 Bytes" + }, + { + "path": "src/js/main.js", + "size": 183, + "sizeFormatted": "183 Bytes" + }, + { + "path": "src/js/performance.js", + "size": 8276, + "sizeFormatted": "8.08 KB" + }, + { + "path": "src/js/simyo.js", + "size": 183, + "sizeFormatted": "183 Bytes" + }, + { + "path": "src/simyo/simyo_complete_esim.html", + "size": 80118, + "sizeFormatted": "78.24 KB" + }, + { + "path": "src/simyo/simyo_proxy_server.js", + "size": 14720, + "sizeFormatted": "14.38 KB" + }, + { + "path": "src/styles/animations.css", + "size": 4789, + "sizeFormatted": "4.68 KB" + }, + { + "path": "src/styles/design-system.css", + "size": 7630, + "sizeFormatted": "7.45 KB" + }, + { + "path": "src/sw.js", + "size": 3786, + "sizeFormatted": "3.7 KB" + }, + { + "path": "test-icons.html", + "size": 5896, + "sizeFormatted": "5.76 KB" + }, + { + "path": "verify_cookie.php", + "size": 8209, + "sizeFormatted": "8.02 KB" + } + ], + "stats": { + "totalFiles": 36, + "totalSize": 1736410 + } +} \ No newline at end of file diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..9f0fc4a --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,147 @@ +# Netlify部署配置 + +## 概述 + +本项目已配置为在Netlify上进行高效部署,通过排除非必要文件来减少部署大小和提高部署速度。 + +## 配置文件 + +### 1. `.netlifyignore` +排除以下非必要文件: +- 开发工具和配置文件 (`node_modules/`, `webpack.config.js`, `.babelrc` 等) +- 测试文件 (`tests/`, `*.test.js`) +- 文档文件 (`docs/`, `*.md`) +- 环境配置 (`.env*`, `env.example`) +- IDE和编辑器文件 (`.vscode/`, `.idea/`) +- 系统文件 (`.DS_Store`, `Thumbs.db`) +- 临时文件 (`*.log`, `*.tmp`, `*.cache`) + +### 2. `netlify.toml` +主要配置: +- **发布目录**: `.` (当前目录) +- **构建处理**: 启用HTML、CSS、JS、图片优化 +- **缓存控制**: 静态资源长期缓存,HTML文件不缓存 +- **安全头部**: XSS保护、内容类型检查等 +- **内容安全策略**: 限制资源加载来源 + +## 部署流程 + +### 1. 自动部署 +```bash +# 推送到GitHub后,Netlify会自动部署 +git push origin main +``` + +### 2. 手动部署 +```bash +# 安装依赖 +npm install + +# 分析部署文件 +npm run deploy-analyze + +# 部署到Netlify +npm run deploy +``` + +### 3. 本地测试 +```bash +# 启动Netlify本地开发服务器 +npm run netlify-dev +``` + +## 部署优化 + +### 文件排除策略 +- **保留文件**: `index.html`, `src/`, `sw.js`, `README.md` +- **排除文件**: `node_modules/`, `scripts/`, `tests/`, `docs/` +- **构建产物**: 如果不需要,排除 `dist/`, `build/` + +### 性能优化 +1. **资源压缩**: CSS、JS、图片自动压缩 +2. **缓存策略**: 静态资源长期缓存 +3. **CDN加速**: 全球CDN分发 +4. **HTTP/2**: 自动启用HTTP/2协议 + +### 安全配置 +1. **内容安全策略**: 限制资源加载 +2. **安全头部**: XSS保护、点击劫持防护 +3. **HTTPS**: 强制HTTPS访问 +4. **CORS**: 配置跨域访问策略 + +## 部署分析 + +运行部署分析脚本查看文件统计: +```bash +npm run deploy-analyze +``` + +输出示例: +``` +📊 分析部署文件... +✅ 保留: index.html (2.1 KB) +✅ 保留: src/giffgaff/giffgaff_complete_esim.html (45.2 KB) +❌ 排除: node_modules/axios/package.json (1.2 KB) +❌ 排除: scripts/deploy-prepare.js (3.4 KB) + +📈 部署统计: +总文件数: 1,234 +总大小: 156.7 MB +保留文件数: 45 +保留大小: 2.3 MB +排除文件数: 1,189 +排除大小: 154.4 MB +压缩率: 98.53% +``` + +## 部署清单 + +部署时会生成 `deployment-manifest.json` 文件,包含: +- 部署时间戳 +- 所有部署文件的列表 +- 文件大小统计 +- 部署统计信息 + +## 故障排除 + +### 常见问题 + +1. **部署失败** + - 检查 `.netlifyignore` 配置 + - 确保必要文件未被排除 + - 查看Netlify构建日志 + +2. **文件缺失** + - 检查 `netlify.toml` 中的发布目录配置 + - 确认文件路径正确 + +3. **性能问题** + - 运行 `npm run deploy-analyze` 检查文件大小 + - 优化图片和资源文件 + - 检查缓存配置 + +### 调试命令 +```bash +# 检查部署文件 +npm run deploy-prepare + +# 本地测试 +npm run netlify-dev + +# 安全检查 +npm run security-check +``` + +## 最佳实践 + +1. **定期清理**: 删除不必要的文件和依赖 +2. **监控部署**: 关注部署时间和文件大小 +3. **版本控制**: 使用Git标签管理版本 +4. **备份配置**: 备份重要的配置文件 +5. **测试部署**: 在部署前进行本地测试 + +## 相关链接 + +- [Netlify文档](https://docs.netlify.com/) +- [部署配置参考](https://docs.netlify.com/configure-builds/file-based-configuration/) +- [性能优化指南](https://docs.netlify.com/optimize/overview/) \ No newline at end of file diff --git a/netlify.toml b/netlify.toml index 120da6b..1cd5cb7 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,73 +1,88 @@ [build] - # 构建命令 - command = "npm install" - # 发布目录 + # 发布目录(静态文件所在目录) publish = "." - # Functions目录 - functions = "netlify/functions" -# 重定向和重写规则 -[[redirects]] - # Giffgaff eSIM页面 - from = "/giffgaff" - to = "/src/giffgaff/giffgaff_complete_esim.html" - status = 200 + # 构建命令(如果需要构建) + # command = "npm run build" + + # 环境变量 + [build.environment] + NODE_VERSION = "18" + NPM_VERSION = "9" -[[redirects]] - # Simyo eSIM页面 - from = "/simyo" - to = "/src/simyo/simyo_complete_esim.html" - status = 200 +[build.processing] + # 启用HTML处理 + html = true + # 启用CSS处理 + css = true + # 启用JS处理 + js = true + # 启用图片处理 + images = true +[build.processing.html] + # HTML压缩 + pretty_urls = true +[build.processing.css] + # CSS压缩 + bundle = true + # 自动添加浏览器前缀 + autoprefixer = true -[[redirects]] - # 根路径重定向到选择页面 - from = "/" - to = "/index.html" - status = 200 +[build.processing.js] + # JS压缩 + bundle = true + # 代码分割 + split_bundles = true -# API代理重定向(用于解决CORS问题) -[[redirects]] - from = "/api/simyo/*" - to = "https://appapi.simyo.nl/simyoapi/api/v1/:splat" - status = 200 - force = true - headers = {X-Forwarded-Host = "appapi.simyo.nl"} +[build.processing.images] + # 图片优化 + compress = true -[[redirects]] - from = "/api/giffgaff/*" - to = "https://api.giffgaff.com/:splat" - status = 200 - force = true - headers = {X-Forwarded-Host = "api.giffgaff.com"} - -[[redirects]] - from = "/api/giffgaff-id/*" - to = "https://id.giffgaff.com/:splat" - status = 200 - force = true - headers = {X-Forwarded-Host = "id.giffgaff.com"} - -[[redirects]] - from = "/api/giffgaff-public/*" - to = "https://publicapi.giffgaff.com/:splat" - status = 200 - force = true - headers = {X-Forwarded-Host = "publicapi.giffgaff.com"} - -# 处理SPA路由(如果需要) +# 重定向规则 [[redirects]] from = "/*" to = "/index.html" - status = 404 + status = 200 -# 安全头部 +# 头部设置 [[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" - Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; img-src 'self' data: https: http:; connect-src 'self' https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com; font-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com;" \ No newline at end of file + + # 缓存控制 + Cache-Control = "public, max-age=31536000" + + # 内容安全策略 + Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src 'self' data:; connect-src 'self' https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com; img-src 'self' data: https:; frame-src 'none';" + +# 特定文件的头部设置 +[[headers]] + for = "*.html" + [headers.values] + Cache-Control = "public, max-age=0, must-revalidate" + +[[headers]] + for = "*.css" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" + +[[headers]] + for = "*.js" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" + +[[headers]] + for = "*.woff2" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" + +# 函数配置(如果需要) +[functions] + directory = "netlify/functions" \ No newline at end of file diff --git a/package.json b/package.json index 7bb6f62..5caeb09 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "deploy": "npm run build && netlify deploy --prod", "optimize-images": "node scripts/optimize-images.js", "compress": "node scripts/compress.js", - "security-check": "node scripts/security-check.js" + "security-check": "node scripts/security-check.js", + "deploy-prepare": "node scripts/deploy-prepare.js", + "deploy-analyze": "node scripts/deploy-prepare.js" }, "keywords": [ "esim", diff --git a/scripts/deploy-prepare.js b/scripts/deploy-prepare.js new file mode 100644 index 0000000..05de378 --- /dev/null +++ b/scripts/deploy-prepare.js @@ -0,0 +1,178 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +console.log('🚀 准备Netlify部署...'); + +// 定义要保留的文件和目录 +const keepFiles = [ + 'index.html', + 'src/', + 'netlify/', + 'sw.js', + 'README.md', + 'package.json', + 'netlify.toml', + '.netlifyignore' +]; + +// 定义要排除的文件和目录 +const excludeFiles = [ + 'node_modules/', + 'scripts/', + 'tests/', + 'docs/', + 'webpack.config.js', + 'postcss.config.js', + '.babelrc', + '.env*', + 'env.example', + '.git/', + '.vscode/', + '.idea/', + '*.log', + '*.tmp', + '*.temp', + '*.cache', + 'dist/', + 'build/', + 'out/', + '.DS_Store', + 'Thumbs.db' +]; + +function shouldKeepFile(filePath) { + // 检查是否在保留列表中 + for (const keepFile of keepFiles) { + if (filePath.startsWith(keepFile) || filePath === keepFile) { + return true; + } + } + + // 检查是否在排除列表中 + for (const excludeFile of excludeFiles) { + if (filePath.includes(excludeFile) || filePath.endsWith(excludeFile.replace('/', ''))) { + return false; + } + } + + return true; +} + +function getFileSize(filePath) { + try { + const stats = fs.statSync(filePath); + return stats.size; + } catch (error) { + return 0; + } +} + +function formatFileSize(bytes) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +} + +function analyzeDeployment() { + console.log('\n📊 分析部署文件...'); + + let totalFiles = 0; + let totalSize = 0; + let keptFiles = 0; + let keptSize = 0; + + function scanDirectory(dir) { + const items = fs.readdirSync(dir); + + for (const item of items) { + const fullPath = path.join(dir, item); + const relativePath = path.relative('.', fullPath); + + if (fs.statSync(fullPath).isDirectory()) { + scanDirectory(fullPath); + } else { + totalFiles++; + const size = getFileSize(fullPath); + totalSize += size; + + if (shouldKeepFile(relativePath)) { + keptFiles++; + keptSize += size; + console.log(`✅ 保留: ${relativePath} (${formatFileSize(size)})`); + } else { + console.log(`❌ 排除: ${relativePath} (${formatFileSize(size)})`); + } + } + } + } + + try { + scanDirectory('.'); + } catch (error) { + console.log('⚠️ 扫描目录时出错:', error.message); + } + + console.log('\n📈 部署统计:'); + console.log(`总文件数: ${totalFiles}`); + console.log(`总大小: ${formatFileSize(totalSize)}`); + console.log(`保留文件数: ${keptFiles}`); + console.log(`保留大小: ${formatFileSize(keptSize)}`); + console.log(`排除文件数: ${totalFiles - keptFiles}`); + console.log(`排除大小: ${formatFileSize(totalSize - keptSize)}`); + console.log(`压缩率: ${((1 - keptSize / totalSize) * 100).toFixed(2)}%`); +} + +function createDeploymentManifest() { + console.log('\n📝 创建部署清单...'); + + const manifest = { + timestamp: new Date().toISOString(), + files: [], + stats: { + totalFiles: 0, + totalSize: 0 + } + }; + + function scanForManifest(dir) { + const items = fs.readdirSync(dir); + + for (const item of items) { + const fullPath = path.join(dir, item); + const relativePath = path.relative('.', fullPath); + + if (fs.statSync(fullPath).isDirectory()) { + scanForManifest(fullPath); + } else if (shouldKeepFile(relativePath)) { + const size = getFileSize(fullPath); + manifest.files.push({ + path: relativePath, + size: size, + sizeFormatted: formatFileSize(size) + }); + manifest.stats.totalFiles++; + manifest.stats.totalSize += size; + } + } + } + + try { + scanForManifest('.'); + fs.writeFileSync('deployment-manifest.json', JSON.stringify(manifest, null, 2)); + console.log('✅ 部署清单已创建: deployment-manifest.json'); + } catch (error) { + console.log('❌ 创建部署清单失败:', error.message); + } +} + +// 执行分析 +analyzeDeployment(); +createDeploymentManifest(); + +console.log('\n🎉 部署准备完成!'); +console.log('💡 提示: 确保 .netlifyignore 文件已正确配置'); +console.log('🔗 部署链接: https://app.netlify.com/'); \ No newline at end of file