import { onUnmounted, ref } from "vue"; import { fetchEventAction, postEventLogoff, postEventQwinsta, type AgentCommandResponse, type EventSummary, } from "../api"; import { rdgQwinstaEventId } from "../utils/rdgFlap"; export type QwinstaSessionRow = { sessionName: string; userName: string; id: number; state: string; }; function parseQwinstaSessions(stdout: string, actorUser: string): QwinstaSessionRow[] { const lines = stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean); const rows: QwinstaSessionRow[] = []; for (const line of lines) { if (/^SESSION/i.test(line) || /^---/.test(line)) continue; const parts = line.split(/\s+/); if (parts.length < 4) continue; const sessionName = parts[0].replace(/^>/, ""); const userName = parts[1]; const id = Number.parseInt(parts[2], 10); const state = parts.slice(3).join(" "); if (!Number.isFinite(id)) continue; const norm = (s: string) => s.replace(/^B26\\/i, "").toLowerCase(); const matchUser = actorUser && norm(userName).includes(norm(actorUser)); if (actorUser && !matchUser) continue; rows.push({ sessionName, userName, id, state }); } if (rows.length === 0 && stdout.trim()) { for (const line of lines) { if (/^SESSION/i.test(line) || /^---/.test(line)) continue; const parts = line.split(/\s+/); if (parts.length < 4) continue; const id = Number.parseInt(parts[2], 10); if (!Number.isFinite(id)) continue; rows.push({ sessionName: parts[0].replace(/^>/, ""), userName: parts[1], id, state: parts.slice(3).join(" "), }); } } return rows; } export function useRdgQwinsta() { let qwinstaPollTimer: ReturnType | null = null; const qwinstaLoadingId = ref(null); const qwinstaModal = ref({ open: false, eventId: 0, actorUser: "", commandUuid: "", target: "", clientHostname: "", internalIp: "", loading: false, error: "", stdout: "", sessions: [] as QwinstaSessionRow[], logoffId: null as number | null, }); function stopQwinstaPoll() { if (qwinstaPollTimer) { clearInterval(qwinstaPollTimer); qwinstaPollTimer = null; } } function closeQwinstaModal() { stopQwinstaPoll(); qwinstaModal.value.open = false; } function applyCommandResult(eventId: number, actorUser: string, cmd: AgentCommandResponse) { if (cmd.status === "pending") return false; qwinstaModal.value.loading = false; if (cmd.status === "failed") { qwinstaModal.value.error = cmd.result_stderr || cmd.result_stdout || "Команда завершилась с ошибкой"; return true; } qwinstaModal.value.stdout = cmd.result_stdout || ""; qwinstaModal.value.sessions = parseQwinstaSessions(qwinstaModal.value.stdout, actorUser); return true; } function applyCommandMeta(modal: typeof qwinstaModal.value, cmd: AgentCommandResponse) { modal.target = cmd.target || ""; modal.clientHostname = cmd.client_hostname || ""; modal.internalIp = cmd.internal_ip || ""; } function pollQwinstaCommand(eventId: number, commandUuid: string, actorUser: string) { stopQwinstaPoll(); qwinstaPollTimer = setInterval(async () => { try { const cmd = await fetchEventAction(eventId, commandUuid); if (applyCommandResult(eventId, actorUser, cmd)) { stopQwinstaPoll(); } } catch (e) { qwinstaModal.value.loading = false; qwinstaModal.value.error = e instanceof Error ? e.message : "Ошибка опроса команды"; stopQwinstaPoll(); } }, 2000); } async function runQwinsta(event: EventSummary) { const targetId = rdgQwinstaEventId(event) ?? event.id; qwinstaLoadingId.value = event.id; qwinstaModal.value = { open: true, eventId: targetId, actorUser: event.actor_user || "", commandUuid: "", target: "", clientHostname: "", internalIp: "", loading: true, error: "", stdout: "", sessions: [], logoffId: null, }; try { const cmd = await postEventQwinsta(targetId); applyCommandMeta(qwinstaModal.value, cmd); qwinstaModal.value.commandUuid = cmd.command_uuid; if (applyCommandResult(targetId, event.actor_user || "", cmd)) { return; } pollQwinstaCommand(targetId, cmd.command_uuid, event.actor_user || ""); } catch (e) { qwinstaModal.value.loading = false; qwinstaModal.value.error = e instanceof Error ? e.message : "Не удалось отправить qwinsta"; } finally { qwinstaLoadingId.value = null; } } async function runLogoff(sessionId: number) { const modal = qwinstaModal.value; if (!modal.eventId) return; if (!window.confirm(`Завершить сеанс ID ${sessionId}?`)) return; modal.logoffId = sessionId; modal.error = ""; try { const cmd = await postEventLogoff(modal.eventId, sessionId); applyCommandMeta(modal, cmd); if (cmd.status === "failed") { modal.error = cmd.result_stderr || cmd.result_stdout || "logoff failed"; return; } modal.loading = true; modal.stdout = ""; modal.sessions = []; const again = await postEventQwinsta(modal.eventId); applyCommandMeta(modal, again); if (applyCommandResult(modal.eventId, modal.actorUser, again)) { return; } pollQwinstaCommand(modal.eventId, again.command_uuid, modal.actorUser); } catch (e) { modal.error = e instanceof Error ? e.message : "logoff error"; } finally { modal.logoffId = null; } } onUnmounted(() => stopQwinstaPoll()); return { qwinstaLoadingId, qwinstaModal, closeQwinstaModal, runQwinsta, runLogoff, }; }