feat(server): gate scheduling, search and login by site capabilities (IA-2)

- snapshots only for sites with list; feed polling only for RSS kinds;
  user data / health checks only for sites with login; search site set
  only includes searchable sites; live search / snapshot skip with
  reason unsupported instead of hitting the site
- refresh-login / credentials need login, manual snapshot needs list (400)
- POST /sites accepts kind and rssUrl (site + feed in one transaction,
  key generated from host); RSS belongs to a site: feeds created without
  siteId go to the same-host custom RSS source site, siteId cannot be
  cleared
- fix: secret name clash when a site gets a second default-named feed
- command palette regrouped; agent site.list returns kind and
  capabilities; system prompt notes RSS lives under sites

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 00:29:56 +08:00
1 parent 89d2c81de6
commit ec65435328
15 files changed
+486 -80

No files matched your search

+11 -11
View File
@@ -1,6 +1,6 @@
import type { SearchAction } from "@umt/contracts";
// 页面与操作跳转(命令面板静态清单),按 IA-3 定稿导航:概览 / 资源 / 订阅 / 整理 / 系统(定时任务在任务页"计划"Tab)。
// 页面与操作跳转(命令面板静态清单),按 IA-2 导航:概览 / 获取 / 整理 / 系统(任务归系统;RSS 在站点详情里)。
// href 是控制台内路径;action 类带查询参数,由前端页面识别后打开对应表单。
const page = (id: string, group: string, title: string, href: string, keywords: string[] = []): SearchAction => ({
@@ -22,28 +22,28 @@ const action = (id: string, group: string, title: string, href: string, keywords
export const SEARCH_ACTIONS: readonly SearchAction[] = [
page("home", "概览", "首页", "/console", ["home", "dashboard", "概览"]),
page("tasks", "概览", "任务", "/console/tasks", ["task", "运行", "历史"]),
page("agent", "概览", "智能体会话", "/console/agent", ["agent", "会话"]),
page("inbox", "概览", "待处理", "/console/inbox", ["inbox", "收件箱", "decision"]),
page("sites", "资源", "站点", "/console/sites", ["site", "pt", "站点账号"]),
page("sources", "资源", "RSS 订阅", "/console/sources", ["rss", "feed"]),
page("subscriptions", "订阅", "订阅", "/console/subscriptions", ["subscription", "追剧"]),
page("rules", "订阅", "规则", "/console/rules", ["rules", "收录策略", "policy"]),
page("schedules", "概览", "计划", "/console/schedules", ["schedule", "cron", "巡检", "计划", "定时任务"]),
page("sites", "获取", "站点", "/console/sites", ["site", "pt", "站点账号", "rss", "feed", "bt"]),
page("subscriptions", "获取", "订阅", "/console/subscriptions", ["subscription", "追剧"]),
page("rules", "获取", "规则", "/console/rules", ["rules", "收录策略", "policy"]),
page("downloads", "整理", "下载面板", "/console/downloads", ["download", "qb", "下载任务"]),
page("download-plans", "整理", "下载计划", "/console/downloads/plans", ["plan", "计划"]),
page("organize", "整理", "整理记录", "/console/organize", ["organize", "入库", "历史"]),
page("files", "整理", "文件管理", "/console/files", ["files", "文件"]),
page("trash", "整理", "回收站", "/console/files/trash", ["trash", "删除"]),
page("library", "整理", "媒体库", "/console/library", ["library", "媒体库", "作品"]),
page("tasks", "系统", "任务", "/console/tasks", ["task", "运行", "历史"]),
page("settings", "系统", "设置", "/console/settings", ["settings", "配置", "tmdb", "下载器"]),
page("schedules", "概览", "定时任务", "/console/tasks?tab=plan", ["schedule", "cron", "巡检", "计划"]),
page("audit", "系统", "审计", "/console/audit", ["audit", "日志"]),
page("preferences", "系统", "偏好", "/console/preferences", ["preferences", "主题"]),
action("new-subscription", "订阅", "新建订阅", "/console/subscriptions?new=1", ["subscribe", "订阅"]),
action("new-subscription", "获取", "新建订阅", "/console/subscriptions?new=1", ["subscribe", "订阅"]),
action("new-agent-run", "概览", "新建智能体会话", "/console/agent?new=1", ["agent", "委托"]),
action("add-site", "资源", "添加站点", "/console/sites?new=1", ["site", "站点"]),
action("add-feed", "资源", "添加 RSS 订阅", "/console/sources?new=1", ["rss", "feed"]),
action("new-schedule", "概览", "新建巡检", "/console/tasks?tab=plan&new=1", ["巡检", "agent_check"]),
action("add-site", "获取", "添加站点", "/console/sites?new=1", ["site", "站点", "rss", "feed", "bt"]),
// RSS 属于站点(IA-2):添加 RSS 源 = 添加「自定义 RSS 源」类型的站点
action("add-feed", "获取", "添加自定义 RSS 源", "/console/sites?new=rss_feed", ["rss", "feed"]),
action("new-schedule", "概览", "新建巡检", "/console/schedules?new=1", ["巡检", "agent_check"]),
];
/** 标题 / 关键词 / 路径包含查询词(不区分大小写);标题前缀命中排前 */
+4 -2
View File
@@ -14,6 +14,7 @@ import { type SiteRow, sql } from "@umt/db";
import { applyFacets, type FacetInput, normalizeTitle, parseFacetParam, parseRelease } from "@umt/domain";
import type { SourcesRuntime } from "../sources-identity/runtime.ts";
import { upsertSiteRelease } from "../sources-identity/service/snapshot.ts";
import { siteCan } from "../sources-identity/site-kinds.ts";
import { raceAbort } from "./media.ts";
// 跨站资源搜索:选中的已启用站点逐站调适配器 search(每站共享限流器 + 单站超时 + 站点级并发上限);
@@ -63,15 +64,16 @@ export class ReleaseFanout {
private readonly opts: FanoutOptions,
) {}
/** 参与搜索的候选站点:启用且未删除(按名称排序) */
/** 参与搜索的候选站点:启用、未删除且支持搜索(IA-2 按站点类型与定义的获取方式;按名称排序) */
async enabledSites(): Promise<SiteRow[]> {
return this.rt()
const rows = await this.rt()
.ctx.db.selectFrom("site")
.selectAll()
.where("enabled", "=", true)
.where("deleted_at", "is", null)
.orderBy("name")
.execute();
return rows.filter((s) => siteCan(s, "search"));
}
/**