feat(telegram): admin-toggleable Bot registration + remove duplicate 最近入库 on 运行状态

- Add telegram.registration_enabled setting (default off); admins toggle via
  Settings page or /registration on|off bot command.
- Add /register (/reg /signup) bot command: when enabled, regular users can
  create a MediaStation account and auto-bind their Telegram. Reuses AuthService
  (username-taken / user-limit handling) and keeps new accounts as regular users.
- Regular users still limited to bind / adult-toggle / start / help; all other
  commands remain admin-only.
- Update /start and /help text to surface registration when enabled.
- Remove the duplicate 最近入库 module from the 运行状态 (stats) page; the homepage
  already shows recently added.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
soldosluka857
2026-05-30 08:48:43 +00:00
committed by Shuke
parent d03f84e78d
commit f0bc066134
5 changed files with 186 additions and 28 deletions
+14
View File
@@ -260,6 +260,20 @@ const GROUPS: SettingGroup[] = [
},
],
},
{
key: 'telegram',
label: 'Telegram Bot',
description: '机器人注册与普通用户权限',
items: [
{
key: 'telegram.registration_enabled',
label: '允许普通用户通过 Bot 注册',
type: 'toggle',
hint: '默认关闭。开启后用户可在 Telegram 中用 /register 用户名 密码 注册账号并自动绑定;关闭后注册被拒绝。普通用户始终只能绑定账号、开关成人目录显隐及使用 /start、/help。',
defaultValue: 'false',
},
],
},
// qBittorrent 配置已迁移到独立的「下载器」页面(侧边栏 → 下载器),
// 该页面支持多客户端 + 连接测试。这里不再重复暴露入口,避免与
// /api/admin/download/clients 写入的数据来源冲突。
+1 -20
View File
@@ -2,9 +2,7 @@ import { useEffect, useState } from 'react'
import { Activity, Cpu, Database, Film, HardDrive, Radio, Users } from 'lucide-react'
import { statsAPI } from '../api/stats'
import { MediaCard } from '../components/MediaCard'
import type { Hardware, StatsSnapshot } from '../types'
import { groupSeries } from '../utils/groupSeries'
// fmtBytes is a tiny helper shared by the dashboard cards.
function fmtBytes(n: number): string {
@@ -89,7 +87,6 @@ export function StatsPage() {
live.disk_total > 0
? (live.disk_used / live.disk_total) * 100
: 0
const recentlyAddedCards = groupSeries(snap.recently_added)
return (
<div className="space-y-8">
@@ -164,25 +161,9 @@ export function StatsPage() {
label="统计刷新"
value={new Date(snap.generated_at).toLocaleString()}
/>
<Row label="统计口径" value="媒体库、用户、容量、最近入库每 30 秒刷新" />
<Row label="统计口径" value="媒体库、用户、容量每 30 秒刷新" />
</div>
</section>
{recentlyAddedCards.length > 0 && (
<section className="space-y-3">
<h2 className="font-display text-xl font-semibold text-ink-600">最近入库</h2>
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
{recentlyAddedCards.map((s) => (
<MediaCard
key={s.key}
media={s.rep}
count={s.count}
linkTo={s.count > 1 ? `/library/${s.rep.library_id}?series=${encodeURIComponent(s.key)}` : undefined}
/>
))}
</div>
</section>
)}
</div>
)
}