LunaTV 支持通过 Telegram Bot 实现无密码登录(Magic Link),用户只需输入 Telegram 用户名,即可通过 Bot 发送的链接完成登录。
- 🔐 无密码登录 - 通过 Telegram Bot 发送一次性登录链接
- 🤖 自动注册 - 首次登录的用户可自动创建账号
- ⏰ 安全过期 - 登录链接 5 分钟后自动过期
- 🌐 多存储支持 - 支持 Kvrocks、Redis、Upstash 存储 token
- 🔄 自动 Webhook 更新 - 自动将 webhook 设置到当前访问的域名
- 在 Telegram 中与 @BotFather 对话
- 发送
/newbot命令创建新 Bot - 按提示设置 Bot 名称和用户名
- 记录 BotFather 返回的 Bot Token 和 Bot Username
在 .env.local 或部署环境中设置存储类型:
# 存储类型:kvrocks、redis 或 upstash
NEXT_PUBLIC_STORAGE_TYPE=kvrocks
# Kvrocks 连接 URL(如果使用 Kvrocks)
KVROCKS_URL=redis://moontv-kvrocks:6666
# Redis 连接 URL(如果使用 Redis)
# REDIS_URL=redis://localhost:6379
# Upstash 配置(如果使用 Upstash)
# UPSTASH_URL=https://xxx.upstash.io
# UPSTASH_TOKEN=your_token_here- 登录管理后台
/admin - 进入 Telegram 登录配置 页面
- 填写以下信息:
- Bot Token: 从 BotFather 获取的 token
- Bot Username: Bot 的用户名(不含 @)
- 启用自动注册: 推荐开启,允许新用户自动创建账号
- 点击 保存配置
系统会在用户首次点击 Bot 链接时自动设置 webhook 到当前域名。
如需手动设置,在浏览器 Console 执行:
fetch('/api/telegram/set-webhook', {
method: 'POST'
}).then(r => r.json()).then(console.log)用户 -> 输入 Telegram 用户名
-> 系统生成一次性 token(5分钟有效期)
-> 系统发送深度链接到用户的 Telegram
-> 用户在 Telegram 点击 /start 链接
-> Bot 发送登录 URL
-> 用户点击 URL 完成登录
// Token 数据结构
interface TelegramTokenData {
telegramUsername: string; // Telegram 用户名
expiresAt: number; // 过期时间戳
baseUrl?: string; // 创建 token 的域名
}
// 存储位置:Redis/Kvrocks
// Key: cache:telegram_token:{token_hash}
// TTL: 300 秒(5分钟)系统会在接收到 Telegram 消息时自动检查 webhook URL 是否匹配当前域名,如果不匹配则自动更新:
// webhook.ts 自动更新逻辑
if (currentWebhookUrl !== expectedWebhookUrl) {
await setWebhook(expectedWebhookUrl);
}Telegram Bot 的 webhook 机制限制:一个 Bot 只能绑定一个 webhook URL。
多部署场景解决方案:
-
方案 A:每个部署使用独立的 Bot
- Vercel 部署 → Bot A
- 自建服务器 → Bot B
- 优点:互不干扰
- 缺点:需要管理多个 Bot
-
方案 B:只在一个域名启用 Telegram 登录
- 主域名启用 Telegram 登录
- 其他部署禁用或使用其他登录方式
- 优点:管理简单
- 缺点:功能受限
生成并发送 Magic Link
请求体:
{
"telegramUsername": "username"
}响应:
{
"success": true,
"deepLink": "https://t.me/yourbot?start=token_hash",
"botUsername": "yourbot"
}接收 Telegram Bot 消息(由 Telegram 服务器调用)
查询或设置 webhook URL
验证并消费 token,完成登录
查询参数:
token: 一次性登录 token
症状: 点击 Bot 发送的链接后显示"登录链接已过期或无效"
可能原因:
- Webhook 指向了其他域名(多部署冲突)
- Token 未正确存储到 Redis/Kvrocks
- 服务器时间不同步
解决方法:
# 1. 检查当前 webhook 配置
curl https://your-domain.com/api/telegram/set-webhook
# 2. 手动设置 webhook 到当前域名
curl -X POST https://your-domain.com/api/telegram/set-webhook
# 3. 检查 Kvrocks/Redis 中的 token
redis-cli -h kvrocks-host -p 6666
KEYS cache:telegram_token:*
GET cache:telegram_token:{token_hash}
TTL cache:telegram_token:{token_hash}症状: getWebhookInfo 显示 last_error_message: "401 Unauthorized"
原因: Webhook URL 返回了 401 状态码(通常是域名或路径错误)
解决方法:
- 确认域名可公开访问(不在防火墙后)
- 检查 webhook 路径是否正确:
/api/telegram/webhook - 检查中间件是否拦截了 webhook 请求
症状: 点击 Bot 链接后没有收到登录 URL
解决方法:
// 在浏览器访问你的域名,然后在 Console 执行
fetch('/api/telegram/set-webhook', {
method: 'POST'
}).then(r => r.json()).then(console.log)- Token 一次性使用 - verify 接口验证后立即删除 token
- 短期有效 - Token 仅 5 分钟有效
- 随机生成 - 使用 crypto.randomBytes(32) 生成 64 字符 token
- 域名绑定 - Token 记录创建时的域名,防止跨域使用
- 自动清理 - Redis/Kvrocks TTL 自动清理过期 token
所有 Telegram 相关操作都有详细日志输出:
# 查看应用日志
docker logs <container-name> --tail 100 --follow | grep -E "\[TelegramToken\]|\[Webhook\]|\[Magic Link\]"# 实时查看 Kvrocks 执行的命令
docker exec -it moontv-kvrocks redis-cli -p 6666 MONITOR# 手动测试 token 存储和读取
docker exec -it moontv-kvrocks redis-cli -p 6666
# 设置测试 token
SETEX cache:test_token 300 '{"telegramUsername":"test","expiresAt":1761999999999}'
# 读取
GET cache:test_token
# 检查 TTL
TTL cache:test_tokensrc/lib/telegram-tokens.ts- Token 管理逻辑src/app/api/telegram/send-magic-link/route.ts- 发送 Magic Linksrc/app/api/telegram/webhook/route.ts- Webhook 处理和自动更新src/app/api/telegram/verify/route.ts- Token 验证和登录src/app/api/telegram/set-webhook/route.ts- Webhook 配置管理src/components/TelegramAuthConfig.tsx- 管理后台配置界面
- v1.0.0 - 初始实现 Telegram Magic Link 登录
- v1.1.0 - 添加 webhook 自动更新功能
- v1.2.0 - 添加多部署域名冲突警告