Cloudflare Workers + R2部署Hugo终极解决方案

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等)...

部署工作流程

  1. 初始设置

    # 安装 CLI
    npm install -g wrangler
    
    # 登录 Cloudflare
    wrangler login
    
    # 创建 R2 存储桶
    wrangler r2 bucket create hugo-assets-prod
    
    # 创建 D1 数据库(可选)
    wrangler d1 create hugo-db
  2. 部署脚本 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!"
  3. 运行部署

    # 部署到预发布环境
    ./deploy.sh staging
    
    # 部署到生产环境
    ./deploy.sh production

关键概念总结

  1. 文件位置

    • worker.jswrangler.toml本地 Hugo 项目根目录
    • 这些文件通过 wrangler deploy 上传到 Cloudflare Workers 平台
    • Hugo 生成的 public/ 内容上传到 R2 存储桶
  2. 高级功能

    • 多环境支持(生产/预发布)
    • 边缘缓存优化
    • 图片动态优化
    • SEO 机器人特殊处理
    • API 端点支持
    • 数据库集成(D1)
    • 智能错误处理

这种架构完全消除了文件数量限制,同时提供了企业级的功能集,包括缓存优化、多环境部署、SEO 增强和动态内容处理,完全利用 Cloudflare 边缘网络的能力。

终极增强版 Cloudflare Workers Hugo 部署方案

下面我将创建一个超越官方文档的终极增强版解决方案,整合了 Cloudflare Workers 的所有高级功能,并添加了创新的边缘计算能力。 这个版本将包含:

  1. 完整的类型支持(TypeScript)

  2. 多环境配置(生产、预发布、开发)

  3. 边缘缓存策略(HTML、静态资源、API)

  4. 智能重定向(包括国际化重定向)

  5. 安全头设置(CSP、HSTS等)

  6. 图片优化(通过Cloudflare Images或Image Resizing)

  7. 边缘函数(包括A/B测试、个性化内容)

  8. 分析跟踪(边缘分析)

  9. 错误处理与自定义404

  10. 预渲染(针对SEO爬虫)

  11. API路由代理

  12. 键值存储(KV)集成

  13. D1数据库集成

  14. 队列(Queues)集成

  15. 定时任务(Cron Triggers)

  16. 环境变量加密(secrets)

  17. 日志记录(Logpush)

  18. 回源请求(回退到源站)

  19. 安全防护(防火墙规则、Bot管理)

  20. 边缘计算优化(减少延迟)

我们将分别创建 wrangler.tomlworker.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 平台的所有高级功能:

  1. 多存储系统集成

    • R2 用于静态资源
    • KV 用于缓存和配置
    • D1 用于结构化数据
    • Vectorize 用于向量搜索
  2. 边缘计算能力

    • 中间件管道处理
    • 边缘AI处理(摘要、翻译、生成)
    • 实时图像优化
    • 个性化内容交付
  3. 高级分析系统

    • 实时请求分析
    • Analytics Engine 集成
    • 队列处理批量数据
    • 定时报告生成
  4. 安全增强

    • 综合安全头设置
    • 威胁检测系统
    • 速率限制防护
    • 恶意请求拦截
  5. 性能优化

    • 智能边缘缓存
    • 资源预取
    • 代码拆分
    • 渐进式 Web App 支持
  6. 开发者体验

    • 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[生产环境]

最佳实践建议

  1. 类型安全开发

    // 使用严格类型定义
    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;
    }
  2. 错误处理增强

    // 自定义错误类型
    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 });
    }
  3. 高级缓存策略

    // 类型安全的缓存接口
    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)

  1. 重命名文件

    mv worker.js src/worker.ts
  2. 添加类型注解

    - export default {
    + interface Env {
    +   ASSETS: R2Bucket;
    + }
    +
    + export default {
    -   async fetch(request, env) {
    +   async fetch(request: Request, env: Env) {
    
  3. 安装依赖

    npm install -D typescript @cloudflare/workers-types
  4. 配置 TypeScript

    // tsconfig.json
    {
      "compilerOptions": {
        "target": "es2022",
        "module": "esnext",
        "strict": true
      }
    }
  5. 更新部署脚本

    // package.json
    "scripts": {
    +  "build": "tsc",
      "deploy": "wrangler deploy"
    }
    

性能对比

指标TypeScript (worker.ts)JavaScript (worker.js)
冷启动时间相同(编译在部署前完成)相同
执行性能相同(运行时都是 JS)相同
开发效率⭐⭐⭐⭐⭐(类型安全)⭐⭐⭐(需手动检查)
错误预防⭐⭐⭐⭐⭐(编译时捕获)⭐⭐(运行时发现)
维护成本⭐⭐⭐⭐(类型即文档)⭐⭐(需注释说明)
团队协作⭐⭐⭐⭐⭐(一致接口)⭐⭐⭐(易出错)

总结

  1. 文件位置

    • src/worker.ts 放在项目源代码目录中
    • 编译后生成 dist/worker.js 供 Cloudflare 使用
  2. 核心区别

    • worker.ts 是 TypeScript 源码,提供类型安全
    • worker.js 是 JavaScript 执行文件
    • Cloudflare 原生支持两者,但 TypeScript 提供更佳开发体验
  3. 终极建议

      graph LR
      A[新项目] -->|强烈推荐| B[TypeScript]
      C[现有JS项目] -->|逐步迁移| D[TypeScript]
      E[简单脚本] -->|可选| F[JavaScript]
      G[企业级应用] -->|必须| B[TypeScript]
    

对于 Hugo + Cloudflare Workers 这种复杂集成,强烈推荐使用 TypeScript,它能显著提高代码质量、减少运行时错误,并提供完整的 Cloudflare API 类型支持,使您能够构建真正强大的边缘应用。