189 lines
5.8 KiB
TypeScript
189 lines
5.8 KiB
TypeScript
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<typeof setInterval> | null = null;
|
|
const qwinstaLoadingId = ref<number | null>(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,
|
|
};
|
|
}
|