fix(ui): подтверждать выполнение команд терминала

This commit is contained in:
Denozordec committed 2026-10-01 03:36:14 +07:00
1 parent bc20e5b9f4
commit a2eae189be
7 files changed
+205 -422

No files matched your search

+1
View File
@@ -227,6 +227,7 @@ export function permissionForPath(pathname: string): string | null {
if (pathname === "/" || pathname.startsWith("/dashboard")) {
return "mm:dashboard:read"
}
if (pathname.startsWith("/terminal")) return "mm:servers:read"
if (pathname.startsWith("/servers")) return "mm:servers:read"
if (pathname.startsWith("/filters") || pathname.startsWith("/gre")) {
return "mm:filters:read"
+6
View File
@@ -0,0 +1,6 @@
import {test} from "node:test"
import assert from "node:assert/strict"
import {terminalServersSchema,readTerminalOutput,readTerminalUrl,patchTerminalUrl} from "./terminal-response"
test("terminal catalog requires explicit state and unique IDs",()=>{const row={id:1,name:"",host:"192.0.2.1",country:"RU",enabled:true,status:null};assert.equal(terminalServersSchema.parse([row])[0].status,null);for(const raw of [{},[row,row],[{...row,status:undefined}],[{...row,enabled:undefined}]])assert.equal(terminalServersSchema.safeParse(raw).success,false)})
test("terminal output distinguishes empty success, malformed and legacy errors",()=>{assert.equal(readTerminalOutput({output:""}),"");assert.equal(readTerminalOutput({output:"0\nok"}),"0\nok");for(const raw of [{},{error:"fail"},{output:null},{output:"error: refused"},{output:"bad command name remove"},{output:"ok",error:"fail"}])assert.throws(()=>readTerminalOutput(raw))})
test("terminal URL refuses ambiguous/malformed context, preserves unrelated parameters",()=>{for(const q of ["server=1&host=x","server=1&server=2","host=","server=bad%20id"])assert.ok(readTerminalUrl(new URLSearchParams(q)).error);assert.equal(readTerminalUrl(new URLSearchParams("host=192.0.2.1")).host,"192.0.2.1");assert.equal(patchTerminalUrl(new URLSearchParams("host=x&ref=map"),"2"),"/terminal?ref=map&server=2")})
+24
View File
@@ -0,0 +1,24 @@
import {z} from "zod"
export const terminalServersSchema=z.array(z.object({
id:z.number().int().positive(),name:z.string(),host:z.string().min(1),country:z.string(),enabled:z.boolean(),
status:z.enum(["online","offline","degraded"]).nullable(),os:z.string().nullable().optional(),identityName:z.string().nullable().optional(),
})).refine(rows=>new Set(rows.map(row=>row.id)).size===rows.length)
const response=z.object({output:z.string().max(1024*1024)}).strict()
export function readTerminalOutput(raw:unknown) {
const data=response.safeParse(raw)
if(!data.success)throw new Error("Некорректный ответ API: вывод команды не подтверждён.")
if(/^(error:|bad command name |Usage:)/i.test(data.data.output))throw new Error(data.data.output)
return data.data.output
}
export function readTerminalUrl(params:URLSearchParams) {
const server=params.get("server"),host=params.get("host")
const error=["server","host"].some(key=>params.getAll(key).length>1)?"В ссылке повторяется устройство терминала."
:server!==null&&host!==null?"В ссылке одновременно указаны server и host."
:server!==null&&!/^[a-zA-Z0-9_.:-]{1,64}$/.test(server)?"В ссылке указан некорректный сервер."
:host!==null&&(!host.trim()||host.length>256||/[\u0000-\u001f]/.test(host))?"В ссылке указан некорректный host.":null
return {server,host,error}
}
export function patchTerminalUrl(params:URLSearchParams,server:string|null){
const next=new URLSearchParams(params);next.delete("server");next.delete("host");if(server)next.set("server",server)
return "/terminal"+(next.size?"?"+next.toString():"")
}