一个功能强大的 URL 转换和 CDN 代理服务,支持将 GitHub、npm、cnb.cool 等源站的 URL 转换为自定义 CDN 地址,并提供灵活的黑名单和白名单管理功能。
github.com、raw.githubusercontent.comnpmjs.com、unpkg.com、esm.run、jsdelivr.networkspace/ ├── .claude/ # Claude AI 配置 │ └── settings.local.json # AI 助手本地设置 ├── .codebuddy/ # CodeBuddy 配置 ├── .next/ # Next.js 构建输出 │ ├── app-build-manifest.json # 应用构建清单 │ ├── build-manifest.json # 构建清单 │ ├── cache/ # 构建缓存 │ ├── server/ # 服务端代码 │ ├── static/ # 静态资源 │ ├── standalone/ # 独立部署文件 │ └── types/ # TypeScript 类型 ├── components.json # shadcn/ui 组件配置 ├── db/ # 数据库文件 │ └── custom.db # 自定义数据库 ├── dev.log # 开发日志 ├── eslint.config.mjs # ESLint 配置 ├── examples/ # 示例代码 │ ├── websocket/ # WebSocket 示例 │ └── ... ├── image.png # 项目图片 ├── mini-services/ # 微服务 ├── next.config.ts # Next.js 配置 ├── next-env.d.ts # Next.js TypeScript 类型声明 ├── package.json # 项目依赖和脚本 ├── package-lock.json # 依赖锁文件 ├── postcss.config.mjs # PostCSS 配置 ├── prisma/ # Prisma 数据库配置 │ ├── dev.db # SQLite 开发数据库 │ ├── prisma/ # Prisma 迁移文件 │ └── schema.prisma # 数据库模型定义 ├── public/ # 静态资源 │ ├── logo.svg # Logo 文件 │ └── robots.txt # 搜索引擎爬虫配置 ├── scripts/ # 工具脚本 │ └── init-db.ts # 数据库初始化脚本 ├── seed-logs.ts # 日志种子数据 ├── seed-test-data.ts # 测试数据种子 ├── skills/ # AI 技能 ├── src/ # 源代码 │ ├── app/ # Next.js App Router │ │ ├── [...catchall]/ # 通配符路由,处理 CDN 代理 │ │ │ └── route.ts # CDN 代理路由处理 │ │ ├── admin/ # 管理后台 │ │ │ └── page.tsx # 管理后台页面 │ │ ├── api/ # API 路由 │ │ │ ├── admin/ # 管理相关 API │ │ │ │ ├── auth/ # 认证 API │ │ │ │ │ └── route.ts # 认证处理 │ │ │ │ ├── blacklist/ # 黑名单相关 API │ │ │ │ │ ├── config/ # 黑名单配置 │ │ │ │ │ │ └── route.ts # 黑名单配置管理 │ │ │ │ │ └── rules/ # 黑名单规则 │ │ │ │ │ └── route.ts # 黑名单规则管理 │ │ │ │ ├── domains/ # 域名白名单 API │ │ │ │ │ └── route.ts # 域名白名单管理 │ │ │ │ ├── logs/ # 操作日志 │ │ │ │ │ └── route.ts # 日志查询 │ │ │ │ ├── stats/ # 统计信息 │ │ │ │ │ └── route.ts # 统计数据 │ │ │ │ └── route.ts # 管理首页 API │ │ │ ├── config/ # 配置 API │ │ │ │ ├── reload/ # 配置重载 │ │ │ │ │ └── route.ts # 配置重载接口 │ │ │ │ └── route.ts # 系统配置 │ │ │ ├── convert/ # URL 转换 API │ │ │ │ └── route.ts # URL 转换处理 │ │ │ ├── proxy/ # 代理 API │ │ │ │ └── route.ts # 内容代理 │ │ │ ├── status/ # 服务状态 │ │ │ │ └── route.ts # 状态检查 │ │ │ └── route.ts # API 路由 │ │ ├── globals.css # 全局样式 │ │ ├── layout.tsx # 根布局 │ │ └── page.tsx # 首页 │ ├── components/ # React 组件 │ │ └── ui/ # shadcn/ui 组件 │ │ ├── accordion.tsx # 手风琴 │ │ ├── alert-dialog.tsx # 警告对话框 │ │ ├── alert.tsx # 警告提示 │ │ ├── aspect-ratio.tsx # 宽高比 │ │ ├── avatar.tsx # 头像 │ │ ├── badge.tsx # 标签 │ │ ├── breadcrumb.tsx # 面包屑导航 │ │ ├── button.tsx # 按钮 │ │ ├── calendar.tsx # 日历 │ │ ├── card.tsx # 卡片 │ │ ├── carousel.tsx # 轮播图 │ │ ├── chart.tsx # 图表 │ │ ├── checkbox.tsx # 复选框 │ │ ├── collapsible.tsx # 可折叠 │ │ ├── command.tsx # 命令面板 │ │ ├── context-menu.tsx # 右键菜单 │ │ ├── dialog.tsx # 对话框 │ │ ├── drawer.tsx # 抽屉 │ │ ├── dropdown-menu.tsx # 下拉菜单 │ │ ├── form.tsx # 表单 │ │ ├── hover-card.tsx # 悬停卡片 │ │ ├── input-otp.tsx # OTP 输入 │ │ ├── input.tsx # 输入框 │ │ ├── label.tsx # 标签 │ │ ├── menubar.tsx # 菜单栏 │ │ ├── navigation-menu.tsx # 导航菜单 │ │ ├── pagination.tsx # 分页 │ │ ├── popover.tsx # 弹出框 │ │ ├── progress.tsx # 进度条 │ │ ├── radio-group.tsx # 单选组 │ │ ├── resizable.tsx # 可调整大小 │ │ ├── scroll-area.tsx # 滚动区域 │ │ ├── select.tsx # 选择器 │ │ ├── separator.tsx # 分隔符 │ │ ├── sheet.tsx # 侧边栏 │ │ ├── sidebar.tsx # 侧边栏组件 │ │ ├── skeleton.tsx # 骨架屏 │ │ ├── slider.tsx # 滑块 │ │ ├── sonner.tsx # Toast 通知 │ │ ├── switch.tsx # 开关 │ │ ├── table.tsx # 表格 │ │ ├── tabs.tsx # 标签页 │ │ ├── textarea.tsx # 文本域 │ │ ├── toast.tsx # Toast 组件 │ │ ├── toaster.tsx # Toast 容器 │ │ ├── toggle-group.tsx # 切换组 │ │ ├── toggle.tsx # 切换按钮 │ │ └── tooltip.tsx # 工具提示 │ ├── hooks/ # React Hooks │ │ ├── use-mobile.ts # 移动端检测 │ │ └── use-toast.ts # Toast 通知 │ └── lib/ # 工具库 │ ├── converter.ts # URL 转换核心逻辑 │ ├── db-manager.ts # 数据库管理 │ ├── db.ts # 数据库连接 │ └── utils.ts # 工具函数 ├── tailwind.config.ts # Tailwind CSS 配置 ├── tsconfig.json # TypeScript 配置 ├── start.bat # Windows 启动脚本 └── start.sh # Linux/Mac 启动脚本
存储系统的全局配置项,支持分类管理。
model SystemConfig { id String @id @default(cuid()) key String @unique value String description String? category String @default("general") updatedAt DateTime @updatedAt updatedBy String? }
定义 URL 拦截规则,支持多种匹配方式和拦截动作。
model BlacklistRule { id String @id @default(cuid()) type String // 'path', 'regex', 'domain' pattern String description String? enabled Boolean @default(true) priority Int @default(0) actionOnMatch String? // 'block', 'redirect', 'custom' redirectUrl String? customResponse String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy String? }
黑名单的全局配置,控制黑名单的整体行为。
model BlacklistConfig { id String @id @default(cuid()) enabled Boolean @default(true) globalMode String @default("block") actionOnMatch String @default("block") redirectUrl String? customResponse String? logMatches Boolean @default(true) updatedAt DateTime @updatedAt updatedBy String? }
定义允许访问的域名规则,支持多种匹配模式。
model DomainRule { id String @id @default(cuid()) domain String matchType String @default("domain") // 'domain', 'domain_path', 'regex' pathPattern String? description String? enabled Boolean @default(true) priority Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy String? }
记录所有的 URL 转换操作,用于统计分析。
model ConversionRecord { id String @id @default(cuid()) sourceDomain String targetUrl String sourceUrl String status String // 'success', 'failed', 'blocked' blockedReason String? ipAddress String? userAgent String? referer String? responseTime Int createdAt DateTime @default(now()) }
按天聚合的转换统计信息,用于分析和报表。
model ConversionStats { id String @id @default(cuid()) date String // YYYY-MM-DD totalConversions Int @default(0) successCount Int @default(0) failedCount Int @default(0) blockedCount Int @default(0) avgResponseTime Float? // 平均响应时间 totalFileSize BigInt? @default(0) topDomains String? // JSON 格式存储热门域名 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([date]) @@index([date]) }
记录系统的重要操作,用于审计和追踪。
model OperationLog { id String @id @default(cuid()) action String // 'CREATE', 'UPDATE', 'DELETE', 'SWITCH_DB', etc. module String // 'BlacklistRule', 'DomainRule', 'SystemConfig', etc. entityId String? details String? ipAddress String? userAgent String? userId String? createdAt DateTime @default(now()) @@index([createdAt]) @@index([module]) @@index([action]) }
日志合规性管理配置。
model ComplianceConfig { id String @id @default(cuid()) retentionDays Int @default(90) // 日志保留天数 enabled Boolean @default(true) auditMode Boolean @default(false) sensitiveDataMask Boolean @default(true) alertThreshold Int? // 告警阈值 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
管理员用户账户(预留)。
model User { id String @id @default(cuid()) username String @unique password String // 哈希后的密码 role String @default("admin") enabled Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt lastLoginAt DateTime? }
git clone <repository-url>
cd workspace
使用 Bun(推荐):
bun install
或使用 npm:
npm install
bun run db:push
bun run dev
服务器将在 http://localhost:3000 启动。
DATABASE_URL="file:./prisma/dev.db" bun run build
bun run start
或使用启动脚本:
Linux/Mac:
./start.sh
Windows:
start.bat
创建 Dockerfile:
FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN DATABASE_URL="file:./prisma/dev.db" npm run build FROM node:18-alpine WORKDIR /app ENV NODE_ENV=production ENV DATABASE_URL="file:./prisma/dev.db" COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static COPY --from=builder /app/public ./public COPY --from=builder /app/prisma ./prisma EXPOSE 3000 CMD ["node", "server.js"]
构建和运行:
docker build -t url-converter . docker run -p 3000:3000 url-converter
# 克隆项目
git clone <repository-url>
cd workspace
# 安装依赖(使用 Bun 更快)
bun install
# 或使用 npm
# npm install
# 初始化数据库
DATABASE_URL="file:./prisma/dev.db" bun run db:push
# 启动开发服务器
bun run dev
服务将在 http://localhost:3000 启动。
使用 curl 测试 URL 转换:
# 转换 GitHub URL
curl -X POST http://localhost:3000/api/convert \
-H "Content-Type: application/json" \
-d '{"url": "https://github.com/user/repo/blob/main/file.js"}'
# 响应示例
# {
# "originalUrl": "https://github.com/user/repo/blob/main/file.js",
# "convertedUrl": "https://your-cdn.com/gh/user/repo@main/file.js",
# "cached": false
# }
使用转换后的 URL 直接访问内容:
# 访问转换后的 CDN 资源
curl http://localhost:3000/cnb/hsred/test@main/README.md
访问 http://localhost:3000/admin 进入管理后台:
# 原始 URL
https://github.com/user/repo/blob/main/README.md
# 转换后
https://your-cdn.com/gh/user/repo@main/README.md
# 原始 URL
https://unpkg.com/react@18.2.0/umd/react.production.min.js
# 转换后
https://your-cdn.com/npm/react@18.2.0/umd/react.production.min.js
在管理后台添加黑名单规则:
path/api/internalblock所有匹配 /api/internal 的请求将被拦截。
# 启动开发服务器
bun run dev
# 或
npm run dev
# 构建项目
DATABASE_URL="file:./prisma/dev.db" bun run build
# 启动生产服务器
bun run start
# 代码检查
bun run lint
# 推送 schema 到数据库
DATABASE_URL="file:./prisma/dev.db" bun run db:push
# 生成 Prisma Client
bun run db:generate
# 创建数据库迁移
DATABASE_URL="file:./prisma/dev.db" bun run db:migrate
# 重置数据库
DATABASE_URL="file:./prisma/dev.db" bun run db:reset
# 打开 Prisma Studio
npx prisma studio
# 构建并启动(Linux/Mac)
DATABASE_URL="file:./prisma/dev.db" bun run build
bun run start
# 使用启动脚本
./start.sh # Linux/Mac
start.bat # Windows
# 部署到 Cloud Studio(需要先登录)
# 点击 CloudStudio 集成按钮
# 查看实时日志
tail -f dev.log
# 搜索日志中的错误
grep "ERROR" dev.log
# 查看最近的转换记录
grep "CONVERT" dev.log | tail -20
# 数据库备份
cp prisma/dev.db prisma/dev.db.backup.$(date +%Y%m%d)
# 数据库恢复
cp prisma/dev.db.backup.20260110 prisma/dev.db
# 克隆项目
git clone <repository-url>
cd workspace
# 安装依赖(使用 Bun 更快)
bun install
# 或使用 npm
# npm install
# 初始化数据库
DATABASE_URL="file:./prisma/dev.db" bun run db:push
# 启动开发服务器
bun run dev
服务将在 http://localhost:3000 启动。
使用 curl 测试 URL 转换:
# 转换 GitHub URL
curl -X POST http://localhost:3000/api/convert \
-H "Content-Type: application/json" \
-d '{"url": "https://github.com/user/repo/blob/main/file.js"}'
# 响应示例
# {
# "originalUrl": "https://github.com/user/repo/blob/main/file.js",
# "convertedUrl": "https://hpyvnhay55-3000.cnb.run/gh/user/repo@main/file.js",
# "cached": false
# }
使用转换后的 URL 直接访问内容:
# 访问转换后的 CDN 资源
curl http://localhost:3000/cnb/hsred/test@main/README.md
访问 http://localhost:3000/admin 进入管理后台:
# 原始 URL
https://github.com/user/repo/blob/main/README.md
# 转换后
https://your-cdn.com/gh/user/repo@main/README.md
# 原始 URL
https://unpkg.com/react@18.2.0/umd/react.production.min.js
# 转换后
https://your-cdn.com/npm/react@18.2.0/umd/react.production.min.js
在管理后台添加黑名单规则:
path/api/internalblock所有匹配 /api/internal 的请求将被拦截。
POST /api/convert
转换原始 URL 为 CDN URL。
请求体:
{
"url": "https://github.com/user/repo/blob/main/file.js"
}
响应:
{
"originalUrl": "https://github.com/user/repo/blob/main/file.js",
"convertedUrl": "https://cdn.example.com/gh/user/repo@main/file.js",
"cached": false
}
GET /api/proxy?url=<encoded-url>
通过代理获取原始 URL 的内容。
示例:
GET /api/proxy?url=https%3A%2F%2Fcnb.cool%2Fhsred%2Ftest%2F-%2Fgit%2Fraw%2Fmain%2FREADME.md
GET /<converted-path>
直接访问转换后的路径获取内容。
示例:
GET /cnb/hsred/test@main/README.md
GET /api/status
获取服务健康状态和统计信息。
响应:
{
"healthy": true,
"totalConversions": 1000,
"avgResponseTime": 45,
"timestamp": "2026-01-10T08:00:00.000Z"
}
POST /api/admin/auth
管理员登录认证。
请求体:
{
"username": "admin",
"password": "password"
}
/api/admin/blacklist/rules - 获取黑名单规则列表/api/admin/blacklist/rules - 创建黑名单规则/api/admin/blacklist/rules - 更新黑名单规则/api/admin/blacklist/rules?id=<id> - 删除黑名单规则/api/admin/domains - 获取域名白名单列表/api/admin/domains - 创建域名白名单/api/admin/domains - 更新域名白名单/api/admin/domains?id=<id> - 删除域名白名单/api/admin/blacklist/config - 获取黑名单配置/api/admin/blacklist/config - 更新黑名单配置GET /api/admin/stats?period=7d
获取转换统计信息。
参数:
period: 时间周期(1d, 7d, 30d, 90d, all)响应:
{
"chartData": [...],
"topDomains": [...],
"totalStats": {
"total": 1000,
"success": 950,
"failed": 30,
"blocked": 20,
"avgResponseTime": 45
},
"ruleStats": {
"blacklistRules": 5,
"domainWhitelist": 3
}
}
GET /api/admin/logs?page=1&limit=20
获取操作日志列表。
检查 URL 路径是否包含指定字符串。
示例:
/README.mdhttps://example.com/README.md ✅https://example.com/docs/README.md ✅https://example.com/readme.txt ❌使用正则表达式进行高级匹配。
示例:
^/api/.*\.json$https://example.com/api/v1/users.json ✅https://example.com/api/data.json ✅https://example.com/api/v1/users ❌检查 URL 的域名是否匹配。
示例:
example.comhttps://example.com/path ✅https://api.example.com/path ✅https://other.com/path ❌直接返回 403 错误,拒绝访问。
重定向到指定 URL。
配置:
https://example.com/blocked返回自定义的响应内容。
配置:
访问被拒绝,请联系管理员匹配指定域名下的所有路径。
示例:
github.comhttps://github.com/user/repo ✅https://github.com/user/repo/blob/main/file.js ✅https://gitlab.com/user/repo ❌匹配指定域名下的特定路径前缀。
示例:
github.com/user/repohttps://github.com/user/repo ✅https://github.com/user/repo/blob/main/file.js ✅https://github.com/other/repo ❌使用正则表达式进行高级匹配。
示例:
example.com^/api/v\d+/https://example.com/api/v1/users ✅https://example.com/api/v2/data ✅https://example.com/api/users ❌原始 URL:
https://github.com/user/repo/blob/main/file.js
转换后:
https://cdn.example.com/gh/user/repo@main/file.js
原始 URL:
https://unpkg.com/package@1.0.0/file.js
转换后:
https://cdn.example.com/npm/package@1.0.0/file.js
原始 URL:
https://cnb.cool/group/subgroup/project/-/git/raw/main/file.js
转换后:
https://cdn.example.com/cnb/group/subgroup/project@main/file.js
在管理后台的"系统配置"中可以配置以下参数:
| 配置项 | 说明 | 默认值 |
|---|---|---|
ACCELERATOR_DOMAIN | CDN 加速域名 | localhost:3000 |
ENABLE_CACHE | 是否启用缓存 | true |
CACHE_TTL | 缓存有效期(秒) | 3600 |
REQUEST_TIMEOUT | 请求超时时间(毫秒) | 30000 |
MAX_REDIRECTS | 最大重定向次数 | 5 |
{
"enabled": true,
"globalMode": "block",
"actionOnMatch": "block",
"redirectUrl": "https://example.com/blocked",
"customResponse": "访问被拒绝",
"logMatches": true
}
// 高优先级规则(100)
{
"type": "regex",
"pattern": "^/api/v\\d+/admin",
"priority": 100,
"actionOnMatch": "block"
}
// 中等优先级规则(50)
{
"type": "path",
"pattern": "/api",
"priority": 50,
"actionOnMatch": "monitor"
}
// 默认优先级规则(0)
{
"type": "domain",
"pattern": "blocked.com",
"priority": 0,
"actionOnMatch": "redirect",
"redirectUrl": "https://safe.example.com"
}
// 在 next.config.ts 中配置
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=3600, s-maxage=3600',
},
],
},
]
}
}
# Caddyfile 示例 https://your-cdn.com { reverse_proxy localhost:3000 header / Cache-Control "public, max-age=3600" }
创建 .env.local 文件配置环境变量:
# 数据库 URL DATABASE_URL="file:./prisma/dev.db" # 管理员密码(可选) ADMIN_PASSWORD="your_secure_password"
主要配置在 next.config.ts:
const nextConfig: NextConfig = {
output: "standalone", // 独立部署模式
typescript: {
ignoreBuildErrors: true,
},
reactStrictMode: false,
eslint: {
ignoreDuringBuilds: true,
},
// ... 其他配置
}
配置文件在 tailwind.config.ts,支持自定义主题和样式。
问题: 构建时提示 DATABASE_URL is not set
解决:
DATABASE_URL="file:./prisma/dev.db" bun run build
问题: Prisma 迁移失败
解决:
# 重置数据库
bun run db:reset
# 或手动推送 schema
bun run db:push
问题: 端口 3000 已被使用
解决:
# 修改端口
bun run dev -p 3001
问题: 配置了黑名单规则但没有拦截请求
检查:
问题: 访问某些资源时提示超时
解决:
REQUEST_TIMEOUT 配置问题: 每次请求都访问源站,没有使用缓存
检查:
ENABLE_CACHE 配置为 trueCACHE_TTL 设置通过 API 获取统计信息:
# 获取最近 7 天的统计
curl http://localhost:3000/api/admin/stats?period=7d
# 响应示例
# {
# "chartData": [
# { "date": "2026-01-04", "success": 120, "failed": 5, "blocked": 3 },
# { "date": "2026-01-05", "success": 150, "failed": 8, "blocked": 2 }
# ],
# "topDomains": [
# { "domain": "github.com", "count": 500 },
# { "domain": "unpkg.com", "count": 300 }
# ],
# "totalStats": {
# "total": 1000,
# "success": 950,
# "failed": 30,
# "blocked": 20,
# "avgResponseTime": 45
# }
# }
# 获取操作日志
curl http://localhost:3000/api/admin/logs?page=1&limit=20
# 响应示例
# {
# "logs": [
# {
# "id": "xxx",
# "action": "CREATE",
# "module": "BlacklistRule",
# "details": "Created blacklist rule: path - /api/test",
# "createdAt": "2026-01-10T08:00:00.000Z"
# }
# ],
# "total": 100,
# "page": 1,
# "limit": 20
# }
/workspace/dev.log/workspace/server.log使用 Prisma Studio 查看数据库:
npx prisma studio
这将启动一个可视化的数据库管理界面,默认在 http://localhost:5555。
prisma/dev.db)不要提交敏感配置:
.env 文件*.db)使用环境变量:
# .env.local
DATABASE_URL="file:./prisma/dev.db"
ADMIN_PASSWORD="your_secure_password"
管理后台保护:
API 限流:
欢迎贡献代码!请遵循以下步骤:
git checkout -b feature/amazing-featuregit commit -m 'Add amazing feature'git push origin feature/amazing-feature<type>: <subject> <body> <footer>
类型:
feat: 新功能fix: 修复docs: 文档style: 格式refactor: 重构test: 测试chore: 构建/工具本项目采用 MIT 许可证。
如有问题或建议,请通过以下方式联系:
感谢以下开源项目的支持:
如有问题或建议,请提交 Issue 或 Pull Request。