订阅和规则组支持从文件导入,配置页与表单一批打磨
文件导入走两步:内容先经新端点 POST /api/provider-files 写进内核工作 目录的 providers/ 下(路径白名单校验,只认两个路径助手生成的形状), 成了再写 file 类型的声明——顺序反了内核重载会去读不存在的文件。 file 类型没有地址和自动更新(内核靠文件监视跟进),详情页照此展示, 编辑按钮禁用。ini 导入的取文件按钮换成同一个拖放模块。 订阅的更新间隔改成开关语义:内核只在 interval > 0 时才起定时拉取 (component/resource/fetcher.go:226-228),关掉开关就是不写这个键。 配置页加「更新所有订阅」,详情的「立刻更新」改叫「更新订阅」。 规则表从零开始写入时自动垫上内网直连——保留地址不直连的话第一条 代理规则一生效,访问局域网的流量就全进了代理;按钮改成「补全内网 直连」,只在有缺口时出现。 所有表单对话框点遮罩关闭;失败横幅右侧加关闭按钮。
This commit is contained in:
+43
-5
@@ -18,8 +18,16 @@ export const DEFAULT_INTERVAL = 86400
|
||||
/** 一个用户订阅在界面上的样子。只读配置文件的 `proxy-providers` 段(决策记录 0004)。 */
|
||||
export interface Subscription {
|
||||
name: string
|
||||
/** http 定时从地址拉取;file 读本机文件,内核靠文件监视跟进变化 */
|
||||
type: 'http' | 'file'
|
||||
url: string
|
||||
/** 更新间隔,秒。null 表示配置里没写 */
|
||||
/** 线路文件的路径(http 类型是缓存,file 类型是数据本体) */
|
||||
path: string | null
|
||||
/**
|
||||
* 更新间隔,秒。null 表示配置里没写——内核只在 interval > 0 时才起定时
|
||||
* 拉取循环(内核源码 component/resource/fetcher.go:226-228),所以
|
||||
* 「没写」就是「不自动更新」,界面上它是个开关而不是必填数字。
|
||||
*/
|
||||
interval: number | null
|
||||
/** 健康检查。没写或 enable 不是 true 都算未开启 */
|
||||
healthCheck: { url: string | null; interval: number | null } | null
|
||||
@@ -49,6 +57,8 @@ export function listSubscriptions(data: ConfigObject): Subscription[] {
|
||||
const hc = isPlainObject(entry['health-check']) ? entry['health-check'] : null
|
||||
result.push({
|
||||
name,
|
||||
type: entry.type === 'file' ? 'file' : 'http',
|
||||
path: typeof entry.path === 'string' ? entry.path : null,
|
||||
url: typeof entry.url === 'string' ? entry.url : '',
|
||||
interval: typeof entry.interval === 'number' ? entry.interval : null,
|
||||
healthCheck:
|
||||
@@ -77,12 +87,37 @@ export function providerNames(data: ConfigObject): string[] {
|
||||
* `path` 自动给出确定的缓存文件路径,不让用户填:不写的话内核按 url 哈希自己生成一个,
|
||||
* 那个路径 Patchbay 算不出来,链式代理的副本就没法靠「读同一个缓存文件」认来源
|
||||
* (见 inbound-groups.ts 的 applySetup)。
|
||||
*
|
||||
* `interval` 为 null 时不写这个键,内核就不自动更新(见 Subscription.interval 的注)。
|
||||
*/
|
||||
export function addSubscription(data: ConfigObject, name: string, url: string): ConfigObject {
|
||||
export function addSubscription(
|
||||
data: ConfigObject,
|
||||
name: string,
|
||||
url: string,
|
||||
interval: number | null = DEFAULT_INTERVAL,
|
||||
): ConfigObject {
|
||||
const next = { ...data }
|
||||
next['proxy-providers'] = {
|
||||
...providersOf(data),
|
||||
[name]: { type: 'http', url, path: providerCachePath(name), interval: DEFAULT_INTERVAL },
|
||||
[name]: {
|
||||
type: 'http',
|
||||
url,
|
||||
path: providerCachePath(name),
|
||||
...(interval !== null ? { interval } : {}),
|
||||
},
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增一个来自本机文件的订阅。数据就在 path 指的文件里(内容由上传流程先写好),
|
||||
* 内核靠文件监视跟进变化,没有地址也没有更新间隔。
|
||||
*/
|
||||
export function addFileSubscription(data: ConfigObject, name: string): ConfigObject {
|
||||
const next = { ...data }
|
||||
next['proxy-providers'] = {
|
||||
...providersOf(data),
|
||||
[name]: { type: 'file', path: providerCachePath(name) },
|
||||
}
|
||||
return next
|
||||
}
|
||||
@@ -92,14 +127,17 @@ export function updateSubscription(
|
||||
data: ConfigObject,
|
||||
name: string,
|
||||
url: string,
|
||||
interval: number,
|
||||
interval: number | null,
|
||||
): ConfigObject {
|
||||
const entry = providersOf(data)[name]
|
||||
if (!isPlainObject(entry)) return data
|
||||
const updated: ConfigObject = { ...entry, url }
|
||||
if (interval === null) delete updated.interval
|
||||
else updated.interval = interval
|
||||
const next = { ...data }
|
||||
next['proxy-providers'] = {
|
||||
...providersOf(data),
|
||||
[name]: { ...entry, url, interval },
|
||||
[name]: updated,
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user