{"owner":"vikiboss","repo":"60s","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","COPILOT.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n60s API is a comprehensive API collection providing news, trending topics, and utility services. Built with Deno and Oak framework, it supports multiple runtime environments (Deno, Node.js, Bun) and deployment platforms (Docker, Cloudflare Workers).\n\n## Development Commands\n\n### Running the application\n```bash\n# Development mode (port 4398)\npnpm run dev\n\n# Production mode (port 4398)\npnpm run start\n\n# Docker\npnpm run docker:build\npnpm run docker:run\n```\n\n### Common operations\n```bash\n# Update all lockfiles\npnpm run update-lockfile\n\n# Release new version (bumps version and creates git tag)\npnpm run release\n```\n\n## Architecture\n\n### Core Structure\n- **Entry points**: `deno.ts`, `node.ts`, `bun.ts`, `cf-worker.ts` for different runtimes\n- **Main app**: `src/app.ts` - Oak application with middleware setup\n- **Routing**: `src/router.ts` - centralized route definitions with `/v2` prefix\n- **Modules**: `src/modules/` - individual API service implementations\n- **Middlewares**: `src/middlewares/` - cross-cutting concerns (CORS, error handling, encoding)\n\n### Key Components\n- **Common utilities**: `src/common.ts` - shared functions for JSON building, parameter extraction, date formatting\n- **Configuration**: `src/config.ts` - environment-based settings\n- **Encoding middleware**: Handles `encoding` query parameter for response format transformation\n\n## Development Guidelines\n\n### Module Development Pattern\n\nEach API endpoint follows a consistent module pattern:\n\n1. **Create service class** in `src/modules/[name].module.ts`\n2. **Implement `handle()` method** returning Oak RouterMiddleware\n3. **Handle different encoding formats** in the middleware (see Response Formats below)\n4. **Register route** in `src/router.ts`\n5. **Import and add** to router configuration\n\nExample structure:\n```typescript\nexport class MyModule {\n  async handle(): RouterMiddleware<any> {\n    return async (ctx) => {\n      const encoding = ctx.state.encoding\n      const data = await this.fetchData()\n\n      if (encoding === 'text') {\n        ctx.response.body = this.formatAsText(data)\n      } else if (encoding === 'markdown') {\n        ctx.response.body = this.formatAsMarkdown(data)\n      } else {\n        ctx.response.body = Common.buildJson(data)\n      }\n    }\n  }\n}\n```\n\n### Response Format Standards\n\n**IMPORTANT**: Unless specifically noted otherwise, ALL APIs MUST support these three encoding formats:\n\n- `json` (default) - structured JSON response via `Common.buildJson()`\n- `text` - plain text format for terminal/script usage\n- `markdown` - markdown formatted text for documentation/display\n\nSpecial formats (only when explicitly needed):\n- `image` - redirect to image URL\n- `image-proxy` - proxied image content\n- `html` - HTML encoded output\n\n### Time Handling Standards\n\n**ALWAYS use `dayjs` for time operations:**\n\n```typescript\nimport { dayjs, TZ_SHANGHAI } from './common.ts'\n\n// Get current time in Shanghai timezone\nconst now = dayjs().tz(TZ_SHANGHAI)\n\n// Format date\nconst dateStr = now.format('YYYY-MM-DD')\n\n// Parse and convert timezone\nconst parsedDate = dayjs(timestamp).tz(TZ_SHANGHAI)\n```\n\n**Default timezone**: Always use `TZ_SHANGHAI` (`Asia/Shanghai`) for all time operations unless explicitly specified otherwise.\n\n### Common Patterns\n\n**Response Building:**\n```typescript\n// Success response\nctx.response.body = Common.buildJson(data)\n\n// Error response with custom message\nctx.response.status = 400\nctx.response.body = Common.buildJson(null, 400, 'Custom error message')\n\n// Require arguments\nif (!param) {\n  Common.requireArguments('paramName', ctx.response)\n  return\n}\n```\n\n**Parameter Handling:**\n```typescript\n// Query parameters\nconst param = ctx.request.url.searchParams.get('param')\n\n// Support both query and POST body (for large params)\nconst largeParam = await Common.getParam('param', ctx.request, true)\n```\n\n**Web Scraping:**\n```typescript\n// Always use Common.chromeUA for User-Agent\nconst response = await fetch(url, {\n  headers: { 'User-Agent': Common.chromeUA }\n})\n```\n\n**Caching:**\n```typescript\n// Implement caching for expensive operations\nconst cache = new Map<string, { data: any; timestamp: number }>()\nconst CACHE_TTL = 30 * 60 * 1000 // 30 minutes\n\n// Check cache before fetching\nconst cached = cache.get(key)\nif (cached && Date.now() - cached.timestamp < CACHE_TTL) {\n  return cached.data\n}\n```\n\n### Code Quality Standards\n\n- **Type Safety**: Use TypeScript types, avoid `any` when possible\n- **Error Handling**: Always handle fetch errors and invalid responses\n- **Validation**: Validate required parameters using `Common.requireArguments()`\n- **Consistency**: Follow existing code patterns in `src/modules/`\n- **Documentation**: Add JSDoc comments for complex functions\n- **Testing**: Test all three encoding formats (json/text/markdown) when adding new APIs\n\n### Useful Utilities\n\nFrom `src/common.ts`:\n- `Common.buildJson()` - Standard JSON response builder\n- `Common.requireArguments()` - Parameter validation\n- `dayjs` / `TZ_SHANGHAI` - Time operations (prefer over `Common.localeDate()`/`Common.localeTime()`)\n- `Common.randomInt()` / `Common.randomItem()` - Random utilities\n- `Common.md5()` - MD5 hashing\n- `Common.qs()` - Query string builder\n- `Common.tryRepoUrl()` - GitHub CDN fallback fetcher\n\n**Note**: `Common.localeDate()` and `Common.localeTime()` are legacy utilities. For new code, always use `dayjs` with `TZ_SHANGHAI` timezone as shown in the Time Handling Standards section.\n","COPILOT.md":"# Copilot 指导文档\n\n本文档为 GitHub Copilot 提供项目编码指导，帮助生成符合项目规范的代码。\n\n## 项目概述\n\n60s API 是一个综合性 API 集合，提供新闻、热搜、工具类等多种服务。项目使用 Deno + Oak 框架构建，支持多种运行时环境（Deno、Node.js、Bun）和部署平台（Docker、Cloudflare Workers）。\n\n## 开发命令\n\n```bash\n# 开发模式 (端口 4398)\npnpm run dev\n\n# 生产模式\npnpm run start\n\n# Docker 构建和运行\npnpm run docker:build\npnpm run docker:run\n```\n\n## 项目架构\n\n### 目录结构\n```\nsrc/\n├── app.ts              # Oak 应用入口，中间件配置\n├── router.ts           # 集中式路由定义（/v2 前缀）\n├── common.ts           # 公共工具函数\n├── config.ts           # 环境配置\n├── middlewares/        # 中间件\n│   ├── cors.ts         # 跨域处理\n│   ├── encoding.ts     # 响应格式处理\n│   └── ...\n└── modules/            # API 模块实现\n    ├── baidu.module.ts\n    ├── weibo.module.ts\n    ├── quark.module.ts\n    └── ...\n```\n\n### 运行时入口\n- `deno.ts` - Deno 运行时\n- `node.ts` - Node.js 运行时\n- `bun.ts` - Bun 运行时\n- `cf-worker.ts` - Cloudflare Workers\n\n## 模块开发规范\n\n### 1. 创建新模块\n\n在 `src/modules/` 目录下创建 `[name].module.ts` 文件：\n\n```typescript\nimport { Common } from '../common.ts'\n\nimport type { RouterMiddleware } from '@oak/oak'\n\n// 定义接口类型\ninterface DataItem {\n  id: string\n  title: string\n  // ... 其他字段使用 snake_case 命名\n}\n\nclass ServiceExample {\n  handle(): RouterMiddleware<'/example'> {\n    return async (ctx) => {\n      const data = await this.#fetch()\n\n      switch (ctx.state.encoding) {\n        case 'text':\n          ctx.response.body = `示例标题\\n\\n${data\n            .slice(0, 20)\n            .map((e, i) => `${i + 1}. ${e.title}`)\n            .join('\\n')}`\n          break\n\n        case 'markdown':\n          ctx.response.body = `# 示例标题\\n\\n${data\n            .slice(0, 20)\n            .map((e, i) => `### ${i + 1}. ${e.title}\\n\\n---\\n`)\n            .join('\\n')}`\n          break\n\n        case 'json':\n        default:\n          ctx.response.body = Common.buildJson(data)\n          break\n      }\n    }\n  }\n\n  async #fetch(): Promise<DataItem[]> {\n    const api = 'https://example.com/api'\n    \n    const response = await fetch(api, {\n      headers: {\n        'User-Agent': Common.chromeUA,\n      },\n    })\n    \n    const json = await response.json()\n    // 处理并返回数据，确保字段使用 snake_case\n    return json.data.map((item: any) => ({\n      id: item.id,\n      title: item.title,\n      // 转换驼峰为下划线\n      publish_time: item.publishTime,\n      source_name: item.sourceName,\n    }))\n  }\n}\n\nexport const serviceExample = new ServiceExample()\n```\n\n### 2. 注册路由\n\n在 `src/router.ts` 中添加：\n\n```typescript\n// 1. 导入模块\nimport { serviceExample } from './modules/example.module.ts'\n\n// 2. 注册路由\nappRouter.get('/example', serviceExample.handle())\n```\n\n## 编码规范\n\n### 响应格式\n\n所有 API 必须支持三种编码格式：\n- `json`（默认）- 结构化 JSON 响应，通过 `Common.buildJson()` 构建\n- `text` - 纯文本格式，用于终端/脚本\n- `markdown` - Markdown 格式，用于文档展示\n\n### JSON 字段命名\n\n**重要**：返回的 JSON 字段必须使用 `snake_case` 格式：\n\n```typescript\n// ✅ 正确\n{\n  id: \"123\",\n  title: \"标题\",\n  publish_time: 1234567890,\n  source_name: \"来源\",\n  comment_count: 100,\n  like_count: 50\n}\n\n// ❌ 错误（驼峰命名）\n{\n  id: \"123\",\n  title: \"标题\",\n  publishTime: 1234567890,  // 应为 publish_time\n  sourceName: \"来源\",        // 应为 source_name\n  commentCount: 100,         // 应为 comment_count\n  likeCount: 50              // 应为 like_count\n}\n```\n\n### 数据完整性\n\n**重要**：返回的 JSON 应尽可能提供完整、有帮助的信息：\n\n1. **不要截断数据**：如有完整内容（如 `content`），应清理 HTML 后返回全文\n2. **包含所有有用字段**：\n   - 基本信息：`id`, `title`, `summary`, `content`\n   - 来源信息：`source_name`, `origin_source_name`, `author`\n   - 时间信息：`publish_time`, `grab_time`, `modify_time`\n   - 媒体资源：`cover`, `images[]`, `videos[]`\n   - 分类标签：`category[]`, `tags[]`\n   - 互动数据：`comment_count`, `like_count`, `share_count`, `favorite_count`\n   - 链接信息：`original_url`\n\n3. **嵌套对象使用 snake_case**：\n```typescript\n{\n  author: {\n    name: \"作者名\",\n    desc: \"简介\",\n    icon: \"头像URL\",\n    follower_count: 10000\n  },\n  images: [{\n    url: \"图片URL\",\n    width: 640,\n    height: 480,\n    type: \"jpg\",\n    description: \"图片描述\"\n  }]\n}\n```\n\n4. **HTML 内容清理**：\n```typescript\n#cleanHtml(html: string): string {\n  return html\n    .replace(/<!--\\{(img|video):\\d+\\}-->/g, '') // 移除占位符\n    .replace(/<[^>]+>/g, '')                     // 移除 HTML 标签\n    .replace(/&nbsp;/g, ' ')                     // 处理实体\n    .replace(/\\s+/g, ' ')                        // 清理空白\n    .trim()\n}\n```\n\n### 时间处理\n\n使用 `dayjs` 处理时间：\n\n```typescript\nimport { dayjs, TZ_SHANGHAI } from './common.ts'\n\n// 当前上海时间\nconst now = dayjs().tz(TZ_SHANGHAI)\n\n// 格式化\nconst dateStr = now.format('YYYY-MM-DD')\n\n// 解析时间戳\nconst time = dayjs(timestamp).tz(TZ_SHANGHAI)\n```\n\n### HTTP 请求\n\n```typescript\n// 始终使用 Common.chromeUA 作为 User-Agent\nconst response = await fetch(url, {\n  headers: { 'User-Agent': Common.chromeUA }\n})\n```\n\n### 参数验证\n\n```typescript\n// 获取参数\nconst param = ctx.request.url.searchParams.get('param')\n\n// 必需参数验证\nif (!param) {\n  Common.requireArguments('param', ctx.response)\n  return\n}\n```\n\n### 错误处理\n\n```typescript\n// 成功响应\nctx.response.body = Common.buildJson(data)\n\n// 错误响应\nctx.response.status = 400\nctx.response.body = Common.buildJson(null, 400, '错误信息')\n```\n\n## 常用工具函数\n\n| 函数 | 说明 |\n|------|------|\n| `Common.buildJson(data, code?, message?)` | 构建标准 JSON 响应 |\n| `Common.requireArguments(name, response)` | 参数验证 |\n| `Common.chromeUA` | Chrome User-Agent 字符串 |\n| `Common.localeTime(timestamp)` | 格式化时间 |\n| `Common.randomInt(min, max)` | 随机整数 |\n| `Common.randomItem(array)` | 随机数组元素 |\n| `Common.md5(str)` | MD5 哈希 |\n| `Common.qs(obj)` | 构建查询字符串 |\n\n## 代码质量\n\n- **类型安全**：使用 TypeScript 类型，避免使用 `any`\n- **错误处理**：处理 fetch 错误和无效响应\n- **参数验证**：使用 `Common.requireArguments()` 验证必需参数\n- **代码一致性**：遵循 `src/modules/` 中现有代码模式\n- **注释文档**：为复杂函数添加 JSDoc 注释\n- **测试覆盖**：测试所有编码格式（json/text/markdown）\n\n## 示例：夸克热点 API\n\n参考 `src/modules/quark.module.ts` 作为新模块的模板：\n\n```typescript\nimport { Common } from '../common.ts'\nimport type { RouterMiddleware } from '@oak/oak'\n\ninterface QuarkHotItem {\n  id: string\n  title: string\n  summary: string\n  source_name: string     // snake_case\n  publish_time: number    // snake_case\n  cover: string\n  category: string[]\n  tags: string[]\n  comment_count: number   // snake_case\n  like_count: number      // snake_case\n}\n\nclass ServiceQuark {\n  handle(): RouterMiddleware<'/quark'> {\n    return async (ctx) => {\n      const data = await this.#fetch()\n      \n      switch (ctx.state.encoding) {\n        case 'text':\n          // 纯文本格式\n          break\n        case 'markdown':\n          // Markdown 格式\n          break\n        case 'json':\n        default:\n          ctx.response.body = Common.buildJson(data)\n          break\n      }\n    }\n  }\n\n  async #fetch(): Promise<QuarkHotItem[]> {\n    // 获取并处理数据\n  }\n}\n\nexport const serviceQuark = new ServiceQuark()\n```\n\n## 快速检查清单\n\n添加新 API 时，确保：\n\n- [ ] 模块文件创建在 `src/modules/` 目录\n- [ ] 类名遵循 `ServiceXxx` 命名规范\n- [ ] 导出实例遵循 `serviceXxx` 命名规范\n- [ ] JSON 返回字段使用 `snake_case` 格式\n- [ ] 支持 `json`、`text`、`markdown` 三种编码格式\n- [ ] 使用 `Common.buildJson()` 构建 JSON 响应\n- [ ] HTTP 请求使用 `Common.chromeUA`\n- [ ] 在 `src/router.ts` 中注册路由\n- [ ] 无 TypeScript 编译错误\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n60s API is a comprehensive API collection providing news, trending topics, and utility services. Built with Deno and Oak framework, it supports multiple runtime environments (Deno, Node.js, Bun) and deployment platforms (Docker, Cloudflare Workers).\n\n## Development Commands\n\n### Running the application\n```bash\n# Development mode (port 4398)\npnpm run dev\n\n# Production mode (port 4398)\npnpm run start\n\n# Docker\npnpm run docker:build\npnpm run docker:run\n```\n\n### Common operations\n```bash\n# Update all lockfiles\npnpm run update-lockfile\n\n# Release new version (bumps version and creates git tag)\npnpm run release\n```\n\n## Architecture\n\n### Core Structure\n- **Entry points**: `deno.ts`, `node.ts`, `bun.ts`, `cf-worker.ts` for different runtimes\n- **Main app**: `src/app.ts` - Oak application with middleware setup\n- **Routing**: `src/router.ts` - centralized route definitions with `/v2` prefix\n- **Modules**: `src/modules/` - individual API service implementations\n- **Middlewares**: `src/middlewares/` - cross-cutting concerns (CORS, error handling, encoding)\n\n### Key Components\n- **Common utilities**: `src/common.ts` - shared functions for JSON building, parameter extraction, date formatting\n- **Configuration**: `src/config.ts` - environment-based settings\n- **Encoding middleware**: Handles `encoding` query parameter for response format transformation\n\n## Development Guidelines\n\n### Module Development Pattern\n\nEach API endpoint follows a consistent module pattern:\n\n1. **Create service class** in `src/modules/[name].module.ts`\n2. **Implement `handle()` method** returning Oak RouterMiddleware\n3. **Handle different encoding formats** in the middleware (see Response Formats below)\n4. **Register route** in `src/router.ts`\n5. **Import and add** to router configuration\n\nExample structure:\n```typescript\nexport class MyModule {\n  async handle(): RouterMiddleware<any> {\n    return async (ctx) => {\n      const encoding = ctx.state.encoding\n      const data = await this.fetchData()\n\n      if (encoding === 'text') {\n        ctx.response.body = this.formatAsText(data)\n      } else if (encoding === 'markdown') {\n        ctx.response.body = this.formatAsMarkdown(data)\n      } else {\n        ctx.response.body = Common.buildJson(data)\n      }\n    }\n  }\n}\n```\n\n### Response Format Standards\n\n**IMPORTANT**: Unless specifically noted otherwise, ALL APIs MUST support these three encoding formats:\n\n- `json` (default) - structured JSON response via `Common.buildJson()`\n- `text` - plain text format for terminal/script usage\n- `markdown` - markdown formatted text for documentation/display\n\nSpecial formats (only when explicitly needed):\n- `image` - redirect to image URL\n- `image-proxy` - proxied image content\n- `html` - HTML encoded output\n\n### Time Handling Standards\n\n**ALWAYS use `dayjs` for time operations:**\n\n```typescript\nimport { dayjs, TZ_SHANGHAI } from './common.ts'\n\n// Get current time in Shanghai timezone\nconst now = dayjs().tz(TZ_SHANGHAI)\n\n// Format date\nconst dateStr = now.format('YYYY-MM-DD')\n\n// Parse and convert timezone\nconst parsedDate = dayjs(timestamp).tz(TZ_SHANGHAI)\n```\n\n**Default timezone**: Always use `TZ_SHANGHAI` (`Asia/Shanghai`) for all time operations unless explicitly specified otherwise.\n\n### Common Patterns\n\n**Response Building:**\n```typescript\n// Success response\nctx.response.body = Common.buildJson(data)\n\n// Error response with custom message\nctx.response.status = 400\nctx.response.body = Common.buildJson(null, 400, 'Custom error message')\n\n// Require arguments\nif (!param) {\n  Common.requireArguments('paramName', ctx.response)\n  return\n}\n```\n\n**Parameter Handling:**\n```typescript\n// Query parameters\nconst param = ctx.request.url.searchParams.get('param')\n\n// Support both query and POST body (for large params)\nconst largeParam = await Common.getParam('param', ctx.request, true)\n```\n\n**Web Scraping:**\n```typescript\n// Always use Common.chromeUA for User-Agent\nconst response = await fetch(url, {\n  headers: { 'User-Agent': Common.chromeUA }\n})\n```\n\n**Caching:**\n```typescript\n// Implement caching for expensive operations\nconst cache = new Map<string, { data: any; timestamp: number }>()\nconst CACHE_TTL = 30 * 60 * 1000 // 30 minutes\n\n// Check cache before fetching\nconst cached = cache.get(key)\nif (cached && Date.now() - cached.timestamp < CACHE_TTL) {\n  return cached.data\n}\n```\n\n### Code Quality Standards\n\n- **Type Safety**: Use TypeScript types, avoid `any` when possible\n- **Error Handling**: Always handle fetch errors and invalid responses\n- **Validation**: Validate required parameters using `Common.requireArguments()`\n- **Consistency**: Follow existing code patterns in `src/modules/`\n- **Documentation**: Add JSDoc comments for complex functions\n- **Testing**: Test all three encoding formats (json/text/markdown) when adding new APIs\n\n### Useful Utilities\n\nFrom `src/common.ts`:\n- `Common.buildJson()` - Standard JSON response builder\n- `Common.requireArguments()` - Parameter validation\n- `dayjs` / `TZ_SHANGHAI` - Time operations (prefer over `Common.localeDate()`/`Common.localeTime()`)\n- `Common.randomInt()` / `Common.randomItem()` - Random utilities\n- `Common.md5()` - MD5 hashing\n- `Common.qs()` - Query string builder\n- `Common.tryRepoUrl()` - GitHub CDN fallback fetcher\n\n**Note**: `Common.localeDate()` and `Common.localeTime()` are legacy utilities. For new code, always use `dayjs` with `TZ_SHANGHAI` timezone as shown in the Time Handling Standards section.\n","COPILOT.md":"# Copilot 指导文档\n\n本文档为 GitHub Copilot 提供项目编码指导，帮助生成符合项目规范的代码。\n\n## 项目概述\n\n60s API 是一个综合性 API 集合，提供新闻、热搜、工具类等多种服务。项目使用 Deno + Oak 框架构建，支持多种运行时环境（Deno、Node.js、Bun）和部署平台（Docker、Cloudflare Workers）。\n\n## 开发命令\n\n```bash\n# 开发模式 (端口 4398)\npnpm run dev\n\n# 生产模式\npnpm run start\n\n# Docker 构建和运行\npnpm run docker:build\npnpm run docker:run\n```\n\n## 项目架构\n\n### 目录结构\n```\nsrc/\n├── app.ts              # Oak 应用入口，中间件配置\n├── router.ts           # 集中式路由定义（/v2 前缀）\n├── common.ts           # 公共工具函数\n├── config.ts           # 环境配置\n├── middlewares/        # 中间件\n│   ├── cors.ts         # 跨域处理\n│   ├── encoding.ts     # 响应格式处理\n│   └── ...\n└── modules/            # API 模块实现\n    ├── baidu.module.ts\n    ├── weibo.module.ts\n    ├── quark.module.ts\n    └── ...\n```\n\n### 运行时入口\n- `deno.ts` - Deno 运行时\n- `node.ts` - Node.js 运行时\n- `bun.ts` - Bun 运行时\n- `cf-worker.ts` - Cloudflare Workers\n\n## 模块开发规范\n\n### 1. 创建新模块\n\n在 `src/modules/` 目录下创建 `[name].module.ts` 文件：\n\n```typescript\nimport { Common } from '../common.ts'\n\nimport type { RouterMiddleware } from '@oak/oak'\n\n// 定义接口类型\ninterface DataItem {\n  id: string\n  title: string\n  // ... 其他字段使用 snake_case 命名\n}\n\nclass ServiceExample {\n  handle(): RouterMiddleware<'/example'> {\n    return async (ctx) => {\n      const data = await this.#fetch()\n\n      switch (ctx.state.encoding) {\n        case 'text':\n          ctx.response.body = `示例标题\\n\\n${data\n            .slice(0, 20)\n            .map((e, i) => `${i + 1}. ${e.title}`)\n            .join('\\n')}`\n          break\n\n        case 'markdown':\n          ctx.response.body = `# 示例标题\\n\\n${data\n            .slice(0, 20)\n            .map((e, i) => `### ${i + 1}. ${e.title}\\n\\n---\\n`)\n            .join('\\n')}`\n          break\n\n        case 'json':\n        default:\n          ctx.response.body = Common.buildJson(data)\n          break\n      }\n    }\n  }\n\n  async #fetch(): Promise<DataItem[]> {\n    const api = 'https://example.com/api'\n    \n    const response = await fetch(api, {\n      headers: {\n        'User-Agent': Common.chromeUA,\n      },\n    })\n    \n    const json = await response.json()\n    // 处理并返回数据，确保字段使用 snake_case\n    return json.data.map((item: any) => ({\n      id: item.id,\n      title: item.title,\n      // 转换驼峰为下划线\n      publish_time: item.publishTime,\n      source_name: item.sourceName,\n    }))\n  }\n}\n\nexport const serviceExample = new ServiceExample()\n```\n\n### 2. 注册路由\n\n在 `src/router.ts` 中添加：\n\n```typescript\n// 1. 导入模块\nimport { serviceExample } from './modules/example.module.ts'\n\n// 2. 注册路由\nappRouter.get('/example', serviceExample.handle())\n```\n\n## 编码规范\n\n### 响应格式\n\n所有 API 必须支持三种编码格式：\n- `json`（默认）- 结构化 JSON 响应，通过 `Common.buildJson()` 构建\n- `text` - 纯文本格式，用于终端/脚本\n- `markdown` - Markdown 格式，用于文档展示\n\n### JSON 字段命名\n\n**重要**：返回的 JSON 字段必须使用 `snake_case` 格式：\n\n```typescript\n// ✅ 正确\n{\n  id: \"123\",\n  title: \"标题\",\n  publish_time: 1234567890,\n  source_name: \"来源\",\n  comment_count: 100,\n  like_count: 50\n}\n\n// ❌ 错误（驼峰命名）\n{\n  id: \"123\",\n  title: \"标题\",\n  publishTime: 1234567890,  // 应为 publish_time\n  sourceName: \"来源\",        // 应为 source_name\n  commentCount: 100,         // 应为 comment_count\n  likeCount: 50              // 应为 like_count\n}\n```\n\n### 数据完整性\n\n**重要**：返回的 JSON 应尽可能提供完整、有帮助的信息：\n\n1. **不要截断数据**：如有完整内容（如 `content`），应清理 HTML 后返回全文\n2. **包含所有有用字段**：\n   - 基本信息：`id`, `title`, `summary`, `content`\n   - 来源信息：`source_name`, `origin_source_name`, `author`\n   - 时间信息：`publish_time`, `grab_time`, `modify_time`\n   - 媒体资源：`cover`, `images[]`, `videos[]`\n   - 分类标签：`category[]`, `tags[]`\n   - 互动数据：`comment_count`, `like_count`, `share_count`, `favorite_count`\n   - 链接信息：`original_url`\n\n3. **嵌套对象使用 snake_case**：\n```typescript\n{\n  author: {\n    name: \"作者名\",\n    desc: \"简介\",\n    icon: \"头像URL\",\n    follower_count: 10000\n  },\n  images: [{\n    url: \"图片URL\",\n    width: 640,\n    height: 480,\n    type: \"jpg\",\n    description: \"图片描述\"\n  }]\n}\n```\n\n4. **HTML 内容清理**：\n```typescript\n#cleanHtml(html: string): string {\n  return html\n    .replace(/<!--\\{(img|video):\\d+\\}-->/g, '') // 移除占位符\n    .replace(/<[^>]+>/g, '')                     // 移除 HTML 标签\n    .replace(/&nbsp;/g, ' ')                     // 处理实体\n    .replace(/\\s+/g, ' ')                        // 清理空白\n    .trim()\n}\n```\n\n### 时间处理\n\n使用 `dayjs` 处理时间：\n\n```typescript\nimport { dayjs, TZ_SHANGHAI } from './common.ts'\n\n// 当前上海时间\nconst now = dayjs().tz(TZ_SHANGHAI)\n\n// 格式化\nconst dateStr = now.format('YYYY-MM-DD')\n\n// 解析时间戳\nconst time = dayjs(timestamp).tz(TZ_SHANGHAI)\n```\n\n### HTTP 请求\n\n```typescript\n// 始终使用 Common.chromeUA 作为 User-Agent\nconst response = await fetch(url, {\n  headers: { 'User-Agent': Common.chromeUA }\n})\n```\n\n### 参数验证\n\n```typescript\n// 获取参数\nconst param = ctx.request.url.searchParams.get('param')\n\n// 必需参数验证\nif (!param) {\n  Common.requireArguments('param', ctx.response)\n  return\n}\n```\n\n### 错误处理\n\n```typescript\n// 成功响应\nctx.response.body = Common.buildJson(data)\n\n// 错误响应\nctx.response.status = 400\nctx.response.body = Common.buildJson(null, 400, '错误信息')\n```\n\n## 常用工具函数\n\n| 函数 | 说明 |\n|------|------|\n| `Common.buildJson(data, code?, message?)` | 构建标准 JSON 响应 |\n| `Common.requireArguments(name, response)` | 参数验证 |\n| `Common.chromeUA` | Chrome User-Agent 字符串 |\n| `Common.localeTime(timestamp)` | 格式化时间 |\n| `Common.randomInt(min, max)` | 随机整数 |\n| `Common.randomItem(array)` | 随机数组元素 |\n| `Common.md5(str)` | MD5 哈希 |\n| `Common.qs(obj)` | 构建查询字符串 |\n\n## 代码质量\n\n- **类型安全**：使用 TypeScript 类型，避免使用 `any`\n- **错误处理**：处理 fetch 错误和无效响应\n- **参数验证**：使用 `Common.requireArguments()` 验证必需参数\n- **代码一致性**：遵循 `src/modules/` 中现有代码模式\n- **注释文档**：为复杂函数添加 JSDoc 注释\n- **测试覆盖**：测试所有编码格式（json/text/markdown）\n\n## 示例：夸克热点 API\n\n参考 `src/modules/quark.module.ts` 作为新模块的模板：\n\n```typescript\nimport { Common } from '../common.ts'\nimport type { RouterMiddleware } from '@oak/oak'\n\ninterface QuarkHotItem {\n  id: string\n  title: string\n  summary: string\n  source_name: string     // snake_case\n  publish_time: number    // snake_case\n  cover: string\n  category: string[]\n  tags: string[]\n  comment_count: number   // snake_case\n  like_count: number      // snake_case\n}\n\nclass ServiceQuark {\n  handle(): RouterMiddleware<'/quark'> {\n    return async (ctx) => {\n      const data = await this.#fetch()\n      \n      switch (ctx.state.encoding) {\n        case 'text':\n          // 纯文本格式\n          break\n        case 'markdown':\n          // Markdown 格式\n          break\n        case 'json':\n        default:\n          ctx.response.body = Common.buildJson(data)\n          break\n      }\n    }\n  }\n\n  async #fetch(): Promise<QuarkHotItem[]> {\n    // 获取并处理数据\n  }\n}\n\nexport const serviceQuark = new ServiceQuark()\n```\n\n## 快速检查清单\n\n添加新 API 时，确保：\n\n- [ ] 模块文件创建在 `src/modules/` 目录\n- [ ] 类名遵循 `ServiceXxx` 命名规范\n- [ ] 导出实例遵循 `serviceXxx` 命名规范\n- [ ] JSON 返回字段使用 `snake_case` 格式\n- [ ] 支持 `json`、`text`、`markdown` 三种编码格式\n- [ ] 使用 `Common.buildJson()` 构建 JSON 响应\n- [ ] HTTP 请求使用 `Common.chromeUA`\n- [ ] 在 `src/router.ts` 中注册路由\n- [ ] 无 TypeScript 编译错误\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n60s API is a comprehensive API collection providing news, trending topics, and utility services. Built with Deno and Oak framework, it supports multiple runtime environments (Deno, Node.js, Bun) and deployment platforms (Docker, Cloudflare Workers).\n\n## Development Commands\n\n### Running the application\n```bash\n# Development mode (port 4398)\npnpm run dev\n\n# Production mode (port 4398)\npnpm run start\n\n# Docker\npnpm run docker:build\npnpm run docker:run\n```\n\n### Common operations\n```bash\n# Update all lockfiles\npnpm run update-lockfile\n\n# Release new version (bumps version and creates git tag)\npnpm run release\n```\n\n## Architecture\n\n### Core Structure\n- **Entry points**: `deno.ts`, `node.ts`, `bun.ts`, `cf-worker.ts` for different runtimes\n- **Main app**: `src/app.ts` - Oak application with middleware setup\n- **Routing**: `src/router.ts` - centralized route definitions with `/v2` prefix\n- **Modules**: `src/modules/` - individual API service implementations\n- **Middlewares**: `src/middlewares/` - cross-cutting concerns (CORS, error handling, encoding)\n\n### Key Components\n- **Common utilities**: `src/common.ts` - shared functions for JSON building, parameter extraction, date formatting\n- **Configuration**: `src/config.ts` - environment-based settings\n- **Encoding middleware**: Handles `encoding` query parameter for response format transformation\n\n## Development Guidelines\n\n### Module Development Pattern\n\nEach API endpoint follows a consistent module pattern:\n\n1. **Create service class** in `src/modules/[name].module.ts`\n2. **Implement `handle()` method** returning Oak RouterMiddleware\n3. **Handle different encoding formats** in the middleware (see Response Formats below)\n4. **Register route** in `src/router.ts`\n5. **Import and add** to router configuration\n\nExample structure:\n```typescript\nexport class MyModule {\n  async handle(): RouterMiddleware<any> {\n    return async (ctx) => {\n      const encoding = ctx.state.encoding\n      const data = await this.fetchData()\n\n      if (encoding === 'text') {\n        ctx.response.body = this.formatAsText(data)\n      } else if (encoding === 'markdown') {\n        ctx.response.body = this.formatAsMarkdown(data)\n      } else {\n        ctx.response.body = Common.buildJson(data)\n      }\n    }\n  }\n}\n```\n\n### Response Format Standards\n\n**IMPORTANT**: Unless specifically noted otherwise, ALL APIs MUST support these three encoding formats:\n\n- `json` (default) - structured JSON response via `Common.buildJson()`\n- `text` - plain text format for terminal/script usage\n- `markdown` - markdown formatted text for documentation/display\n\nSpecial formats (only when explicitly needed):\n- `image` - redirect to image URL\n- `image-proxy` - proxied image content\n- `html` - HTML encoded output\n\n### Time Handling Standards\n\n**ALWAYS use `dayjs` for time operations:**\n\n```typescript\nimport { dayjs, TZ_SHANGHAI } from './common.ts'\n\n// Get current time in Shanghai timezone\nconst now = dayjs().tz(TZ_SHANGHAI)\n\n// Format date\nconst dateStr = now.format('YYYY-MM-DD')\n\n// Parse and convert timezone\nconst parsedDate = dayjs(timestamp).tz(TZ_SHANGHAI)\n```\n\n**Default timezone**: Always use `TZ_SHANGHAI` (`Asia/Shanghai`) for all time operations unless explicitly specified otherwise.\n\n### Common Patterns\n\n**Response Building:**\n```typescript\n// Success response\nctx.response.body = Common.buildJson(data)\n\n// Error response with custom message\nctx.response.status = 400\nctx.response.body = Common.buildJson(null, 400, 'Custom error message')\n\n// Require arguments\nif (!param) {\n  Common.requireArguments('paramName', ctx.response)\n  return\n}\n```\n\n**Parameter Handling:**\n```typescript\n// Query parameters\nconst param = ctx.request.url.searchParams.get('param')\n\n// Support both query and POST body (for large params)\nconst largeParam = await Common.getParam('param', ctx.request, true)\n```\n\n**Web Scraping:**\n```typescript\n// Always use Common.chromeUA for User-Agent\nconst response = await fetch(url, {\n  headers: { 'User-Agent': Common.chromeUA }\n})\n```\n\n**Caching:**\n```typescript\n// Implement caching for expensive operations\nconst cache = new Map<string, { data: any; timestamp: number }>()\nconst CACHE_TTL = 30 * 60 * 1000 // 30 minutes\n\n// Check cache before fetching\nconst cached = cache.get(key)\nif (cached && Date.now() - cached.timestamp < CACHE_TTL) {\n  return cached.data\n}\n```\n\n### Code Quality Standards\n\n- **Type Safety**: Use TypeScript types, avoid `any` when possible\n- **Error Handling**: Always handle fetch errors and invalid responses\n- **Validation**: Validate required parameters using `Common.requireArguments()`\n- **Consistency**: Follow existing code patterns in `src/modules/`\n- **Documentation**: Add JSDoc comments for complex functions\n- **Testing**: Test all three encoding formats (json/text/markdown) when adding new APIs\n\n### Useful Utilities\n\nFrom `src/common.ts`:\n- `Common.buildJson()` - Standard JSON response builder\n- `Common.requireArguments()` - Parameter validation\n- `dayjs` / `TZ_SHANGHAI` - Time operations (prefer over `Common.localeDate()`/`Common.localeTime()`)\n- `Common.randomInt()` / `Common.randomItem()` - Random utilities\n- `Common.md5()` - MD5 hashing\n- `Common.qs()` - Query string builder\n- `Common.tryRepoUrl()` - GitHub CDN fallback fetcher\n\n**Note**: `Common.localeDate()` and `Common.localeTime()` are legacy utilities. For new code, always use `dayjs` with `TZ_SHANGHAI` timezone as shown in the Time Handling Standards section.\n","category":"root","tokens":1397},{"name":"COPILOT.md","path":"COPILOT.md","title":"COPILOT.md","content":"# Copilot 指导文档\n\n本文档为 GitHub Copilot 提供项目编码指导，帮助生成符合项目规范的代码。\n\n## 项目概述\n\n60s API 是一个综合性 API 集合，提供新闻、热搜、工具类等多种服务。项目使用 Deno + Oak 框架构建，支持多种运行时环境（Deno、Node.js、Bun）和部署平台（Docker、Cloudflare Workers）。\n\n## 开发命令\n\n```bash\n# 开发模式 (端口 4398)\npnpm run dev\n\n# 生产模式\npnpm run start\n\n# Docker 构建和运行\npnpm run docker:build\npnpm run docker:run\n```\n\n## 项目架构\n\n### 目录结构\n```\nsrc/\n├── app.ts              # Oak 应用入口，中间件配置\n├── router.ts           # 集中式路由定义（/v2 前缀）\n├── common.ts           # 公共工具函数\n├── config.ts           # 环境配置\n├── middlewares/        # 中间件\n│   ├── cors.ts         # 跨域处理\n│   ├── encoding.ts     # 响应格式处理\n│   └── ...\n└── modules/            # API 模块实现\n    ├── baidu.module.ts\n    ├── weibo.module.ts\n    ├── quark.module.ts\n    └── ...\n```\n\n### 运行时入口\n- `deno.ts` - Deno 运行时\n- `node.ts` - Node.js 运行时\n- `bun.ts` - Bun 运行时\n- `cf-worker.ts` - Cloudflare Workers\n\n## 模块开发规范\n\n### 1. 创建新模块\n\n在 `src/modules/` 目录下创建 `[name].module.ts` 文件：\n\n```typescript\nimport { Common } from '../common.ts'\n\nimport type { RouterMiddleware } from '@oak/oak'\n\n// 定义接口类型\ninterface DataItem {\n  id: string\n  title: string\n  // ... 其他字段使用 snake_case 命名\n}\n\nclass ServiceExample {\n  handle(): RouterMiddleware<'/example'> {\n    return async (ctx) => {\n      const data = await this.#fetch()\n\n      switch (ctx.state.encoding) {\n        case 'text':\n          ctx.response.body = `示例标题\\n\\n${data\n            .slice(0, 20)\n            .map((e, i) => `${i + 1}. ${e.title}`)\n            .join('\\n')}`\n          break\n\n        case 'markdown':\n          ctx.response.body = `# 示例标题\\n\\n${data\n            .slice(0, 20)\n            .map((e, i) => `### ${i + 1}. ${e.title}\\n\\n---\\n`)\n            .join('\\n')}`\n          break\n\n        case 'json':\n        default:\n          ctx.response.body = Common.buildJson(data)\n          break\n      }\n    }\n  }\n\n  async #fetch(): Promise<DataItem[]> {\n    const api = 'https://example.com/api'\n    \n    const response = await fetch(api, {\n      headers: {\n        'User-Agent': Common.chromeUA,\n      },\n    })\n    \n    const json = await response.json()\n    // 处理并返回数据，确保字段使用 snake_case\n    return json.data.map((item: any) => ({\n      id: item.id,\n      title: item.title,\n      // 转换驼峰为下划线\n      publish_time: item.publishTime,\n      source_name: item.sourceName,\n    }))\n  }\n}\n\nexport const serviceExample = new ServiceExample()\n```\n\n### 2. 注册路由\n\n在 `src/router.ts` 中添加：\n\n```typescript\n// 1. 导入模块\nimport { serviceExample } from './modules/example.module.ts'\n\n// 2. 注册路由\nappRouter.get('/example', serviceExample.handle())\n```\n\n## 编码规范\n\n### 响应格式\n\n所有 API 必须支持三种编码格式：\n- `json`（默认）- 结构化 JSON 响应，通过 `Common.buildJson()` 构建\n- `text` - 纯文本格式，用于终端/脚本\n- `markdown` - Markdown 格式，用于文档展示\n\n### JSON 字段命名\n\n**重要**：返回的 JSON 字段必须使用 `snake_case` 格式：\n\n```typescript\n// ✅ 正确\n{\n  id: \"123\",\n  title: \"标题\",\n  publish_time: 1234567890,\n  source_name: \"来源\",\n  comment_count: 100,\n  like_count: 50\n}\n\n// ❌ 错误（驼峰命名）\n{\n  id: \"123\",\n  title: \"标题\",\n  publishTime: 1234567890,  // 应为 publish_time\n  sourceName: \"来源\",        // 应为 source_name\n  commentCount: 100,         // 应为 comment_count\n  likeCount: 50              // 应为 like_count\n}\n```\n\n### 数据完整性\n\n**重要**：返回的 JSON 应尽可能提供完整、有帮助的信息：\n\n1. **不要截断数据**：如有完整内容（如 `content`），应清理 HTML 后返回全文\n2. **包含所有有用字段**：\n   - 基本信息：`id`, `title`, `summary`, `content`\n   - 来源信息：`source_name`, `origin_source_name`, `author`\n   - 时间信息：`publish_time`, `grab_time`, `modify_time`\n   - 媒体资源：`cover`, `images[]`, `videos[]`\n   - 分类标签：`category[]`, `tags[]`\n   - 互动数据：`comment_count`, `like_count`, `share_count`, `favorite_count`\n   - 链接信息：`original_url`\n\n3. **嵌套对象使用 snake_case**：\n```typescript\n{\n  author: {\n    name: \"作者名\",\n    desc: \"简介\",\n    icon: \"头像URL\",\n    follower_count: 10000\n  },\n  images: [{\n    url: \"图片URL\",\n    width: 640,\n    height: 480,\n    type: \"jpg\",\n    description: \"图片描述\"\n  }]\n}\n```\n\n4. **HTML 内容清理**：\n```typescript\n#cleanHtml(html: string): string {\n  return html\n    .replace(/<!--\\{(img|video):\\d+\\}-->/g, '') // 移除占位符\n    .replace(/<[^>]+>/g, '')                     // 移除 HTML 标签\n    .replace(/&nbsp;/g, ' ')                     // 处理实体\n    .replace(/\\s+/g, ' ')                        // 清理空白\n    .trim()\n}\n```\n\n### 时间处理\n\n使用 `dayjs` 处理时间：\n\n```typescript\nimport { dayjs, TZ_SHANGHAI } from './common.ts'\n\n// 当前上海时间\nconst now = dayjs().tz(TZ_SHANGHAI)\n\n// 格式化\nconst dateStr = now.format('YYYY-MM-DD')\n\n// 解析时间戳\nconst time = dayjs(timestamp).tz(TZ_SHANGHAI)\n```\n\n### HTTP 请求\n\n```typescript\n// 始终使用 Common.chromeUA 作为 User-Agent\nconst response = await fetch(url, {\n  headers: { 'User-Agent': Common.chromeUA }\n})\n```\n\n### 参数验证\n\n```typescript\n// 获取参数\nconst param = ctx.request.url.searchParams.get('param')\n\n// 必需参数验证\nif (!param) {\n  Common.requireArguments('param', ctx.response)\n  return\n}\n```\n\n### 错误处理\n\n```typescript\n// 成功响应\nctx.response.body = Common.buildJson(data)\n\n// 错误响应\nctx.response.status = 400\nctx.response.body = Common.buildJson(null, 400, '错误信息')\n```\n\n## 常用工具函数\n\n| 函数 | 说明 |\n|------|------|\n| `Common.buildJson(data, code?, message?)` | 构建标准 JSON 响应 |\n| `Common.requireArguments(name, response)` | 参数验证 |\n| `Common.chromeUA` | Chrome User-Agent 字符串 |\n| `Common.localeTime(timestamp)` | 格式化时间 |\n| `Common.randomInt(min, max)` | 随机整数 |\n| `Common.randomItem(array)` | 随机数组元素 |\n| `Common.md5(str)` | MD5 哈希 |\n| `Common.qs(obj)` | 构建查询字符串 |\n\n## 代码质量\n\n- **类型安全**：使用 TypeScript 类型，避免使用 `any`\n- **错误处理**：处理 fetch 错误和无效响应\n- **参数验证**：使用 `Common.requireArguments()` 验证必需参数\n- **代码一致性**：遵循 `src/modules/` 中现有代码模式\n- **注释文档**：为复杂函数添加 JSDoc 注释\n- **测试覆盖**：测试所有编码格式（json/text/markdown）\n\n## 示例：夸克热点 API\n\n参考 `src/modules/quark.module.ts` 作为新模块的模板：\n\n```typescript\nimport { Common } from '../common.ts'\nimport type { RouterMiddleware } from '@oak/oak'\n\ninterface QuarkHotItem {\n  id: string\n  title: string\n  summary: string\n  source_name: string     // snake_case\n  publish_time: number    // snake_case\n  cover: string\n  category: string[]\n  tags: string[]\n  comment_count: number   // snake_case\n  like_count: number      // snake_case\n}\n\nclass ServiceQuark {\n  handle(): RouterMiddleware<'/quark'> {\n    return async (ctx) => {\n      const data = await this.#fetch()\n      \n      switch (ctx.state.encoding) {\n        case 'text':\n          // 纯文本格式\n          break\n        case 'markdown':\n          // Markdown 格式\n          break\n        case 'json':\n        default:\n          ctx.response.body = Common.buildJson(data)\n          break\n      }\n    }\n  }\n\n  async #fetch(): Promise<QuarkHotItem[]> {\n    // 获取并处理数据\n  }\n}\n\nexport const serviceQuark = new ServiceQuark()\n```\n\n## 快速检查清单\n\n添加新 API 时，确保：\n\n- [ ] 模块文件创建在 `src/modules/` 目录\n- [ ] 类名遵循 `ServiceXxx` 命名规范\n- [ ] 导出实例遵循 `serviceXxx` 命名规范\n- [ ] JSON 返回字段使用 `snake_case` 格式\n- [ ] 支持 `json`、`text`、`markdown` 三种编码格式\n- [ ] 使用 `Common.buildJson()` 构建 JSON 响应\n- [ ] HTTP 请求使用 `Common.chromeUA`\n- [ ] 在 `src/router.ts` 中注册路由\n- [ ] 无 TypeScript 编译错误\n","category":"root","tokens":1705}]}