feat: agent updates from SAC, poll config, SSH/WinRM fallback (0.12.0)
Add agent update settings, pending self-update via poll, desired config per host, and automatic or manual SSH/WinRM fallback when agents do not report agent.update events.
This commit is contained in:
@@ -230,6 +230,8 @@ export interface HostSummary {
|
||||
last_daily_report_at: string | null;
|
||||
last_inventory_at?: string | null;
|
||||
agent_status: "online" | "stale" | "unknown";
|
||||
version_status?: "ok" | "outdated" | "unknown";
|
||||
pending_agent_update?: boolean;
|
||||
ssh_admin_ok?: boolean | null;
|
||||
ssh_admin_checked_at?: string | null;
|
||||
}
|
||||
@@ -242,6 +244,13 @@ export interface HostDetail extends HostSummary {
|
||||
inventory: Record<string, unknown> | null;
|
||||
inventory_updated_at: string | null;
|
||||
created_at: string | null;
|
||||
pending_update_requested_at?: string | null;
|
||||
pending_update_target_version?: string | null;
|
||||
agent_config_revision?: number;
|
||||
agent_config?: Record<string, unknown> | null;
|
||||
agent_update_state?: string | null;
|
||||
agent_update_last_at?: string | null;
|
||||
agent_update_last_error?: string | null;
|
||||
}
|
||||
|
||||
export function fetchHost(id: number): Promise<HostDetail> {
|
||||
@@ -495,6 +504,66 @@ export function updateHostAgentViaSsh(hostId: number): Promise<HostSshActionResu
|
||||
});
|
||||
}
|
||||
|
||||
export interface HostAgentUpdateRequestResult {
|
||||
status: string;
|
||||
pending_agent_update: boolean;
|
||||
target_version: string | null;
|
||||
agent_update_state: string | null;
|
||||
}
|
||||
|
||||
export function requestHostAgentUpdate(hostId: number): Promise<HostAgentUpdateRequestResult> {
|
||||
return apiFetch<HostAgentUpdateRequestResult>(
|
||||
`/api/v1/hosts/${hostId}/actions/request-agent-update`,
|
||||
{ method: "POST" },
|
||||
);
|
||||
}
|
||||
|
||||
export function runHostAgentUpdateFallback(hostId: number): Promise<HostSshActionResult> {
|
||||
return apiFetch<HostSshActionResult>(`/api/v1/hosts/${hostId}/actions/agent-update-fallback`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export interface HostAgentConfigResponse {
|
||||
config_revision: number;
|
||||
settings: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export function patchHostAgentConfig(
|
||||
hostId: number,
|
||||
settings: Record<string, unknown>,
|
||||
): Promise<HostAgentConfigResponse> {
|
||||
return apiFetch<HostAgentConfigResponse>(`/api/v1/hosts/${hostId}/agent-config`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ settings }),
|
||||
});
|
||||
}
|
||||
|
||||
export interface AgentUpdateSettings {
|
||||
mode: "gpo" | "sac";
|
||||
fallback_enabled: boolean;
|
||||
fallback_after_minutes: number;
|
||||
recommended_rdp_version: string | null;
|
||||
recommended_ssh_version: string | null;
|
||||
min_rdp_version: string | null;
|
||||
min_ssh_version: string | null;
|
||||
win_agent_update_script: string | null;
|
||||
source: string;
|
||||
}
|
||||
|
||||
export function fetchAgentUpdateSettings(): Promise<AgentUpdateSettings> {
|
||||
return apiFetch<AgentUpdateSettings>("/api/v1/settings/agent-updates");
|
||||
}
|
||||
|
||||
export function saveAgentUpdateSettings(
|
||||
payload: Partial<AgentUpdateSettings>,
|
||||
): Promise<AgentUpdateSettings> {
|
||||
return apiFetch<AgentUpdateSettings>("/api/v1/settings/agent-updates", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export interface DashboardSummary {
|
||||
events_last_24h: number;
|
||||
hosts_total: number;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */
|
||||
export const APP_NAME = "Security Alert Center";
|
||||
export const APP_VERSION = "0.11.6";
|
||||
export const APP_VERSION = "0.12.0";
|
||||
export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;
|
||||
|
||||
@@ -20,7 +20,23 @@
|
||||
</div>
|
||||
<div class="detail-field">
|
||||
<dt>Версия агента</dt>
|
||||
<dd>{{ host.product_version || "—" }}</dd>
|
||||
<dd>
|
||||
<span :class="versionStatusClass">{{ host.product_version || "—" }}</span>
|
||||
<span v-if="host.version_status === 'outdated'" class="muted version-hint"> (устарела)</span>
|
||||
<span v-if="host.pending_agent_update" class="pending-update-badge">обновление запрошено</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div v-if="host.agent_update_state" class="detail-field">
|
||||
<dt>Обновление</dt>
|
||||
<dd>
|
||||
{{ host.agent_update_state }}
|
||||
<span v-if="host.pending_update_target_version"> → {{ host.pending_update_target_version }}</span>
|
||||
<span v-if="host.agent_update_last_at" class="muted"> ({{ formatDt(host.agent_update_last_at) }})</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div v-if="host.agent_update_last_error" class="detail-field">
|
||||
<dt>Ошибка обновления</dt>
|
||||
<dd class="error-inline">{{ host.agent_update_last_error }}</dd>
|
||||
</div>
|
||||
<div class="detail-field">
|
||||
<dt>OS</dt>
|
||||
@@ -57,6 +73,25 @@
|
||||
{{ testingWinRm ? "Проверка…" : "Проверить WinRM (admin)" }}
|
||||
</button>
|
||||
<p v-if="winRmTestMessage" :class="winRmTestOk ? 'success' : 'error'">{{ winRmTestMessage }}</p>
|
||||
<div class="host-actions-row">
|
||||
<button
|
||||
type="button"
|
||||
class="secondary"
|
||||
:disabled="requestingAgentUpdate"
|
||||
@click="runRequestAgentUpdate"
|
||||
>
|
||||
{{ requestingAgentUpdate ? "Запрос…" : "Запросить обновление (SAC)" }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="secondary"
|
||||
:disabled="runningAgentFallback"
|
||||
@click="runAgentFallback"
|
||||
>
|
||||
{{ runningAgentFallback ? "Обновление…" : "Обновить через WinRM" }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="agentControlMessage" :class="agentControlOk ? 'success' : 'error'">{{ agentControlMessage }}</p>
|
||||
</div>
|
||||
<div v-if="isLinuxHost" class="host-actions">
|
||||
<div class="host-actions-row">
|
||||
@@ -81,9 +116,26 @@
|
||||
:disabled="updatingAgent"
|
||||
@click="runAgentUpdate"
|
||||
>
|
||||
{{ updatingAgent ? "Обновление…" : "Обновить ssh-monitor" }}
|
||||
{{ updatingAgent ? "Обновление…" : "Обновить ssh-monitor (SSH)" }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="secondary"
|
||||
:disabled="requestingAgentUpdate"
|
||||
@click="runRequestAgentUpdate"
|
||||
>
|
||||
{{ requestingAgentUpdate ? "Запрос…" : "Запросить обновление (SAC)" }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="secondary"
|
||||
:disabled="runningAgentFallback"
|
||||
@click="runAgentFallback"
|
||||
>
|
||||
{{ runningAgentFallback ? "Fallback…" : "Fallback SSH" }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="agentControlMessage" :class="agentControlOk ? 'success' : 'error'">{{ agentControlMessage }}</p>
|
||||
<div v-if="showSshTestFeedback && (sshTestMessage || sshTestOutput)" class="host-action-feedback">
|
||||
<p :class="sshTestOk ? 'success' : 'error'">{{ sshTestMessage }}</p>
|
||||
<pre v-if="sshTestOutput" class="host-ssh-output">{{ sshTestOutput }}</pre>
|
||||
@@ -95,6 +147,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section v-if="showAgentConfig" class="card host-agent-config">
|
||||
<h2>Desired config (poll агента)</h2>
|
||||
<p class="muted">Агент заберёт настройки при следующем poll. Revision: {{ host.agent_config_revision ?? 0 }}</p>
|
||||
<form class="agent-config-form" @submit.prevent="saveAgentConfig">
|
||||
<label v-if="isWindowsHost" class="config-field">
|
||||
ServerDisplayName
|
||||
<input v-model="configForm.serverDisplayName" type="text" />
|
||||
</label>
|
||||
<label v-if="isLinuxHost" class="config-field">
|
||||
SERVER_DISPLAY_NAME
|
||||
<input v-model="configForm.serverDisplayName" type="text" />
|
||||
</label>
|
||||
<label v-if="isWindowsHost" class="config-inline">
|
||||
<input v-model="configForm.getInventory" type="checkbox" />
|
||||
GetInventory
|
||||
</label>
|
||||
<button type="submit" :disabled="savingAgentConfig">
|
||||
{{ savingAgentConfig ? "Сохранение…" : "Сохранить config" }}
|
||||
</button>
|
||||
<p v-if="agentConfigMessage" :class="agentConfigOk ? 'success' : 'error'">{{ agentConfigMessage }}</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section v-if="inventory" class="host-inventory card">
|
||||
<h2>Железо и ПО</h2>
|
||||
<div v-if="windowsInfo" class="inv-block">
|
||||
@@ -204,7 +279,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useSacLiveEventStream } from "../composables/useSacLiveEventStream";
|
||||
import {
|
||||
@@ -214,6 +289,9 @@ import {
|
||||
testHostWinRm,
|
||||
testHostSsh,
|
||||
updateHostAgentViaSsh,
|
||||
requestHostAgentUpdate,
|
||||
runHostAgentUpdateFallback,
|
||||
patchHostAgentConfig,
|
||||
type EventListResponse,
|
||||
type HostDetail,
|
||||
} from "../api";
|
||||
@@ -244,6 +322,17 @@ const sshTestOutput = ref("");
|
||||
const agentUpdateMessage = ref("");
|
||||
const agentUpdateOk = ref(false);
|
||||
const agentUpdateOutput = ref("");
|
||||
const requestingAgentUpdate = ref(false);
|
||||
const runningAgentFallback = ref(false);
|
||||
const agentControlMessage = ref("");
|
||||
const agentControlOk = ref(false);
|
||||
const savingAgentConfig = ref(false);
|
||||
const agentConfigMessage = ref("");
|
||||
const agentConfigOk = ref(false);
|
||||
const configForm = reactive({
|
||||
serverDisplayName: "",
|
||||
getInventory: true,
|
||||
});
|
||||
let sshTestHideTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let sshProbeSeq = 0;
|
||||
let refreshingLive = false;
|
||||
@@ -278,6 +367,23 @@ const sshVerifiedTitle = computed(() => {
|
||||
return "SSH доступен";
|
||||
});
|
||||
|
||||
const versionStatusClass = computed(() => {
|
||||
const status = host.value?.version_status;
|
||||
if (status === "outdated") return "agent-version-outdated";
|
||||
if (status === "ok") return "agent-version-ok";
|
||||
return "";
|
||||
});
|
||||
|
||||
const showAgentConfig = computed(() => isWindowsHost.value || isLinuxHost.value);
|
||||
|
||||
function syncConfigFormFromHost(detail: HostDetail) {
|
||||
const cfg = detail.agent_config ?? {};
|
||||
configForm.serverDisplayName = String(
|
||||
cfg.ServerDisplayName ?? cfg.SERVER_DISPLAY_NAME ?? "",
|
||||
);
|
||||
configForm.getInventory = cfg.GetInventory !== false;
|
||||
}
|
||||
|
||||
const inventory = computed(() => host.value?.inventory ?? null);
|
||||
|
||||
const windowsFields = computed(() => {
|
||||
@@ -467,9 +573,75 @@ function formatSshOutput(result: { stdout: string | null; stderr: string | null;
|
||||
|
||||
function applyHostDetail(detail: HostDetail) {
|
||||
host.value = detail;
|
||||
syncConfigFormFromHost(detail);
|
||||
emitHostPatch(detail);
|
||||
}
|
||||
|
||||
async function runRequestAgentUpdate() {
|
||||
requestingAgentUpdate.value = true;
|
||||
agentControlMessage.value = "";
|
||||
try {
|
||||
const result = await requestHostAgentUpdate(hostId.value);
|
||||
agentControlOk.value = true;
|
||||
agentControlMessage.value = result.target_version
|
||||
? `Запрошено обновление до ${result.target_version}`
|
||||
: "Запрошено обновление агента";
|
||||
await loadHost();
|
||||
} catch (e) {
|
||||
agentControlOk.value = false;
|
||||
agentControlMessage.value = e instanceof Error ? e.message : "Не удалось запросить обновление";
|
||||
} finally {
|
||||
requestingAgentUpdate.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function runAgentFallback() {
|
||||
runningAgentFallback.value = true;
|
||||
agentControlMessage.value = "";
|
||||
try {
|
||||
const result = await runHostAgentUpdateFallback(hostId.value);
|
||||
agentControlOk.value = result.ok;
|
||||
agentControlMessage.value = result.message;
|
||||
if (result.ok && result.product_version && host.value) {
|
||||
host.value = { ...host.value, product_version: result.product_version };
|
||||
emitHostPatch(host.value);
|
||||
}
|
||||
await loadHost();
|
||||
} catch (e) {
|
||||
agentControlOk.value = false;
|
||||
agentControlMessage.value = e instanceof Error ? e.message : "Ошибка fallback-обновления";
|
||||
} finally {
|
||||
runningAgentFallback.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAgentConfig() {
|
||||
savingAgentConfig.value = true;
|
||||
agentConfigMessage.value = "";
|
||||
const settings: Record<string, unknown> = {};
|
||||
if (isWindowsHost.value) {
|
||||
if (configForm.serverDisplayName.trim()) {
|
||||
settings.ServerDisplayName = configForm.serverDisplayName.trim();
|
||||
}
|
||||
settings.GetInventory = configForm.getInventory;
|
||||
} else if (isLinuxHost.value) {
|
||||
if (configForm.serverDisplayName.trim()) {
|
||||
settings.SERVER_DISPLAY_NAME = configForm.serverDisplayName.trim();
|
||||
}
|
||||
}
|
||||
try {
|
||||
const result = await patchHostAgentConfig(hostId.value, settings);
|
||||
agentConfigOk.value = true;
|
||||
agentConfigMessage.value = `Config сохранён (revision ${result.config_revision})`;
|
||||
await loadHost();
|
||||
} catch (e) {
|
||||
agentConfigOk.value = false;
|
||||
agentConfigMessage.value = e instanceof Error ? e.message : "Ошибка сохранения config";
|
||||
} finally {
|
||||
savingAgentConfig.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadHost() {
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
@@ -636,4 +808,49 @@ watch(
|
||||
font-size: 0.85rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.agent-version-outdated {
|
||||
color: #f0883e;
|
||||
}
|
||||
|
||||
.agent-version-ok {
|
||||
color: #3fb950;
|
||||
}
|
||||
|
||||
.pending-update-badge {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: #58a6ff;
|
||||
}
|
||||
|
||||
.version-hint {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.error-inline {
|
||||
color: #f85149;
|
||||
}
|
||||
|
||||
.host-agent-config {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.agent-config-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
max-width: 28rem;
|
||||
}
|
||||
|
||||
.config-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.config-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -200,6 +200,8 @@ function agentLabel(status: string) {
|
||||
}
|
||||
|
||||
function isVersionOutdated(h: HostSummary): boolean {
|
||||
if (h.version_status === "outdated") return true;
|
||||
if (h.version_status === "ok") return false;
|
||||
const latest = data.value?.latest_agent_versions?.[h.product];
|
||||
return isAgentVersionOutdated(h.product_version, latest);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,56 @@
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card settings-card settings-policy">
|
||||
<h2>Обновления агентов</h2>
|
||||
<p class="settings-hint settings-hint-top">
|
||||
Режим <code>gpo</code> — обновления вручную/GPO; SAC только показывает устаревшие версии.
|
||||
Режим <code>sac</code> — «Запросить обновление» ставит флаг в poll; агент self-update;
|
||||
через {{ agentUpdateForm.fallback_after_minutes }} мин без ответа — fallback SSH/WinRM с SAC.
|
||||
</p>
|
||||
<form class="settings-form" @submit.prevent="saveAgentUpdateSettingsForm">
|
||||
<label class="settings-field">
|
||||
Режим
|
||||
<select v-model="agentUpdateForm.mode">
|
||||
<option value="gpo">GPO / ручной</option>
|
||||
<option value="sac">SAC (poll + fallback)</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="settings-field settings-inline">
|
||||
<input v-model="agentUpdateForm.fallback_enabled" type="checkbox" />
|
||||
Авто-fallback SSH/WinRM после таймаута
|
||||
</label>
|
||||
<label class="settings-field">
|
||||
Fallback через (мин)
|
||||
<input v-model.number="agentUpdateForm.fallback_after_minutes" type="number" min="1" max="1440" />
|
||||
</label>
|
||||
<label class="settings-field">
|
||||
Recommended RDP-login-monitor
|
||||
<input v-model="agentUpdateForm.recommended_rdp_version" type="text" placeholder="2.1.0-SAC" />
|
||||
</label>
|
||||
<label class="settings-field">
|
||||
Recommended ssh-monitor
|
||||
<input v-model="agentUpdateForm.recommended_ssh_version" type="text" placeholder="2.1.0-SAC" />
|
||||
</label>
|
||||
<label class="settings-field">
|
||||
WinRM: путь к Deploy-LoginMonitor.ps1 (fallback)
|
||||
<input
|
||||
v-model="agentUpdateForm.win_agent_update_script"
|
||||
type="text"
|
||||
placeholder="C:\ProgramData\LoginMonitor\Deploy-LoginMonitor.ps1"
|
||||
/>
|
||||
</label>
|
||||
<p v-if="agentUpdateLoaded" class="settings-meta">
|
||||
Источник: <code>{{ agentUpdateLoaded.source }}</code>
|
||||
</p>
|
||||
<div class="settings-actions">
|
||||
<button type="submit" :disabled="savingAgentUpdate">
|
||||
{{ savingAgentUpdate ? "Сохранение…" : "Сохранить обновления агентов" }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card settings-card settings-policy">
|
||||
<h2>Правило оповещений</h2>
|
||||
<p class="settings-hint settings-hint-top">
|
||||
@@ -514,6 +564,8 @@ import {
|
||||
fetchUsers,
|
||||
fetchWinAdminSettings,
|
||||
fetchLinuxAdminSettings,
|
||||
fetchAgentUpdateSettings,
|
||||
saveAgentUpdateSettings,
|
||||
revokeMobileDevice,
|
||||
deleteMobileDevice,
|
||||
revokeMobileEnrollmentCode,
|
||||
@@ -524,6 +576,7 @@ import {
|
||||
type MobileDevice,
|
||||
type MobileEnrollmentCode,
|
||||
type MobileSettings,
|
||||
type AgentUpdateSettings,
|
||||
type UserSummary,
|
||||
} from "../api";
|
||||
|
||||
@@ -604,6 +657,7 @@ const savingPolicy = ref(false);
|
||||
const savingUi = ref(false);
|
||||
const savingWinAdmin = ref(false);
|
||||
const savingLinuxAdmin = ref(false);
|
||||
const savingAgentUpdate = ref(false);
|
||||
const savingTelegram = ref(false);
|
||||
const savingWebhook = ref(false);
|
||||
const savingEmail = ref(false);
|
||||
@@ -623,6 +677,7 @@ const policyLoaded = ref<NotificationPolicy | null>(null);
|
||||
const uiLoaded = ref<UiSettings | null>(null);
|
||||
const winAdminLoaded = ref<WinAdminSettings | null>(null);
|
||||
const linuxAdminLoaded = ref<LinuxAdminSettings | null>(null);
|
||||
const agentUpdateLoaded = ref<AgentUpdateSettings | null>(null);
|
||||
const severityRows = ref<SeverityOverrideRowUi[]>([]);
|
||||
const severityFilter = ref("");
|
||||
const mobileLoaded = ref<MobileSettings | null>(null);
|
||||
@@ -695,6 +750,15 @@ const linuxAdminForm = reactive({
|
||||
password: "",
|
||||
});
|
||||
|
||||
const agentUpdateForm = reactive({
|
||||
mode: "gpo" as "gpo" | "sac",
|
||||
fallback_enabled: true,
|
||||
fallback_after_minutes: 15,
|
||||
recommended_rdp_version: "",
|
||||
recommended_ssh_version: "",
|
||||
win_agent_update_script: "",
|
||||
});
|
||||
|
||||
const telegramForm = reactive({
|
||||
enabled: false,
|
||||
bot_token: "",
|
||||
@@ -909,6 +973,38 @@ async function saveLinuxAdminSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAgentUpdateSettings() {
|
||||
const data = await fetchAgentUpdateSettings();
|
||||
agentUpdateLoaded.value = data;
|
||||
agentUpdateForm.mode = data.mode;
|
||||
agentUpdateForm.fallback_enabled = data.fallback_enabled;
|
||||
agentUpdateForm.fallback_after_minutes = data.fallback_after_minutes;
|
||||
agentUpdateForm.recommended_rdp_version = data.recommended_rdp_version ?? "";
|
||||
agentUpdateForm.recommended_ssh_version = data.recommended_ssh_version ?? "";
|
||||
agentUpdateForm.win_agent_update_script = data.win_agent_update_script ?? "";
|
||||
}
|
||||
|
||||
async function saveAgentUpdateSettingsForm() {
|
||||
savingAgentUpdate.value = true;
|
||||
error.value = "";
|
||||
success.value = "";
|
||||
try {
|
||||
agentUpdateLoaded.value = await saveAgentUpdateSettings({
|
||||
mode: agentUpdateForm.mode,
|
||||
fallback_enabled: agentUpdateForm.fallback_enabled,
|
||||
fallback_after_minutes: agentUpdateForm.fallback_after_minutes,
|
||||
recommended_rdp_version: agentUpdateForm.recommended_rdp_version.trim() || null,
|
||||
recommended_ssh_version: agentUpdateForm.recommended_ssh_version.trim() || null,
|
||||
win_agent_update_script: agentUpdateForm.win_agent_update_script.trim() || null,
|
||||
});
|
||||
success.value = "Настройки обновления агентов сохранены.";
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : "Ошибка сохранения agent-updates";
|
||||
} finally {
|
||||
savingAgentUpdate.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMobileSection() {
|
||||
mobileLoaded.value = await fetchMobileSettings();
|
||||
mobileForm.devices_allowed = mobileLoaded.value.devices_allowed;
|
||||
@@ -1056,6 +1152,7 @@ async function load() {
|
||||
await loadUiSettings();
|
||||
await loadWinAdminSettings();
|
||||
await loadLinuxAdminSettings();
|
||||
await loadAgentUpdateSettings();
|
||||
await loadSeverityOverrides();
|
||||
await loadMobileSection();
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user