Cloudflare Workers + R2部署Hugo终极解决方案
Cloudflare Workers + R2部署Hugo网站,突破Workers文件数上限终极解决方案
增强版配置模板
终极版 wrangler.toml
name = "hugo-prod"
main = "worker.js"
compatibility_date = "2024-06-25"
node_compat = true
# R2 存储桶配置
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "hugo-assets-prod"
# KV 命名空间(用于缓存/会话)
[[kv_namespaces]]
binding = "CACHE"
id = "xxxxxxxxxxxxxxxxxxxxxxxx" # 实际的 KV ID
# 环境变量
[vars]
SITE_URL = "https://yourdomain.com"
GA_ID = "UA-XXXXX-1"
ENV = "production"
# D1 数据库(可选)
[[d1_databases]]
binding = "DB"
database_name = "hugo-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# 生产环境配置
[env.production]
name = "hugo-prod-worker"
vars = { ENV = "production" }
# 预览环境配置
[env.staging]
name = "hugo-staging-worker"
vars = {
ENV = "staging",
R2_BUCKET = "hugo-assets-staging" # 使用不同的存储桶
}
# 路由配置
routes = [
{ pattern = "yourdomain.com/*", zone_id = "your-zone-id" },
{ pattern = "*.yourdomain.com/*", zone_id = "your-zone-id" }
]
# 自定义构建命令
[build]
command = "npm run build && hugo --minify"
终极版 worker.js
https://developers.cloudflare.com/workers/examples/
// ===== 初始化 =====
export default {
async fetch(request, env, ctx) {
try {
const url = new URL(request.url);
const cacheKey = new Request(url, request);
// 1. 尝试从缓存获取
const cache = caches.default;
let response = await cache.match(cacheKey);
if (response) {
console.log(`Cache hit: ${url.pathname}`);
return response;
}
// 2. 处理请求
response = await handleRequest(request, env);
// 3. 缓存响应(非个性化内容)
if (shouldCache(request, response)) {
ctx.waitUntil(cache.put(cacheKey, response.clone()));
}
return response;
} catch (err) {
// 错误处理
return new Response(`Server Error: ${err.message}`, {
status: 500,
headers: { 'Content-Type': 'text/plain' }
});
}
}
};
// ===== 核心请求处理器 =====
async function handleRequest(request, env) {
const url = new URL(request.url);
let pathname = url.pathname;
// 0. 机器人检测
if (isSearchBot(request)) {
return handleBotRequest(request, env);
}
// 1. 处理特殊路径
if (pathname.startsWith('/api/')) {
return handleAPI(request, env);
}
if (pathname.startsWith('/_image/')) {
return handleImageProxy(request, env);
}
// 2. 重定向规则
const redirectPath = getRedirect(pathname);
if (redirectPath) {
return Response.redirect(redirectPath, 301);
}
// 3. 多语言处理
const { lang, path } = detectLanguage(pathname, request.headers);
pathname = path || pathname;
// 4. 尾部斜杠处理
pathname = normalizeTrailingSlash(pathname);
// 5. 获取文件内容
let objectName = pathname.replace(/^\//, '');
let object = await getAsset(env, objectName);
// 6. 处理404
if (!object) {
return handleNotFound(env, pathname);
}
// 7. 构建响应
return buildAssetResponse(object, objectName, env);
}
// ===== 增强功能模块 =====
// 资源获取(带后备)
async function getAsset(env, objectName) {
// 尝试直接获取
let object = await env.ASSETS.get(objectName);
// 尝试 index.html 后备
if (!object && !objectName.endsWith('index.html')) {
const altPath = objectName.endsWith('/')
? `${objectName}index.html`
: `${objectName}/index.html`;
object = await env.ASSETS.get(altPath);
}
return object;
}
// 图片代理(优化/转换)
async function handleImageProxy(request) {
const url = new URL(request.url);
const src = url.searchParams.get('src');
if (!src) return new Response('Missing src param', { status: 400 });
// 获取原始图片
const imageResponse = await fetch(src);
// 图片优化(使用 Cloudflare Images)
const transformedUrl = `https://imagedelivery.net/ACCOUNT_HASH/${encodeURIComponent(src)}/resize=width:800`;
return Response.redirect(transformedUrl, 302);
}
// API 处理
async function handleAPI(request, env) {
const url = new URL(request.url);
const path = url.pathname.replace('/api/', '');
// 示例:获取访问统计
if (path === 'analytics') {
const data = { visits: 1234, users: 567 };
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=60'
}
});
}
return new Response('API endpoint not found', { status: 404 });
}
// SEO 机器人处理
async function handleBotRequest(request) {
// 特别为机器人提供优化内容
const url = new URL(request.url);
// 从数据库获取预渲染内容
const prerendered = await env.DB.prepare(
"SELECT content FROM prerender WHERE path = ?"
).bind(url.pathname).first();
if (prerendered) {
return new Response(prerendered.content, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
'X-Robots-Tag': 'index, follow'
}
});
}
// 回退到正常处理
return handleRequest(request);
}
// ===== 实用工具函数 =====
function shouldCache(request, response) {
// 不缓存个性化内容
if (request.headers.get('Cookie')) return false;
// 只缓存成功响应
if (response.status !== 200) return false;
// 检查内容类型
const contentType = response.headers.get('Content-Type') || '';
return contentType.startsWith('text/') ||
contentType.startsWith('image/') ||
contentType.startsWith('font/');
}
function isSearchBot(request) {
const ua = request.headers.get('User-Agent') || '';
return /Googlebot|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot/i.test(ua);
}
// ...其他辅助函数(getContentType, getCacheControl等)...
部署工作流程
初始设置:
# 安装 CLI npm install -g wrangler # 登录 Cloudflare wrangler login # 创建 R2 存储桶 wrangler r2 bucket create hugo-assets-prod # 创建 D1 数据库(可选) wrangler d1 create hugo-db
部署脚本
deploy.sh
:#!/bin/bash # 配置环境 ENV=${1:-staging} BUCKET_NAME="hugo-assets-$ENV" # 生成 Hugo 网站 hugo --minify --environment $ENV # 上传到 R2 echo "Uploading to $BUCKET_NAME..." wrangler r2 object put $BUCKET_NAME/ --directory ./public --recursive # 部署 Worker echo "Deploying Worker..." wrangler deploy --env $ENV # 预热缓存 echo "Preheating cache..." curl -X POST "https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/cache/purge" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data '{"files":["https://yourdomain.com/"]}' echo "Deployment complete for $ENV environment!"
运行部署:
# 部署到预发布环境 ./deploy.sh staging # 部署到生产环境 ./deploy.sh production
关键概念总结
文件位置:
worker.js
和wrangler.toml
在本地 Hugo 项目根目录- 这些文件通过
wrangler deploy
上传到 Cloudflare Workers 平台 - Hugo 生成的
public/
内容上传到 R2 存储桶
高级功能:
- 多环境支持(生产/预发布)
- 边缘缓存优化
- 图片动态优化
- SEO 机器人特殊处理
- API 端点支持
- 数据库集成(D1)
- 智能错误处理
这种架构完全消除了文件数量限制,同时提供了企业级的功能集,包括缓存优化、多环境部署、SEO 增强和动态内容处理,完全利用 Cloudflare 边缘网络的能力。
终极增强版 Cloudflare Workers Hugo 部署方案
下面我将创建一个超越官方文档的终极增强版解决方案,整合了 Cloudflare Workers 的所有高级功能,并添加了创新的边缘计算能力。 这个版本将包含:
完整的类型支持(TypeScript)
多环境配置(生产、预发布、开发)
边缘缓存策略(HTML、静态资源、API)
智能重定向(包括国际化重定向)
安全头设置(CSP、HSTS等)
图片优化(通过Cloudflare Images或Image Resizing)
边缘函数(包括A/B测试、个性化内容)
分析跟踪(边缘分析)
错误处理与自定义404
预渲染(针对SEO爬虫)
API路由代理
键值存储(KV)集成
D1数据库集成
队列(Queues)集成
定时任务(Cron Triggers)
环境变量加密(secrets)
日志记录(Logpush)
回源请求(回退到源站)
安全防护(防火墙规则、Bot管理)
边缘计算优化(减少延迟)
我们将分别创建 wrangler.toml
和 worker.ts
(使用TypeScript)文件。
终极版 wrangler.toml
name = "hugo-ultimate-edge"
main = "src/worker.ts"
compatibility_date = "2024-06-25"
node_compat = true
workers_dev = false
# ===== 核心存储 =====
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "hugo-assets-ultimate"
# ===== 高级数据库 =====
[[d1_databases]]
binding = "DB"
database_name = "hugo-db-ultimate"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
preview_database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
[[d1_databases]]
binding = "ANALYTICS_DB"
database_name = "hugo-analytics"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# ===== KV 命名空间 =====
[[kv_namespaces]]
binding = "CACHE"
id = "xxxxxxxxxxxxxxxxxxxxxxxx"
[[kv_namespaces]]
binding = "CONFIG"
id = "xxxxxxxxxxxxxxxxxxxxxxxx"
[[kv_namespaces]]
binding = "SESSION"
id = "xxxxxxxxxxxxxxxxxxxxxxxx"
# ===== 队列系统 =====
[[queues]]
binding = "ANALYTICS_QUEUE"
queue_name = "analytics-queue"
[[queues]]
binding = "IMAGE_PROCESSING_QUEUE"
queue_name = "image-processing-queue"
# ===== AI 集成 =====
[[ai.bindings]]
name = "AI"
type = "text_embeddings"
# ===== 服务绑定 =====
[[services]]
binding = "AUTH_SERVICE"
service = "auth-service"
environment = "production"
# ===== 向量存储 =====
[[vectorize]]
binding = "CONTENT_VECTOR"
index_name = "hugo-content-index"
# ===== 多环境配置 =====
[env.production]
name = "hugo-prod"
routes = [
{ pattern = "yourdomain.com/*", zone_id = "your-zone-id" },
{ pattern = "*.yourdomain.com/*", zone_id = "your-zone-id" }
]
vars = {
ENVIRONMENT = "production",
CDN_HOST = "https://cdn.yourdomain.com",
SENTRY_DSN = "https://xxxxxxxx@sentry.io/xxxxxx"
}
[env.staging]
name = "hugo-staging"
vars = {
ENVIRONMENT = "staging",
CDN_HOST = "https://staging-cdn.yourdomain.com"
}
[env.preview]
name = "hugo-preview"
vars = {
ENVIRONMENT = "preview",
CDN_HOST = "https://preview-cdn.yourdomain.com"
}
# ===== 构建配置 =====
[build]
command = "npm run build"
watch_dir = "src"
out_dir = "dist"
# ===== 部署钩子 =====
[hooks]
prebuild = "hugo --minify --cleanDestinationDir"
postbuild = "wrangler r2 object put hugo-assets-ultimate/ --directory ./public --recursive"
# ===== 定时任务 =====
[triggers]
crons = [
"0 0 * * *", # 每日午夜执行
"0 */6 * * *" # 每6小时执行
]
# ===== 分析引擎 =====
[analytics_engine_datasets]
binding = "ANALYTICS"
dataset = "hugo_analytics"
# ===== 安全设置 =====
[unsafe]
bindings = [
{ name = "SECURE_CONFIG", type = "secret_text" }
]
# ===== 性能优化 =====
[limits]
cpu_ms = 50
memory = 128
终极版 src/worker.ts
(TypeScript 实现)
// ===== 类型定义 =====
interface Env {
// 存储
ASSETS: R2Bucket;
CACHE: KVNamespace;
CONFIG: KVNamespace;
SESSION: KVNamespace;
// 数据库
DB: D1Database;
ANALYTICS_DB: D1Database;
// 队列
ANALYTICS_QUEUE: Queue;
IMAGE_PROCESSING_QUEUE: Queue;
// AI
AI: any;
// 向量
CONTENT_VECTOR: VectorizeIndex;
// 分析
ANALYTICS: AnalyticsEngineDataset;
// 环境变量
ENVIRONMENT: "production" | "staging" | "preview";
CDN_HOST: string;
SENTRY_DSN?: string;
// 服务
AUTH_SERVICE: Fetcher;
// 安全
SECURE_CONFIG: string;
}
// 请求上下文
interface RequestContext {
request: Request;
env: Env;
ctx: ExecutionContext;
user?: UserSession;
geo?: {
country: string;
city: string;
latitude: string;
longitude: string;
};
}
// 用户会话
interface UserSession {
id: string;
role: "guest" | "user" | "admin";
preferences: {
theme: "light" | "dark";
language: string;
};
}
// ===== 核心中间件系统 =====
type Middleware = (ctx: RequestContext) => Promise<Response | void>;
const createMiddlewarePipeline = (middlewares: Middleware[]) => {
return async (ctx: RequestContext): Promise<Response> => {
for (const middleware of middlewares) {
const result = await middleware(ctx);
if (result instanceof Response) return result;
}
// 默认处理
return handleRequest(ctx);
};
};
// ===== 中间件集合 =====
const middlewares: Middleware[] = [
// 1. 错误捕获
async (ctx) => {
try {
return;
} catch (err) {
return handleError(ctx, err);
}
},
// 2. 地理位置检测
async (ctx) => {
ctx.geo = {
country: ctx.request.cf?.country || "XX",
city: ctx.request.cf?.city || "Unknown",
latitude: ctx.request.cf?.latitude || "0",
longitude: ctx.request.cf?.longitude || "0",
};
},
// 3. 用户会话管理
async (ctx) => {
const sessionId = ctx.request.headers.get("Cookie")?.match(/session=([^;]+)/)?.[1];
if (sessionId) {
const sessionData = await ctx.env.SESSION.get(sessionId, "json");
if (sessionData) {
ctx.user = sessionData as UserSession;
}
}
},
// 4. 安全防护
async (ctx) => {
const url = new URL(ctx.request.url);
// 阻止已知恶意请求
if (await isMaliciousRequest(ctx)) {
return new Response("Access Denied", { status: 403 });
}
// API 速率限制
if (url.pathname.startsWith("/api/")) {
if (await isRateLimited(ctx)) {
return new Response("Too Many Requests", { status: 429 });
}
}
},
// 5. 边缘缓存检查
async (ctx) => {
if (ctx.request.method === "GET") {
const cache = caches.default;
const cacheKey = new Request(ctx.request.url, ctx.request);
const cachedResponse = await cache.match(cacheKey);
if (cachedResponse) {
// 添加缓存命中头
const headers = new Headers(cachedResponse.headers);
headers.set("X-Edge-Cache", "HIT");
return new Response(cachedResponse.body, {
status: cachedResponse.status,
headers
});
}
}
},
// 6. A/B 测试路由
async (ctx) => {
const abTest = await getABTestConfig(ctx);
if (abTest) {
const variant = selectABTestVariant(abTest, ctx);
if (variant) {
const url = new URL(ctx.request.url);
url.pathname = variant.path;
return Response.redirect(url.toString(), 302);
}
}
}
];
// ===== 主请求处理器 =====
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const context: RequestContext = { request, env, ctx };
const pipeline = createMiddlewarePipeline(middlewares);
return pipeline(context);
},
// 定时任务处理器
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
switch (event.cron) {
case "0 0 * * *":
await runDailyTasks(env);
break;
case "0 */6 * * *":
await runImageProcessing(env);
break;
}
},
// 队列处理器
async queue(batch: MessageBatch<Error>, env: Env): Promise<void> {
if (batch.queue === "analytics-queue") {
await processAnalyticsBatch(batch, env);
} else if (batch.queue === "image-processing-queue") {
await processImageBatch(batch, env);
}
}
};
// ===== 核心请求处理 =====
async function handleRequest(ctx: RequestContext): Promise<Response> {
const { request, env } = ctx;
const url = new URL(request.url);
// API 路由
if (url.pathname.startsWith('/api/')) {
return handleApiRequest(ctx);
}
// 图像处理路由
if (url.pathname.startsWith('/_image/')) {
return handleImageProcessing(ctx);
}
// 认证路由
if (url.pathname.startsWith('/_auth/')) {
return handleAuthRequest(ctx);
}
// AI 助手路由
if (url.pathname.startsWith('/_ai/')) {
return handleAIRequest(ctx);
}
// 静态资源服务
return serveStaticAsset(ctx);
}
// ===== 增强功能模块 =====
// 1. 智能静态资源服务
async function serveStaticAsset(ctx: RequestContext): Promise<Response> {
const { request, env } = ctx;
const url = new URL(request.url);
let pathname = url.pathname === '/' ? '/index.html' : url.pathname;
// 移除开头的斜杠
let objectName = pathname.startsWith('/') ? pathname.substring(1) : pathname;
// 尝试获取优化版本
const optimizedAsset = await getOptimizedAsset(ctx, objectName);
if (optimizedAsset) return optimizedAsset;
// 获取原始资源
const object = await env.ASSETS.get(objectName);
// 处理多语言
if (!object && ctx.user?.preferences.language) {
const langPath = `${ctx.user.preferences.language}/${objectName}`;
const langObject = await env.ASSETS.get(langPath);
if (langObject) return buildAssetResponse(langObject, langPath, ctx);
}
// 处理 404
if (!object) {
return handleNotFound(ctx);
}
// 构建响应
const response = buildAssetResponse(object, objectName, ctx);
// 添加到缓存
if (shouldCache(request, response)) {
const cache = caches.default;
const cacheKey = new Request(request.url, request);
ctx.ctx.waitUntil(cache.put(cacheKey, response.clone()));
}
// 记录分析
ctx.ctx.waitUntil(logRequest(ctx, response));
return response;
}
// 2. AI 增强搜索
async function handleAISearch(ctx: RequestContext): Promise<Response> {
const { request } = ctx;
const url = new URL(request.url);
const query = url.searchParams.get('q') || '';
// 生成查询向量
const queryVector = await ctx.env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: query
});
// 向量搜索
const vectorResults = await ctx.env.CONTENT_VECTOR.query(queryVector.data[0], {
topK: 5,
returnValues: true,
returnMetadata: true
});
// 获取相关内容
const results = await Promise.all(
vectorResults.matches.map(async match => {
const contentId = match.metadata?.contentId;
if (!contentId) return null;
const content = await ctx.env.DB.prepare(
"SELECT title, excerpt, path FROM content WHERE id = ?"
).bind(contentId).first();
return {
id: contentId,
title: content.title,
excerpt: content.excerpt,
url: content.path,
score: match.score
};
})
);
// 过滤无效结果
const validResults = results.filter(Boolean);
return new Response(JSON.stringify({ query, results: validResults }), {
headers: { 'Content-Type': 'application/json' }
});
}
// 3. 实时图像处理
async function handleImageProcessing(ctx: RequestContext): Promise<Response> {
const { request, env } = ctx;
const url = new URL(request.url);
// 解析参数
const src = url.searchParams.get('src');
const width = parseInt(url.searchParams.get('w') || '0');
const height = parseInt(url.searchParams.get('h') || '0');
const quality = parseInt(url.searchParams.get('q') || '80');
if (!src) return new Response('Missing src parameter', { status: 400 });
// 检查缓存
const cacheKey = `image:${src}:${width}x${height}:${quality}`;
const cachedImage = await env.CACHE.get(cacheKey, { type: 'arrayBuffer' });
if (cachedImage) {
return new Response(cachedImage, {
headers: {
'Content-Type': 'image/webp',
'X-Image-Cache': 'HIT'
}
});
}
// 获取原始图像
const imageResponse = await fetch(src);
if (!imageResponse.ok) {
return new Response('Image not found', { status: 404 });
}
const imageData = await imageResponse.arrayBuffer();
// 处理图像(使用 Cloudflare 图像优化)
const optimizedImage = await processImage(imageData, { width, height, quality });
// 缓存结果
ctx.ctx.waitUntil(env.CACHE.put(cacheKey, optimizedImage, {
expirationTtl: 60 * 60 * 24 * 7 // 缓存1周
}));
return new Response(optimizedImage, {
headers: {
'Content-Type': 'image/webp',
'X-Image-Cache': 'MISS'
}
});
}
// 4. 边缘个性化
async function personalizeContent(content: string, ctx: RequestContext): Promise<string> {
if (!ctx.user) return content;
// 1. 个性化推荐
if (ctx.user.role === 'admin') {
content = content.replace('<!--ADMIN_CONTENT-->', generateAdminDashboard());
}
// 2. 主题适配
if (ctx.user.preferences.theme === 'dark') {
content = content.replace('<body>', '<body class="dark-mode">');
}
// 3. 基于位置的内容
if (ctx.geo) {
const locationContent = await getLocationBasedContent(ctx.geo.country);
content = content.replace('<!--LOCAL_CONTENT-->', locationContent);
}
// 4. AI 生成摘要
if (shouldGenerateSummary()) {
const summary = await generateAISummary(content);
content = content.replace('<!--AI_SUMMARY-->', summary);
}
return content;
}
// 5. 渐进式 Web App 功能
function injectPWAFeatures(content: string): string {
// 注入 Service Worker
const swScript = `
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
`;
// 添加 Web App Manifest
const manifestLink = '<link rel="manifest" href="/manifest.json">';
return content
.replace('</head>', `${manifestLink}${swScript}</head>`)
.replace('<body>', '<body><div id="app-shell">');
}
// 6. 实时分析
async function logRequest(ctx: RequestContext, response: Response): Promise<void> {
const { request, env, user, geo } = ctx;
// 基本分析数据
const analyticsData = {
timestamp: Date.now(),
method: request.method,
path: request.url,
status: response.status,
userAgent: request.headers.get('User-Agent'),
country: geo?.country,
city: geo?.city,
userId: user?.id || 'anonymous',
responseSize: Number(response.headers.get('Content-Length') || 0,
cacheStatus: response.headers.get('X-Edge-Cache') || 'MISS'
};
// 发送到分析引擎
env.ANALYTICS.writeDataPoint({
blobs: [
analyticsData.userId,
analyticsData.path,
analyticsData.userAgent,
analyticsData.country,
analyticsData.city,
analyticsData.cacheStatus
],
doubles: [
analyticsData.status,
analyticsData.responseSize
],
indexes: [
analyticsData.userId.substring(0, 8)
]
});
// 发送到队列进行详细处理
await env.ANALYTICS_QUEUE.send(analyticsData);
}
// 7. 安全防护增强
async function isMaliciousRequest(ctx: RequestContext): Promise<boolean> {
const { request } = ctx;
// 1. 检查 Cloudflare 威胁评分
if (request.cf?.threatScore && request.cf.threatScore > 10) {
return true;
}
// 2. SQL 注入检测
const sqlKeywords = ['SELECT', 'INSERT', 'DELETE', 'DROP', 'UNION'];
const urlParams = request.url.toLowerCase();
if (sqlKeywords.some(keyword => urlParams.includes(keyword.toLowerCase()))) {
return true;
}
// 3. XSS 攻击检测
const xssPatterns = ['<script>', 'javascript:', 'onerror='];
if (xssPatterns.some(pattern => urlParams.includes(pattern))) {
return true;
}
// 4. 速率限制检查(基于IP)
const ip = request.headers.get('CF-Connecting-IP') || '';
const key = `rate_limit:${ip}`;
const count = (await ctx.env.CACHE.get(key)) || 0;
if (count > 100) { // 100 请求/分钟
return true;
}
// 更新计数器
await ctx.env.CACHE.put(key, (parseInt(count) + 1).toString(), {
expirationTtl: 60
});
return false;
}
// 8. 定时任务处理
async function runDailyTasks(env: Env): Promise<void> {
// 1. 内容预缓存
const popularContent = await env.DB.prepare(
"SELECT path FROM content ORDER BY views DESC LIMIT 50"
).all();
await Promise.all(popularContent.results.map(async (content) => {
const url = new URL(content.path, env.CDN_HOST);
await fetch(url.toString());
}));
// 2. 数据库维护
await env.DB.prepare(
"DELETE FROM sessions WHERE expires_at < ?"
).bind(Date.now()).run();
// 3. 生成报告
const report = await generateDailyReport(env);
await sendEmailReport(report);
}
// 9. AI 助手功能
async function handleAIRequest(ctx: RequestContext): Promise<Response> {
const { request } = ctx;
const url = new URL(request.url);
const action = url.pathname.split('/')[2];
switch (action) {
case 'summarize':
const text = await request.text();
const summary = await generateAISummary(text);
return new Response(summary);
case 'translate':
const { content, targetLang } = await request.json();
const translation = await translateContent(content, targetLang);
return new Response(translation);
case 'generate':
const prompt = await request.text();
const generatedContent = await generateAIContent(prompt);
return new Response(generatedContent);
default:
return new Response('Invalid AI action', { status: 400 });
}
}
// ===== 实用工具函数 =====
async function buildAssetResponse(object: R2Object, objectName: string, ctx: RequestContext): Promise<Response> {
let body = object.body;
const headers = new Headers();
// 设置内容类型
const contentType = getContentType(objectName);
headers.set('Content-Type', contentType);
// 设置缓存头
headers.set('Cache-Control', getCacheControl(objectName));
// 设置安全头
setSecurityHeaders(headers);
// 个性化 HTML 内容
if (contentType.startsWith('text/html')) {
let htmlContent = await object.text();
// 注入个性化内容
htmlContent = await personalizeContent(htmlContent, ctx);
// 注入 PWA 功能
htmlContent = injectPWAFeatures(htmlContent);
// 重写资源路径
htmlContent = rewriteAssetPaths(htmlContent, ctx.env.CDN_HOST);
body = htmlContent;
}
// 添加缓存标签
headers.set('ETag', object.httpEtag);
return new Response(body, { headers });
}
function setSecurityHeaders(headers: Headers): void {
const securityHeaders = {
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.yourdomain.com; frame-ancestors 'none';",
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'same-site'
};
Object.entries(securityHeaders).forEach(([key, value]) => {
headers.set(key, value);
});
}
// ...其他工具函数(getContentType, getCacheControl等)...
// ===== 错误处理 =====
function handleError(ctx: RequestContext, error: unknown): Response {
const { env } = ctx;
// 记录错误到 Sentry
if (env.SENTRY_DSN) {
ctx.ctx.waitUntil(logErrorToSentry(error, env.SENTRY_DSN));
}
// 返回自定义错误页面
return new Response(`<h1>Application Error</h1><p>${error instanceof Error ? error.message : 'Unknown error'}</p>`, {
status: 500,
headers: { 'Content-Type': 'text/html' }
});
}
async function handleNotFound(ctx: RequestContext): Promise<Response> {
// 尝试获取 404 页面
const notFoundPage = await ctx.env.ASSETS.get('404.html');
if (notFoundPage) {
return new Response(notFoundPage.body, {
status: 404,
headers: { 'Content-Type': 'text/html' }
});
}
// 智能 404 建议
const suggestions = await getContentSuggestions(ctx);
return new Response(`
<h1>Page Not Found</h1>
<p>We couldn't find the page you requested.</p>
${suggestions ? `<h2>You might be interested in:</h2>${suggestions}` : ''}
`, {
status: 404,
headers: { 'Content-Type': 'text/html' }
});
}
架构优势
这个终极版解决方案整合了 Cloudflare Workers 平台的所有高级功能:
多存储系统集成:
- R2 用于静态资源
- KV 用于缓存和配置
- D1 用于结构化数据
- Vectorize 用于向量搜索
边缘计算能力:
- 中间件管道处理
- 边缘AI处理(摘要、翻译、生成)
- 实时图像优化
- 个性化内容交付
高级分析系统:
- 实时请求分析
- Analytics Engine 集成
- 队列处理批量数据
- 定时报告生成
安全增强:
- 综合安全头设置
- 威胁检测系统
- 速率限制防护
- 恶意请求拦截
性能优化:
- 智能边缘缓存
- 资源预取
- 代码拆分
- 渐进式 Web App 支持
开发者体验:
- TypeScript 支持
- 多环境配置
- 自动化部署
- 详细日志记录
部署流程
# 1. 安装依赖
npm install -g wrangler
npm install typescript @cloudflare/workers-types
# 2. 初始化项目
wrangler init --yes
mv worker.js src/worker.ts
# 3. 创建资源
wrangler r2 bucket create hugo-assets-ultimate
wrangler d1 create hugo-db-ultimate
wrangler vectorize create hugo-content-index
# 4. 配置环境变量
echo "SECURE_CONFIG=supersecretvalue" | wrangler secret put SECURE_CONFIG
# 5. 构建和部署
npm run build
wrangler deploy
性能监控仪表板
graph TD A[用户请求] --> B[边缘节点] B --> C{缓存检查} C -->|命中| D[返回缓存响应] C -->|未命中| E[请求处理管道] E --> F[地理位置检测] E --> G[用户会话管理] E --> H[安全防护] E --> I[A/B测试] E --> J[资源获取] J --> K{资源存在?} K -->|是| L[个性化处理] K -->|否| M[404处理] L --> N[响应构建] N --> O[缓存存储] O --> P[返回响应] P --> Q[分析记录]
这个终极版解决方案不仅突破了 Hugo 部署的文件数量限制,还通过 Cloudflare Workers 的高级功能实现了前所未有的边缘计算能力,为静态网站带来了动态、个性化和智能化的用户体验。
文件位置与 TypeScript vs JavaScript 详解
1. 文件位置说明
src/worker.ts
应该存放在项目的以下位置:
your-hugo-project/
├── src/ # TypeScript 源代码目录
│ └── worker.ts # 主 Worker 脚本
├── public/ # Hugo 生成的静态文件
├── wrangler.toml # Cloudflare 配置
└── package.json # 项目依赖配置
2. worker.ts
vs worker.js
区别
特性 | worker.ts (TypeScript) | worker.js (JavaScript) |
---|---|---|
语言类型 | 强类型静态类型语言 | 动态类型脚本语言 |
文件扩展名 | .ts | .js |
类型系统 | 支持类型注解和接口 | 无原生类型支持 |
错误检测 | 编译时类型检查 | 运行时错误发现 |
开发体验 | 智能代码补全、重构支持 | 基本编辑器支持 |
Cloudflare 支持 | 原生支持(通过 Wrangler) | 原生支持 |
编译过程 | 需要转译为 JavaScript | 直接执行 |
模块系统 | ES 模块 + TypeScript 模块 | ES 模块/CommonJS |
类型定义 | 自带类型定义 | 需要 JSDoc 注释 |
示例 | const env: Env = {...} | const env = {...} |
TypeScript 优势详解
1. 类型安全
// 定义环境变量接口
interface Env {
ASSETS: R2Bucket;
DB: D1Database;
ENVIRONMENT: "production" | "staging";
}
// 使用类型安全的环境变量
async function handleRequest(request: Request, env: Env) {
// env 自动补全 ASSETS、DB 等属性
const object = await env.ASSETS.get("index.html");
// 类型错误会在编译时捕获
const count = await env.DB.count(); // 错误:DB 没有 count 方法
}
2. 高级类型特性
// 联合类型
type CacheStrategy = "immutable" | "revalidate" | "off";
// 泛型函数
async function cachedFetch<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json() as Promise<T>;
}
// 类型守卫
function isUser(obj: unknown): obj is User {
return typeof obj === "object" && obj !== null && "id" in obj;
}
3. Cloudflare 类型定义
/// <reference types="@cloudflare/workers-types" />
// 使用 Cloudflare 官方类型
declare global {
interface Request {
cf: IncomingRequestCfProperties;
}
}
// 使用完整类型提示
function handleRequest(request: Request) {
const country = request.cf.country; // 自动补全所有属性
const isMobile = request.cf.isMobile;
}
完整项目结构
hugo-cloudflare-edge/
├── src/ # TypeScript 源代码
│ ├── worker.ts # 主 Worker 脚本
│ ├── middleware/ # 中间件系统
│ │ ├── auth.ts
│ │ ├── cache.ts
│ │ └── security.ts
│ ├── services/ # 业务服务
│ │ ├── image.ts
│ │ ├── ai.ts
│ │ └── analytics.ts
│ └── types/ # 类型定义
│ ├── env.d.ts
│ └── requests.d.ts
├── public/ # Hugo 生成内容
│ ├── index.html
│ ├── css/
│ └── js/
├── scripts/ # 部署脚本
│ ├── deploy.ts
│ └── purge-cache.ts
├── wrangler.toml # Cloudflare 配置
├── package.json # 依赖管理
├── tsconfig.json # TypeScript 配置
└── .eslintrc # 代码规范
配置说明
1. tsconfig.json
{
"compilerOptions": {
"target": "es2022",
"module": "esnext",
"lib": ["esnext"],
"types": ["@cloudflare/workers-types"],
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist"
},
"include": ["src/**/*.ts"]
}
2. package.json
{
"name": "hugo-edge-worker",
"type": "module",
"scripts": {
"build": "tsc",
"dev": "wrangler dev",
"deploy": "npm run build && wrangler deploy",
"preview": "wrangler preview"
},
"dependencies": {
"@cloudflare/workers-types": "^4.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"wrangler": "^3.0.0"
}
}
3. wrangler.toml
(TypeScript 适配版)
name = "hugo-ultimate-edge"
main = "dist/worker.js" # 指向编译后的输出
compatibility_date = "2024-06-25"
[build]
command = "npm run build"
watch_dir = "src"
out_dir = "dist"
# R2 存储
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "hugo-assets-prod"
# TypeScript 特定优化
[unsafe]
bindings = [
{ name = "TS_CONFIG", type = "text" }
]
# 开发环境配置
[dev]
ip = "localhost"
port = 8787
local_protocol = "http"
开发工作流
graph TB A[编写代码] --> B[src/worker.ts] B --> C[TypeScript 编译] C --> D[dist/worker.js] D --> E{wrangler 命令} E -->|dev| F[本地开发] E -->|deploy| G[部署到 Cloudflare] F --> H[实时重载] G --> I[生产环境]
最佳实践建议
类型安全开发:
// 使用严格类型定义 interface UserSession { id: string; expires: Date; role: 'user' | 'admin' | 'guest'; } // 类型安全的会话获取 async function getSession(request: Request, env: Env): Promise<UserSession | null> { const sessionId = getCookie(request, 'session'); if (!sessionId) return null; const session = await env.SESSIONS.get(sessionId, 'json'); return isUserSession(session) ? session : null; }
错误处理增强:
// 自定义错误类型 class EdgeError extends Error { constructor( public readonly status: number, message: string, public readonly context?: Record<string, unknown> ) { super(message); } } // 类型安全的错误处理 try { // ...业务逻辑 } catch (error) { if (error instanceof EdgeError) { return new Response(error.message, { status: error.status }); } return new Response('Internal Server Error', { status: 500 }); }
高级缓存策略:
// 类型安全的缓存接口 interface CacheStrategy { get(key: string): Promise<Response | null>; put(key: string, response: Response): Promise<void>; } // 实现 Cloudflare 缓存策略 class CloudflareCache implements CacheStrategy { async get(key: string): Promise<Response | null> { return caches.default.match(new Request(key)); } async put(key: string, response: Response): Promise<void> { const cache = caches.default; await cache.put(new Request(key), response.clone()); } }
迁移指南(JS 到 TS)
重命名文件:
mv worker.js src/worker.ts
添加类型注解:
- export default { + interface Env { + ASSETS: R2Bucket; + } + + export default { - async fetch(request, env) { + async fetch(request: Request, env: Env) {
安装依赖:
npm install -D typescript @cloudflare/workers-types
配置 TypeScript:
// tsconfig.json { "compilerOptions": { "target": "es2022", "module": "esnext", "strict": true } }
更新部署脚本:
// package.json "scripts": { + "build": "tsc", "deploy": "wrangler deploy" }
性能对比
指标 | TypeScript (worker.ts) | JavaScript (worker.js) |
---|---|---|
冷启动时间 | 相同(编译在部署前完成) | 相同 |
执行性能 | 相同(运行时都是 JS) | 相同 |
开发效率 | ⭐⭐⭐⭐⭐(类型安全) | ⭐⭐⭐(需手动检查) |
错误预防 | ⭐⭐⭐⭐⭐(编译时捕获) | ⭐⭐(运行时发现) |
维护成本 | ⭐⭐⭐⭐(类型即文档) | ⭐⭐(需注释说明) |
团队协作 | ⭐⭐⭐⭐⭐(一致接口) | ⭐⭐⭐(易出错) |
总结
文件位置:
src/worker.ts
放在项目源代码目录中- 编译后生成
dist/worker.js
供 Cloudflare 使用
核心区别:
worker.ts
是 TypeScript 源码,提供类型安全worker.js
是 JavaScript 执行文件- Cloudflare 原生支持两者,但 TypeScript 提供更佳开发体验
终极建议:
graph LR A[新项目] -->|强烈推荐| B[TypeScript] C[现有JS项目] -->|逐步迁移| D[TypeScript] E[简单脚本] -->|可选| F[JavaScript] G[企业级应用] -->|必须| B[TypeScript]
对于 Hugo + Cloudflare Workers 这种复杂集成,强烈推荐使用 TypeScript,它能显著提高代码质量、减少运行时错误,并提供完整的 Cloudflare API 类型支持,使您能够构建真正强大的边缘应用。