CI / verify (push) Canceled after 0s
- 配置版本:fsutil 版本化备份(内容相同自动去重),主 nginx.conf 与 VPS vhost 的版本列表/逐行差异/一键回滚,回滚先校验语法与 nginx -t 再写回 - agent 0.12.2:写 vhost 前留版本;nginx -t 校验失败改为恢复上一个可用版本(此前直接删文件),新增域名 vhost 版本端点,双架构二进制已重编 - 通知:补企业微信 / 钉钉 / 飞书机器人,切换预设时服务地址跟随该预设默认值 - 阈值告警:新增 internal/monitor 巡检(CPU/内存/磁盘持续超阈值、隧道连续 N 次未注册),默认关闭,带静默期与恢复通知 - API Token:api_tokens 表 + Authorization Bearer 认证,只读/读写两种范围;令牌管理与改密/退出仅限登录会话
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
// 极简行级差异:Nginx 配置通常只有几十行,用 LCS 就够,不为此引入第三方 diff 依赖。
|
|
|
|
export interface DiffLine {
|
|
kind: 'same' | 'add' | 'del'
|
|
text: string
|
|
oldLine?: number
|
|
newLine?: number
|
|
}
|
|
|
|
// maxDiffCells 限制 LCS 表大小:超长文本(例如整份日志误粘进来)退化为「整体替换」,
|
|
// 避免 O(n*m) 的表把页面卡死。
|
|
const maxDiffCells = 250_000
|
|
|
|
export function diffLines(fromText: string, toText: string): DiffLine[] {
|
|
const from = splitLines(fromText)
|
|
const to = splitLines(toText)
|
|
|
|
if (from.length * to.length > maxDiffCells) {
|
|
return [
|
|
...from.map((text, index) => ({ kind: 'del' as const, text, oldLine: index + 1 })),
|
|
...to.map((text, index) => ({ kind: 'add' as const, text, newLine: index + 1 })),
|
|
]
|
|
}
|
|
|
|
// dp[i][j] = from[i..] 与 to[j..] 的最长公共子序列长度
|
|
const dp: number[][] = Array.from({ length: from.length + 1 }, () => new Array<number>(to.length + 1).fill(0))
|
|
for (let i = from.length - 1; i >= 0; i -= 1) {
|
|
for (let j = to.length - 1; j >= 0; j -= 1) {
|
|
dp[i][j] = from[i] === to[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1])
|
|
}
|
|
}
|
|
|
|
const out: DiffLine[] = []
|
|
let i = 0
|
|
let j = 0
|
|
while (i < from.length && j < to.length) {
|
|
if (from[i] === to[j]) {
|
|
out.push({ kind: 'same', text: from[i], oldLine: i + 1, newLine: j + 1 })
|
|
i += 1
|
|
j += 1
|
|
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
out.push({ kind: 'del', text: from[i], oldLine: i + 1 })
|
|
i += 1
|
|
} else {
|
|
out.push({ kind: 'add', text: to[j], newLine: j + 1 })
|
|
j += 1
|
|
}
|
|
}
|
|
while (i < from.length) {
|
|
out.push({ kind: 'del', text: from[i], oldLine: i + 1 })
|
|
i += 1
|
|
}
|
|
while (j < to.length) {
|
|
out.push({ kind: 'add', text: to[j], newLine: j + 1 })
|
|
j += 1
|
|
}
|
|
return out
|
|
}
|
|
|
|
export function diffStats(lines: DiffLine[]): { added: number; removed: number } {
|
|
let added = 0
|
|
let removed = 0
|
|
for (const line of lines) {
|
|
if (line.kind === 'add') added += 1
|
|
else if (line.kind === 'del') removed += 1
|
|
}
|
|
return { added, removed }
|
|
}
|
|
|
|
function splitLines(text: string): string[] {
|
|
if (text === '') return []
|
|
return text.replace(/\r\n/g, '\n').replace(/\n+$/, '').split('\n')
|
|
}
|