Files
SWebStatic/cloudflare/src/index.js
T

129 lines
4.3 KiB
JavaScript
Raw Normal View History

2026-07-08 18:29:36 +08:00
/**
* Cloudflare Workers 静态站点服务 (Wrangler 4.x 兼容)
*
* @param {Request} request
* @param {Object} env - 环境变量,包含 ASSETS 绑定
* @param {ExecutionContext} ctx
* @returns {Promise<Response>}
*/
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const pathname = url.pathname;
// 🏠 根路径处理
if (pathname === '/' || pathname === '/index') {
url.pathname = '/index.html';
}
try {
// 🔍 检查 ASSETS 是否存在
if (!env.ASSETS) {
throw new Error('ASSETS binding not configured. Check wrangler.toml configuration.');
}
// 🔍 获取静态资源
let response = await env.ASSETS.fetch(url);
// 🚫 404 处理
if (response.status === 404) {
try {
const notFoundResponse = await env.ASSETS.fetch('/404.html');
if (notFoundResponse.ok) {
response = new Response(notFoundResponse.body, {
...notFoundResponse,
status: 404,
statusText: 'Not Found'
});
}
} catch (error) {
console.warn('Custom 404 page not available, using default');
response = new Response(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - 页面未找到</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
text-align: center;
padding: 50px;
background: #f5f5f5;
color: #333;
}
h1 {
color: #ff4444;
font-size: 3em;
margin: 20px 0;
}
p {
color: #666;
font-size: 1.2em;
margin: 20px 0;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1>404</h1>
<p>抱歉,您访问的页面不存在</p>
<p><a href="/">返回首页</a></p>
</body>
</html>
`.trim(), {
status: 404,
headers: {
'Content-Type': 'text/html; charset=utf-8'
}
});
}
}
// ⚙️ 优化响应
return this.optimizeResponse(response, pathname);
} catch (error) {
console.error('Worker error:', error);
return new Response(`服务暂时不可用: ${error.message}`, {
status: 503,
headers: {
'Content-Type': 'text/plain; charset=utf-8'
}
});
}
},
optimizeResponse(response, pathname) {
const newResponse = new Response(response.body, response);
const headers = newResponse.headers;
const contentType = headers.get('Content-Type') || '';
// 🔒 安全头
headers.set('X-Content-Type-Options', 'nosniff');
headers.set('X-Frame-Options', 'DENY');
headers.set('X-XSS-Protection', '1; mode=block');
headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
// 📦 智能缓存
if (/\.(js|css|png|jpg|jpeg|gif|svg|webp|woff2|woff|ttf|eot|ico|json)$/.test(pathname)) {
headers.set('Cache-Control', 'public, max-age=31536000, immutable');
} else if (pathname.endsWith('.html')) {
headers.set('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400');
} else {
headers.set('Cache-Control', 'public, max-age=86400');
}
// 🌐 CORS
headers.set('Access-Control-Allow-Origin', '*');
return newResponse;
}
};