基于 Wasp 和 Open SaaS 构建的多租户工具发现站群系统,专为创建类似 findly.tools 的工具目录而设计。
- 多租户架构 - 一套代码支持多个域名/品牌
- 自动 Badge 巡检 - 要求工具网站展示 Badge,定期自动检查
- 图片自动处理 - 所有图片自动转换为 AVIF 格式并上传到 Cloudflare R2
- 防机器人保护 - 集成 Cloudflare Turnstile 验证码
- 邮件通知 - 使用 Resend API 发送审核和通知邮件
- 管理后台 - 完整的审核、管理和配置界面
- 框架: Wasp 0.18.0 (React + Node.js + Prisma)
- 数据库: PostgreSQL
- 缓存/队列: Redis + Bull
- 对象存储: Cloudflare R2 (S3 兼容)
- 图片处理: Sharp (AVIF 转换)
- 邮件: Resend API
- 验证码: Cloudflare Turnstile
- UI: React + Tailwind CSS + shadcn/ui
- 部署: Zeabur + Docker
- Node.js 18+
- PostgreSQL 14+
- Redis 7+
- Wasp CLI (
curl -sSL https://get.wasp-lang.dev/installer.sh | sh)
git clone <your-repo>
cd mklaunch/template/app
npm install复制 .env.server.example 为 .env.server 并填写配置:
cp .env.server.example .env.server关键环境变量:
DATABASE_URL- PostgreSQL 连接字符串REDIS_URL- Redis 连接字符串R2_*- Cloudflare R2 凭证RESEND_API_KEY- Resend 邮件 API 密钥TURNSTILE_*- Cloudflare Turnstile 密钥
wasp db migrate-dev
wasp db seed # 可选:添加示例数据wasp start应用将在 http://localhost:3000 启动。
每个租户包含:
- primaryDomain - 主域名
- extraDomains - 额外域名(JSON 数组)
- theme - 主题配置(颜色、Logo、Favicon)
- seo - SEO 配置(标题、描述、关键词)
- locale - 语言设置(默认 zh-CN)
系统通过 Host 头自动识别租户:
- 从 HTTP 请求的 Host 头提取域名
- 查询匹配的 Tenant(支持 primaryDomain 和 extraDomains)
- 将租户信息注入请求上下文(
req.tenant) - 所有数据库查询自动过滤
tenantId
// 通过 Prisma 或管理后台创建
await prisma.tenant.create({
data: {
name: "My Tool Directory",
primaryDomain: "tools.example.com",
extraDomains: ["www.tools.example.com"],
theme: {
primaryColor: "#667eea",
logo: "https://...",
favicon: "https://..."
},
seo: {
title: "My Tool Directory",
description: "Discover the best tools...",
keywords: "tools, directory, saas"
},
locale: "zh-CN"
}
});工具提交时必须在其网站上展示 Badge:
<a href="https://yourdomain.com/" target="_blank" rel="noopener">
<img src="https://yourdomain.com/static/badges/powered-by.svg"
alt="Powered by YourBrand" />
</a>- 即时校验: 提交时立即检查 Badge
- 定期巡检: 根据 CRON 配置定期检查(默认:每天 3AM 和 3PM)
- 失败策略:
- 第一次失败:发送警告邮件
- 连续两次失败:自动下线工具并发送通知
- 重试: 指数退避,最多 3 次
在 .env.server 中设置 CRON 表达式:
# 每天 3AM 和 3PM(默认)
BADGE_CHECK_CRON=0 3,15 * * *
# 每周一和周四上午 10 点
BADGE_CHECK_CRON=0 10 * * 1,4
# 每 6 小时
BADGE_CHECK_CRON=0 */6 * * *所有图片自动处理流程:
- 上传/提供 URL - Logo 或封面图片
- 下载原图 - 系统下载原始图片
- 转换 AVIF - 使用 Sharp 转换(quality: 80, effort: 4)
- 上传 R2 - 存储到 Cloudflare R2
- 记录数据库 - 在 Asset 表中记录
支持两种上传方式:
- 服务端转存 - 提供图片 URL,服务端下载转换上传
- 前端直传 - 获取预签名 URL,前端直接上传到 R2
- 在 Cloudflare Dashboard 创建 R2 bucket
- 生成 API 令牌
- 配置环境变量:
R2_ACCOUNT_IDR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_BUCKETR2_PUBLIC_BASE_URL
使用 Resend API 发送以下邮件:
- 审核通过 - 工具被批准并上线
- 审核驳回 - 工具被拒绝及原因
- Badge 警告 - 第一次检测不到 Badge
- 工具下线 - Badge 连续失败,工具被下线
- 在 Resend 注册账户
- 验证发件域名
- 生成 API 密钥
- 配置环境变量:
RESEND_API_KEY=re_xxxxx MAIL_FROM=Support <[email protected]>
- Cloudflare Turnstile - 防止机器人提交
- 速率限制 - Redis 实现的智能限流
- 提交:5 次/5 分钟(按 IP)
- 登录:5 次/5 分钟(按 IP)
- API:10 次/分钟(可配置)
- 租户隔离 - 所有查询强制 tenantId 过滤
- HTTPS Only - 生产环境强制 HTTPS
每个租户可独立配置主题:
{
"primaryColor": "#667eea",
"logo": "https://cdn.example.com/logo.avif",
"favicon": "https://cdn.example.com/favicon.ico",
"customCSS": "/* optional */"
}每个租户独立生成:
/sitemap.xml- 自动生成 XML Sitemap/robots.txt- 自定义爬虫规则/rss.xml- 最新工具 RSS Feed/llms.txt- LLM 抓取说明- Open Graph / Twitter Card meta 标签
- JSON-LD 结构化数据
访问 /admin 进入管理后台(需要管理员权限):
- Dashboard - 统计概览
- 审核队列 - 批准/驳回提交
- 工具管理 - 编辑、批量操作、手动复检
- 租户配置 - 域名、主题、SEO 设置
- 批量导入 - CSV/JSON 导入工具
- Owner - 完全控制
- Admin - 管理工具和审核
参见 DEPLOYMENT.md 了解详细的部署指南。
Zeabur 原生支持 Node.js/Wasp 项目,部署非常简单:
-
推送代码到 GitHub
git push origin main
-
在 Zeabur 创建项目
- 访问 Zeabur Dashboard
- 点击 "Create Project"
-
部署应用
- 选择 "Deploy from GitHub"
- 选择你的仓库
- Zeabur 自动检测并构建
-
添加服务(一键部署)
- PostgreSQL: Marketplace → PostgreSQL → Deploy
- Redis: Marketplace → Redis → Deploy
- 环境变量自动注入!
-
配置其他环境变量
- R2 凭证
- Resend API Key
- Turnstile 密钥
-
运行数据库迁移
- 使用 Zeabur Console 或 Web SQL
详细步骤请查看 DEPLOYMENT.md
GET /health- 健康检查GET /outbound/:toolId- 外链跳转(统计点击)POST /jobs/badge-cron- Badge 巡检触发(Cron)
GET /upload/presign- 获取上传预签名 URL
getTools- 获取工具列表(支持分页、筛选、搜索)getToolBySlug- 获取单个工具getCategories- 获取分类列表getTags- 获取标签列表submitTool- 提交新工具
1. Turnstile 验证失败
- 检查
TURNSTILE_SITE_KEY和TURNSTILE_SECRET_KEY - 开发环境自动跳过验证
2. Badge 检查总是失败
- 确保 Badge 在页面 HTML 中可见
- 检查
<a>标签没有rel="nofollow" - 确保图片 URL 可访问
3. 图片上传失败
- 检查 R2 凭证配置
- 确认 bucket 权限正确
- 检查图片大小(< 10MB)和格式
4. 邮件未发送
- 检查
RESEND_API_KEY - 确认发件域名已验证
- 查看服务器日志
5. Redis 连接失败
- 检查
REDIS_URL格式 - 确认 Redis 服务运行中
- 检查网络连接
MIT
欢迎提交 Issues 和 Pull Requests!
注意: 这是一个生产就绪的模板,但在实际部署前请:
- 修改所有默认配置和密钥
- 配置生产环境数据库备份
- 设置监控和日志系统
- 进行安全审计
- 配置 CDN 和缓存策略