Merge branch 'qb1'
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
commit
8e47d8715b
36 files changed
+2053
-89
No files matched your search
@@ -67,6 +67,19 @@ export function TestButton({
|
||||
|
||||
const credentialHint = { editHint: "留空不修改" };
|
||||
|
||||
const AUTH_KINDS = [
|
||||
{ value: "password", label: "用户名和密码" },
|
||||
{ value: "api_key", label: "API Key" },
|
||||
{ value: "none", label: "无认证(内网白名单)" },
|
||||
];
|
||||
|
||||
/** 已存认证方式;旧数据没有 settings.authKind 时按用户名 / 凭证推断(与服务端一致) */
|
||||
function authKindOf(r: DownloaderInstance): string {
|
||||
const k = (r.settings as { authKind?: unknown }).authKind;
|
||||
if (k === "password" || k === "api_key" || k === "none") return k;
|
||||
return r.username ? "password" : r.secretRef ? "api_key" : "none";
|
||||
}
|
||||
|
||||
export function DownloadersSection() {
|
||||
return (
|
||||
<SettingsBlock section="downloaders" anchor="instances" title="qBittorrent 实例" plain>
|
||||
@@ -121,8 +134,29 @@ function DownloadersTable() {
|
||||
required: true,
|
||||
placeholder: "http://192.168.1.10:8080",
|
||||
},
|
||||
{ name: "username", label: "用户名", kind: "text", hint: "留空时凭证按 WebUI API Key 使用" },
|
||||
{ name: "credential", label: "密码 / API Key", kind: "password", ...credentialHint },
|
||||
{
|
||||
name: "authKind",
|
||||
label: "认证方式",
|
||||
kind: "select",
|
||||
options: AUTH_KINDS,
|
||||
hint: "API Key 需要 qBittorrent 5.2 及以上,在 WebUI 设置里生成",
|
||||
},
|
||||
{ name: "username", label: "用户名", kind: "text", visible: (v) => v.authKind === "password" },
|
||||
{
|
||||
name: "password",
|
||||
label: "密码",
|
||||
kind: "password",
|
||||
visible: (v) => v.authKind === "password",
|
||||
...credentialHint,
|
||||
},
|
||||
{
|
||||
name: "apiKey",
|
||||
label: "API Key",
|
||||
kind: "password",
|
||||
placeholder: "qbt_…",
|
||||
visible: (v) => v.authKind === "api_key",
|
||||
...credentialHint,
|
||||
},
|
||||
{ name: "managedTag", label: "管理标签", kind: "text", placeholder: "umt", hint: "只接管带此标签的任务" },
|
||||
{ name: "cacheVolumeKey", label: "缓存卷标识", kind: "text", placeholder: "cache-1" },
|
||||
{ name: "enabled", label: "启用", kind: "switch" },
|
||||
@@ -130,8 +164,10 @@ function DownloadersTable() {
|
||||
initial: {
|
||||
name: "",
|
||||
baseUrl: "",
|
||||
authKind: "password",
|
||||
username: "",
|
||||
credential: "",
|
||||
password: "",
|
||||
apiKey: "",
|
||||
managedTag: "umt",
|
||||
cacheVolumeKey: "",
|
||||
enabled: true,
|
||||
@@ -139,8 +175,10 @@ function DownloadersTable() {
|
||||
fromRow: (r) => ({
|
||||
name: r.name,
|
||||
baseUrl: r.baseUrl,
|
||||
authKind: authKindOf(r),
|
||||
username: r.username ?? "",
|
||||
credential: "",
|
||||
password: "",
|
||||
apiKey: "",
|
||||
managedTag: r.managedTag,
|
||||
cacheVolumeKey: r.cacheVolumeKey ?? "",
|
||||
enabled: r.enabled,
|
||||
@@ -149,8 +187,10 @@ function DownloadersTable() {
|
||||
name: str(v.name),
|
||||
kind: mode === "create" ? "qbittorrent" : undefined,
|
||||
baseUrl: str(v.baseUrl),
|
||||
username: nullStr(v.username),
|
||||
credential: optStr(v.credential),
|
||||
authKind: str(v.authKind),
|
||||
username: v.authKind === "password" ? nullStr(v.username) : null,
|
||||
credential:
|
||||
v.authKind === "password" ? optStr(v.password) : v.authKind === "api_key" ? optStr(v.apiKey) : undefined,
|
||||
managedTag: optStr(v.managedTag),
|
||||
cacheVolumeKey: nullStr(v.cacheVolumeKey),
|
||||
enabled: v.enabled === true,
|
||||
@@ -307,6 +347,8 @@ export function LibraryRootsTable() {
|
||||
{ value: "register_only", label: "只登记" },
|
||||
{ value: "managed", label: "自动维护(补齐、升级)" },
|
||||
],
|
||||
hint: "自动维护:自动整理可把新下载放进来,文件管理可在其中改名、移动、删除;只登记:只扫描登记已有文件,不自动写入也不可改动。",
|
||||
wide: true,
|
||||
},
|
||||
{ name: "enabled", label: "启用", kind: "switch" },
|
||||
{ name: "notes", label: "备注", kind: "textarea" },
|
||||
|
||||
@@ -845,7 +845,9 @@ function DownloadsPanel({ detail }: { detail: LibraryDetail }) {
|
||||
<p className="text-meta text-muted-foreground">
|
||||
{d.plan
|
||||
? `${d.plan.purpose === "upgrade" ? "升级" : d.plan.purpose === "fill" ? "补缺" : "换源"}计划 · `
|
||||
: ""}
|
||||
: d.downloadTask.origin === "adopted"
|
||||
? "接管 · "
|
||||
: ""}
|
||||
{formatDateTime(d.downloadTask.createdAt)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -902,6 +904,9 @@ function DownloadsPanel({ detail }: { detail: LibraryDetail }) {
|
||||
),
|
||||
},
|
||||
];
|
||||
// 没有下载任务的整理记录(文件管理按路径手动整理等),下载任务的记录已在上表
|
||||
const listed = new Set(detail.downloads.map((d) => d.downloadTask.id));
|
||||
const manual = (detail.organizationRecords ?? []).filter((r) => !r.downloadTaskId || !listed.has(r.downloadTaskId));
|
||||
return (
|
||||
<Panel title="下载历史与校验结果" bodyClassName="p-0 sm:p-0">
|
||||
<DataTable
|
||||
@@ -909,8 +914,31 @@ function DownloadsPanel({ detail }: { detail: LibraryDetail }) {
|
||||
columns={columns}
|
||||
rows={detail.downloads}
|
||||
rowKey={(d) => d.downloadTask.id}
|
||||
empty={{ title: "还没有下载记录" }}
|
||||
empty={{ title: manual.length ? "没有下载任务,只有手动整理记录" : "还没有下载记录" }}
|
||||
/>
|
||||
{manual.length ? (
|
||||
<div className="border-t border-border/60 px-4 py-3 sm:px-5">
|
||||
<p className="mb-2 text-meta text-muted-foreground">其他整理记录</p>
|
||||
<ul className="space-y-1.5">
|
||||
{manual.map((r) => (
|
||||
<li key={r.id} className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<Tone v={ORG_STATUS[r.status]} />
|
||||
<Link
|
||||
to={consolePath("organize", r.id)}
|
||||
className="min-w-0 max-w-full truncate font-mono text-caption text-foreground hover:underline sm:max-w-md"
|
||||
title={r.sourcePath ?? r.id}
|
||||
>
|
||||
{r.targetPath ?? r.sourcePath ?? r.id}
|
||||
</Link>
|
||||
<span className="text-meta text-muted-foreground tabular-nums">
|
||||
{r.trigger === "manual" ? "手动" : r.trigger === "agent" ? "Agent" : "自动"} · {r.fileSucceeded}/
|
||||
{r.fileTotal} 个文件 · {formatDateTime(r.createdAt)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user