设置页重做:配置文件下载上传、内核参数、资源、免责声明
配置每次落盘都打顶层 patchbay: 标记(盐加版本的哈希)。内核忽略未知 顶层键(yaml.Unmarshal 没开严格模式,config/config.go:612)。刻意不对 内容做哈希:本项目只接管部分段落,手改其余段落是正常用法,内容哈希 会把合法手改判成无效。上传配置时靠标记分流:有标记的确认后替换, 没有的用更重的措辞确认导入;两者都走 config-store 完整保护链路 (新端点 POST /api/config/import)。自动备份列表从界面撤下,改成 下载当前配置 + 拖放上传。 内核参数一组编辑五个老式顶层端口和 keep-alive-interval(config.go:444, 整数秒),走带基准指纹的保存链路。资源一组接 POST /configs/geo (同步接口,透传给了 300 秒超时)。测速地址成为本地偏好,延迟测试 读它;prefs 模块顶层的浏览器全局加了存在性守卫——api.ts 开始引用 prefs,而 bun test 环境没有 localStorage。 退出登录挪到侧栏底部;密度设置从设置页撤下(侧栏按钮还在); 免责声明和关于做成子页(左上角返回),登录页输入框下加同一份 免责声明的弹层入口。
This commit is contained in:
+38
-2
@@ -12,9 +12,16 @@
|
||||
import { resolve, sep } from 'node:path'
|
||||
import listenerFields from '../shared/listener-fields.json' with { type: 'json' }
|
||||
import { isProviderFilePath } from '../shared/provider-files.ts'
|
||||
import { applyConfig, listBackups, readConfig, restoreBackup } from './config-store.ts'
|
||||
import { applyConfig, importConfig, listBackups, readConfig, restoreBackup } from './config-store.ts'
|
||||
import { hostInfo, probeExit } from './host.ts'
|
||||
import { kernelVersion, proxyGet, proxyWrite, restartKernel, selectProxy } from './kernel.ts'
|
||||
import {
|
||||
kernelVersion,
|
||||
proxyGet,
|
||||
proxyWrite,
|
||||
restartKernel,
|
||||
selectProxy,
|
||||
updateGeoDatabases,
|
||||
} from './kernel.ts'
|
||||
import { tokenFromRequest, tokenMatches } from './auth.ts'
|
||||
import type { ConfigObject } from '../vendor/config-editor/index.ts'
|
||||
import type { Env } from './env.ts'
|
||||
@@ -176,6 +183,27 @@ function makeRoute(env: Env, token: string) {
|
||||
return json({ backups: await listBackups(env) })
|
||||
}
|
||||
|
||||
// 配置文件原文,给「下载当前配置」用。带 attachment 让浏览器按下载处理
|
||||
if (path === '/api/config/raw' && req.method === 'GET') {
|
||||
const file = Bun.file(env.configPath)
|
||||
return new Response(file, {
|
||||
headers: {
|
||||
'Content-Type': 'text/yaml; charset=utf-8',
|
||||
'Content-Disposition': 'attachment; filename="config.yaml"',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 上传的 YAML 整份替换当前配置。走 config-store 完整的保护链路
|
||||
if (path === '/api/config/import' && req.method === 'POST') {
|
||||
const body = (await req.json()) as { content?: string }
|
||||
if (typeof body.content !== 'string' || body.content.trim() === '') {
|
||||
return json({ message: '请求缺少 content 字段' }, 400)
|
||||
}
|
||||
const result = await importConfig(env, body.content)
|
||||
return result.ok ? json(result) : json(result, applyFailureStatus(result.stage))
|
||||
}
|
||||
|
||||
const restoreMatch = /^\/api\/config\/backups\/([^/]+)\/restore$/.exec(path)
|
||||
if (restoreMatch && req.method === 'POST') {
|
||||
const name = decodeURIComponent(restoreMatch[1]!)
|
||||
@@ -236,6 +264,14 @@ function makeRoute(env: Env, token: string) {
|
||||
: json({ message: upstream.message || '内核拒绝了这次切换' }, 422)
|
||||
}
|
||||
|
||||
// 更新 GEO 数据库。运行时动作;同步接口,透传层给了长超时
|
||||
if (path === '/api/kernel/geo' && req.method === 'POST') {
|
||||
const upstream = await updateGeoDatabases(env)
|
||||
return upstream.ok
|
||||
? json({ ok: true })
|
||||
: json({ message: upstream.message || '内核更新 GEO 数据库失败' }, 502)
|
||||
}
|
||||
|
||||
// 重启内核进程。运行时动作,不碰配置文件;和其它写操作一样单独点名,不开通用口子
|
||||
if (path === '/api/kernel/restart' && req.method === 'POST') {
|
||||
const upstream = await restartKernel(env)
|
||||
|
||||
Reference in New Issue
Block a user