反馈改右上角轻提示;侧栏加撤回重做等配置动作
设置页的横幅换成 toast(右滑入、几秒后上移淡出,web/toast.ts 单例): 动作结果看一眼就行,不值得在页面里占一块常驻位置。表单校验类的错误 仍留在各自表单里。 撤回栈就是备份链:撤回把最新备份应用回去并消费掉它,撤回前的状态进 重做栈(.patchbay-redo,同样留 10 份);重做反向;新的正常保存清空 重做栈,不留分叉历史。这两条路复用 applyConfig 的保护链路,用 restoreFrom 参数跳过自建备份——否则备份链会被撤回本身污染,撤两次 等于原地打转。侧栏左下角一排图标:撤回、重做、历史记录(备份列表 弹窗)、更新配置(POST /api/kernel/reload,给 SSH 手改后用)、重启 内核;收拢时容器变窄自动折成一列。 设置页顺手修三处:GEO 行的编辑按钮在输入框开着时再点会闪(blur 关了 click 又开),mousedown.prevent 保焦点、click 显式开关;上传按钮改 「选择或拖放文件」,投放区拖入高亮;恢复策略挪到恢复行下面。
This commit is contained in:
@@ -2,7 +2,16 @@ import { afterAll, describe, expect, it } from 'bun:test'
|
||||
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { applyConfig, importConfig, listBackups, readConfig, restoreBackup } from './config-store.ts'
|
||||
import {
|
||||
applyConfig,
|
||||
importConfig,
|
||||
listBackups,
|
||||
listRedo,
|
||||
readConfig,
|
||||
redoConfig,
|
||||
restoreBackup,
|
||||
undoConfig,
|
||||
} from './config-store.ts'
|
||||
import type { Env } from './env.ts'
|
||||
|
||||
/**
|
||||
@@ -184,6 +193,48 @@ describe('落盘保护链路(内核在线)', () => {
|
||||
if (!result.ok) expect(result.message).toContain('没有这个备份')
|
||||
})
|
||||
|
||||
it('撤回回到改动前,重做拿回改动,备份链不被撤回污染', async () => {
|
||||
const env = makeEnv()
|
||||
const first = await readConfig(env)
|
||||
await applyConfig(env, { ...first.data, 'mixed-port': 7891 }, first.revision)
|
||||
|
||||
const undone = await undoConfig(env)
|
||||
expect(undone.ok).toBe(true)
|
||||
expect(readFileSync(env.configPath, 'utf8')).toContain('mixed-port: 7890')
|
||||
// 备份被消费、当前状态进了重做栈
|
||||
expect(await listBackups(env)).toHaveLength(0)
|
||||
expect(await listRedo(env)).toHaveLength(1)
|
||||
|
||||
const redone = await redoConfig(env)
|
||||
expect(redone.ok).toBe(true)
|
||||
expect(readFileSync(env.configPath, 'utf8')).toContain('mixed-port: 7891')
|
||||
expect(await listRedo(env)).toHaveLength(0)
|
||||
expect(await listBackups(env)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('栈空时撤回和重做被拦住,文件不动', async () => {
|
||||
const env = makeEnv()
|
||||
const undone = await undoConfig(env)
|
||||
expect(undone.ok).toBe(false)
|
||||
if (!undone.ok) expect(undone.message).toContain('没有可以撤回')
|
||||
|
||||
const redone = await redoConfig(env)
|
||||
expect(redone.ok).toBe(false)
|
||||
expect(readFileSync(env.configPath, 'utf8')).toBe(ORIGINAL)
|
||||
})
|
||||
|
||||
it('新的保存清空重做栈', async () => {
|
||||
const env = makeEnv()
|
||||
const first = await readConfig(env)
|
||||
await applyConfig(env, { ...first.data, 'mixed-port': 7891 }, first.revision)
|
||||
await undoConfig(env)
|
||||
expect(await listRedo(env)).toHaveLength(1)
|
||||
|
||||
const { data, revision } = await readConfig(env)
|
||||
await applyConfig(env, { ...data, 'mixed-port': 7892 }, revision)
|
||||
expect(await listRedo(env)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('备份最多留 10 份,旧的被清掉', async () => {
|
||||
const env = makeEnv()
|
||||
const dir = join(env.configPath, '..', '.patchbay-backups')
|
||||
|
||||
Reference in New Issue
Block a user