Merge branch 'deai2'
Co-Authored-By: Claude Fable 5.1 <[email protected]> # Conflicts: # apps/server/src/modules/acquisition-organize/services/organize.ts # apps/web/app/routes/console/sites.$siteId.tsx
This commit is contained in:
commit
12bc4664b6
247 files changed
+2658
-2148
No files matched your search
@@ -19,7 +19,7 @@ import { errorMessage } from "~/lib/api.ts";
|
||||
import { formatBytes } from "~/lib/utils.ts";
|
||||
|
||||
const STEPS = [
|
||||
"生成下载计划,预留版本目标和预算;每个版本目标同时只有一个在途计划",
|
||||
"生成下载计划,预留收录版本和预算;每个收录版本同时只有一个在途计划",
|
||||
"已有相同资源或相同 hash 的任务时直接接管,不重复提交",
|
||||
"缓存卷超过硬阈值或读不到磁盘状态时无法提交",
|
||||
"提交前复核免费、做种等易变字段",
|
||||
@@ -93,7 +93,7 @@ export function DownloadDialog({
|
||||
{slots.length ? (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<FilterSelect
|
||||
label="版本目标"
|
||||
label="收录版本"
|
||||
value={slotId}
|
||||
onChange={setSlotId}
|
||||
allLabel="自动匹配"
|
||||
@@ -103,7 +103,7 @@ export function DownloadDialog({
|
||||
) : (
|
||||
<p className="text-meta text-muted-foreground">
|
||||
{release.entity
|
||||
? "这个作品没有版本目标。先在订阅里新建。"
|
||||
? "这个作品没有收录版本。先在订阅里新建。"
|
||||
: "资源未关联本地作品。先在「身份与元数据」指定。"}
|
||||
</p>
|
||||
)}
|
||||
@@ -143,7 +143,7 @@ export function DownloadDialog({
|
||||
}}
|
||||
>
|
||||
<Bot />
|
||||
委托 Agent
|
||||
委托智能体
|
||||
</Button>
|
||||
<Button onClick={go} disabled={submit.isPending || !!done || !release.entity}>
|
||||
<Download />
|
||||
|
||||
@@ -122,14 +122,14 @@ export function FeedFormDialog({
|
||||
if (!parsed.success) {
|
||||
const e = zodFieldErrors(parsed.error.issues);
|
||||
if (e.pollIntervalSec) e.pollMin = "轮询间隔至少 1 分钟";
|
||||
if (e.url) e.url = feed ? "URL 格式不对" : "填写完整的 Feed URL";
|
||||
if (e.url) e.url = feed ? "URL 格式不对" : "填写完整的 RSS 地址";
|
||||
setErrors(e);
|
||||
return;
|
||||
}
|
||||
setErrors({});
|
||||
const done = {
|
||||
onSuccess: () => {
|
||||
toast.success(feed ? "已保存" : "Feed 已创建");
|
||||
toast.success(feed ? "已保存" : "RSS 已创建");
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (e: unknown) => toast.error(errorMessage(e)),
|
||||
@@ -142,7 +142,7 @@ export function FeedFormDialog({
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{feed ? "编辑 Feed" : "新建 Feed"}</DialogTitle>
|
||||
<DialogTitle>{feed ? "编辑 RSS" : "新建 RSS"}</DialogTitle>
|
||||
<DialogDescription>URL 加密保存,之后只显示脱敏地址。</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form
|
||||
|
||||
@@ -161,7 +161,12 @@ export function FeedTable({
|
||||
);
|
||||
},
|
||||
},
|
||||
{ key: "purpose", header: "用途", hideBelow: "md", cell: (f) => PURPOSE_LABEL[f.purpose] },
|
||||
{
|
||||
key: "purpose",
|
||||
header: "用途",
|
||||
hideBelow: "md",
|
||||
cell: (f) => (f.purpose === "generic" ? null : PURPOSE_LABEL[f.purpose]),
|
||||
},
|
||||
{
|
||||
key: "site",
|
||||
header: "站点 / 账号",
|
||||
@@ -208,8 +213,8 @@ export function FeedTable({
|
||||
selectedKey={openId}
|
||||
empty={{
|
||||
icon: Rss,
|
||||
title: filtered ? "没有符合筛选的 Feed" : "还没有 Feed",
|
||||
description: filtered ? undefined : "新建 Feed 后按轮询间隔抓取,新资源进入识别和收录评估。",
|
||||
title: filtered ? "没有符合筛选的 RSS" : "还没有 RSS",
|
||||
description: filtered ? undefined : "新建 RSS 后按轮询间隔抓取,新资源进入识别和收录评估。",
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ function LocalInventory({ release, entry }: { release: WorkbenchRelease; entry:
|
||||
) : (
|
||||
<>
|
||||
<p className="text-meta text-muted-foreground">
|
||||
{slots.length ? `${slots.length} 个版本目标` : "没有版本目标"}
|
||||
{slots.length ? `${slots.length} 个收录版本` : "没有收录版本"}
|
||||
{entry ? (entry.subscribed ? " · 已订阅" : " · 未订阅") : ""}
|
||||
</p>
|
||||
{candidates.length ? (
|
||||
@@ -78,7 +78,7 @@ function LocalInventory({ release, entry }: { release: WorkbenchRelease; entry:
|
||||
<li key={c.slotId} className="rounded-lg border border-border/70 px-3 py-2">
|
||||
<div className="flex flex-wrap items-center gap-2 text-caption">
|
||||
<StatusBadge tone={d.tone}>{d.label}</StatusBadge>
|
||||
<span className="text-foreground">{slot ? `目标 ${slot.name}` : "版本目标"}</span>
|
||||
<span className="text-foreground">{slot ? `目标 ${slot.name}` : "收录版本"}</span>
|
||||
<span className="text-muted-foreground">{relTime(c.evaluatedAt)}评估</span>
|
||||
</div>
|
||||
{c.reasons.length ? (
|
||||
@@ -173,7 +173,8 @@ export function ReleaseDetailSheet({
|
||||
})
|
||||
}
|
||||
>
|
||||
<Bot />问 Agent
|
||||
<Bot />
|
||||
问智能体
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => setDl(true)}>
|
||||
<Download />
|
||||
@@ -281,7 +282,7 @@ export function ReleaseDetailSheet({
|
||||
</DetailSection>
|
||||
|
||||
{detail.data?.observations.length ? (
|
||||
<DetailSection title="Feed 观察">
|
||||
<DetailSection title="RSS 抓取记录">
|
||||
<ul className="space-y-1 text-caption">
|
||||
{detail.data.observations.map((o) => (
|
||||
<li key={o.observationId} className="flex flex-wrap justify-between gap-2">
|
||||
|
||||
@@ -162,9 +162,9 @@ export function relTime(iso: string | null | undefined, now = Date.now()): strin
|
||||
}
|
||||
|
||||
export const ORIGIN_LABEL: Record<WorkbenchRelease["origin"], string> = {
|
||||
snapshot: "快照",
|
||||
feed: "Feed",
|
||||
live: "实时补查",
|
||||
snapshot: "抓取",
|
||||
feed: "RSS",
|
||||
live: "去站点搜",
|
||||
unknown: "来源未知",
|
||||
};
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ export function SpecTags({ r }: { r: Pick<WorkbenchRelease, "parsed" | "title" |
|
||||
/** 没有关联作品时的说明(明确写出,不猜) */
|
||||
export function unresolvedLabel(release: Pick<WorkbenchRelease, "resolutionStatus">): string {
|
||||
return release.resolutionStatus === "ambiguous"
|
||||
? "识别有歧义"
|
||||
? "没识别出作品"
|
||||
: release.resolutionStatus === "unidentifiable"
|
||||
? "无法识别"
|
||||
: release.resolutionStatus === "rejected"
|
||||
|
||||
@@ -10,10 +10,10 @@ import { formatDateTime } from "~/lib/utils.ts";
|
||||
import { relTime, SITE_HEALTH } from "./release-fields.ts";
|
||||
|
||||
const KIND: Record<SiteSnapshot["kind"], string> = {
|
||||
periodic: "定期快照",
|
||||
periodic: "定期抓取",
|
||||
backfill: "缺口补漏",
|
||||
live_search: "实时补查",
|
||||
manual: "手动快照",
|
||||
live_search: "去站点搜",
|
||||
manual: "手动抓取",
|
||||
};
|
||||
const STATUS: Record<SiteSnapshot["status"], { label: string; tone: StatusTone }> = {
|
||||
running: { label: "进行中", tone: "neutral" },
|
||||
@@ -44,10 +44,10 @@ export function SnapshotPanel({ site }: { site: Site }) {
|
||||
{!site.enabled ? <StatusBadge tone="warning">站点已停用</StatusBadge> : null}
|
||||
{site.snapshotEnabled ? (
|
||||
<StatusBadge tone={stale ? "warning" : "success"}>
|
||||
{stale ? "快照过期" : "快照新鲜"} · {relTime(site.lastSnapshotAt)}
|
||||
{stale ? "抓取已过期" : "已抓取"} · {relTime(site.lastSnapshotAt)}
|
||||
</StatusBadge>
|
||||
) : (
|
||||
<StatusBadge>未开启定期快照</StatusBadge>
|
||||
<StatusBadge>未开启定期抓取</StatusBadge>
|
||||
)}
|
||||
{site.snapshotIntervalSec ? (
|
||||
<span className="text-meta text-muted-foreground">
|
||||
@@ -64,14 +64,14 @@ export function SnapshotPanel({ site }: { site: Site }) {
|
||||
snap.mutate(
|
||||
{ id: site.id, maxPages: 2 },
|
||||
{
|
||||
onSuccess: () => toast.success(`${site.name}:快照已排队`),
|
||||
onSuccess: () => toast.success(`${site.name}:已开始抓取`),
|
||||
onError: (e) => toast.error(errorMessage(e)),
|
||||
},
|
||||
)
|
||||
}
|
||||
>
|
||||
<Camera />
|
||||
手动快照
|
||||
手动抓取
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
@@ -80,7 +80,7 @@ export function SnapshotPanel({ site }: { site: Site }) {
|
||||
) : snaps.error ? (
|
||||
<ErrorState plain error={snaps.error} onRetry={() => void snaps.refetch()} />
|
||||
) : !snaps.data.items.length ? (
|
||||
<p className="text-caption text-muted-foreground">还没有快照。</p>
|
||||
<p className="text-caption text-muted-foreground">还没抓取过。</p>
|
||||
) : (
|
||||
<ul className="divide-y divide-border/60 text-caption">
|
||||
{snaps.data.items.map((s) => (
|
||||
|
||||
Reference in new issue
Block a user