фича: бекап параметров и восстановление через вебку
This commit is contained in:
parent
57ba61da41
commit
996e711436
@ -72,3 +72,130 @@
|
||||
this.submitStatus.modemReboot = 30
|
||||
fetch('/api/reboot', { method: 'POST' }).then((r) => {})
|
||||
},
|
||||
async restoreAllSettings() {
|
||||
// Порядок применения настроек
|
||||
const settingsApplyOrder = ['qos', 'tcpaccel', 'rxtx', 'buclnb', 'network'];
|
||||
|
||||
// 1. Чтение JSON-файла, выбранного пользователем
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
|
||||
const filePromise = new Promise((resolve, reject) => {
|
||||
fileInput.onchange = e => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
reject(new Error('Файл не выбран'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
try {
|
||||
const jsonData = JSON.parse(event.target.result);
|
||||
resolve(jsonData);
|
||||
} catch (error) {
|
||||
reject(new Error('Ошибка парсинга JSON'));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error('Ошибка чтения файла'));
|
||||
reader.readAsText(file);
|
||||
};
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
try {
|
||||
const settingsToApply = await filePromise;
|
||||
const errors = [];
|
||||
|
||||
// 2. Перебор групп параметров в заданном порядке
|
||||
for (const groupName of settingsApplyOrder) {
|
||||
if (!settingsToApply.hasOwnProperty(groupName)) {
|
||||
continue; // Пропускаем группы, которых нет в файле
|
||||
}
|
||||
|
||||
const groupSettings = settingsToApply[groupName];
|
||||
if (typeof groupSettings !== 'object' || groupSettings === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2.1. POST-запрос для применения группы параметров
|
||||
const postResponse = await fetch(`/api/set/${groupName}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(groupSettings)
|
||||
});
|
||||
|
||||
if (!postResponse.ok) {
|
||||
throw new Error(`HTTP error ${postResponse.status}`);
|
||||
}
|
||||
|
||||
const postResult = await postResponse.json();
|
||||
if (postResult.status !== 'ok') {
|
||||
throw new Error(`API error: ${postResult.message || 'unknown error'}`);
|
||||
}
|
||||
|
||||
// 2.2. Проверка примененных параметров
|
||||
const getResponse = await fetch('/api/get/settings', { method: 'GET' });
|
||||
if (!getResponse.ok) {
|
||||
throw new Error(`HTTP error ${getResponse.status}`);
|
||||
}
|
||||
|
||||
const fetchSettingsResult = await getResponse.json();
|
||||
if (fetchSettingsResult.status !== 'ok') {
|
||||
throw new Error('Не удалось получить текущие настройки');
|
||||
}
|
||||
|
||||
// Проверка соответствия параметров
|
||||
const appliedGroup = fetchSettingsResult.settings[groupName] || {};
|
||||
const failedSettings = [];
|
||||
|
||||
for (const [key, value] of Object.entries(groupSettings)) {
|
||||
if (appliedGroup[key] !== value) {
|
||||
failedSettings.push(`${key} (ожидалось: ${value}, получено: ${appliedGroup[key]})`);
|
||||
}
|
||||
}
|
||||
|
||||
if (failedSettings.length > 0) {
|
||||
throw new Error(`Не совпадают параметры: ${failedSettings.join(', ')}`);
|
||||
}
|
||||
|
||||
} catch (groupError) {
|
||||
errors.push(`Группа ${groupName}: ${groupError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Показ ошибок, если они есть
|
||||
if (errors.length > 0) {
|
||||
const errorMessage = errors.join('\n\n') +
|
||||
'\n\nНекоторые настройки могли примениться некорректно.\nСтраница будет перезагружена.';
|
||||
alert(errorMessage);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
alert(`Ошибка при восстановлении настроек: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Перезагрузка страницы
|
||||
location.reload();
|
||||
},
|
||||
async dumpAllSettings() {
|
||||
function downloadAsFile(data, filename) {
|
||||
let a = document.createElement("a");
|
||||
let file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
const response = await fetch('/api/get/settings', { method: 'GET' })
|
||||
if (response.ok) {
|
||||
const jres = await response.json()
|
||||
if (jres["status"] === "ok") {
|
||||
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -21,6 +21,8 @@
|
||||
<div>
|
||||
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
|
||||
</div>
|
||||
<button class="action-button" @click="dumpAllSettings()">Сохранить бекап конфигурации</button>
|
||||
<button class="dangerous-button" @click="restoreAllSettings()">Восстановить бекап конфигурации</button>
|
||||
</div>
|
||||
|
||||
<h2>Обновление ПО</h2>
|
||||
|
@ -533,6 +533,8 @@
|
||||
<div>
|
||||
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
|
||||
</div>
|
||||
<button class="action-button" @click="dumpAllSettings()">Сохранить бекап конфигурации</button>
|
||||
<button class="dangerous-button" @click="restoreAllSettings()">Восстановить бекап конфигурации</button>
|
||||
</div>
|
||||
|
||||
<h2>Обновление ПО</h2>
|
||||
@ -1363,6 +1365,133 @@
|
||||
}
|
||||
this.submitStatus.modemReboot = 30
|
||||
fetch('/api/reboot', { method: 'POST' }).then((r) => {})
|
||||
},
|
||||
async restoreAllSettings() {
|
||||
// Порядок применения настроек
|
||||
const settingsApplyOrder = ['qos', 'tcpaccel', 'rxtx', 'buclnb', 'network'];
|
||||
|
||||
// 1. Чтение JSON-файла, выбранного пользователем
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
|
||||
const filePromise = new Promise((resolve, reject) => {
|
||||
fileInput.onchange = e => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
reject(new Error('Файл не выбран'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
try {
|
||||
const jsonData = JSON.parse(event.target.result);
|
||||
resolve(jsonData);
|
||||
} catch (error) {
|
||||
reject(new Error('Ошибка парсинга JSON'));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error('Ошибка чтения файла'));
|
||||
reader.readAsText(file);
|
||||
};
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
try {
|
||||
const settingsToApply = await filePromise;
|
||||
const errors = [];
|
||||
|
||||
// 2. Перебор групп параметров в заданном порядке
|
||||
for (const groupName of settingsApplyOrder) {
|
||||
if (!settingsToApply.hasOwnProperty(groupName)) {
|
||||
continue; // Пропускаем группы, которых нет в файле
|
||||
}
|
||||
|
||||
const groupSettings = settingsToApply[groupName];
|
||||
if (typeof groupSettings !== 'object' || groupSettings === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2.1. POST-запрос для применения группы параметров
|
||||
const postResponse = await fetch(`/api/set/${groupName}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(groupSettings)
|
||||
});
|
||||
|
||||
if (!postResponse.ok) {
|
||||
throw new Error(`HTTP error ${postResponse.status}`);
|
||||
}
|
||||
|
||||
const postResult = await postResponse.json();
|
||||
if (postResult.status !== 'ok') {
|
||||
throw new Error(`API error: ${postResult.message || 'unknown error'}`);
|
||||
}
|
||||
|
||||
// 2.2. Проверка примененных параметров
|
||||
const getResponse = await fetch('/api/get/settings', { method: 'GET' });
|
||||
if (!getResponse.ok) {
|
||||
throw new Error(`HTTP error ${getResponse.status}`);
|
||||
}
|
||||
|
||||
const fetchSettingsResult = await getResponse.json();
|
||||
if (fetchSettingsResult.status !== 'ok') {
|
||||
throw new Error('Не удалось получить текущие настройки');
|
||||
}
|
||||
|
||||
// Проверка соответствия параметров
|
||||
const appliedGroup = fetchSettingsResult.settings[groupName] || {};
|
||||
const failedSettings = [];
|
||||
|
||||
for (const [key, value] of Object.entries(groupSettings)) {
|
||||
if (appliedGroup[key] !== value) {
|
||||
failedSettings.push(`${key} (ожидалось: ${value}, получено: ${appliedGroup[key]})`);
|
||||
}
|
||||
}
|
||||
|
||||
if (failedSettings.length > 0) {
|
||||
throw new Error(`Не совпадают параметры: ${failedSettings.join(', ')}`);
|
||||
}
|
||||
|
||||
} catch (groupError) {
|
||||
errors.push(`Группа ${groupName}: ${groupError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Показ ошибок, если они есть
|
||||
if (errors.length > 0) {
|
||||
const errorMessage = errors.join('\n\n') +
|
||||
'\n\nНекоторые настройки могли примениться некорректно.\nСтраница будет перезагружена.';
|
||||
alert(errorMessage);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
alert(`Ошибка при восстановлении настроек: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Перезагрузка страницы
|
||||
location.reload();
|
||||
},
|
||||
async dumpAllSettings() {
|
||||
function downloadAsFile(data, filename) {
|
||||
let a = document.createElement("a");
|
||||
let file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
const response = await fetch('/api/get/settings', { method: 'GET' })
|
||||
if (response.ok) {
|
||||
const jres = await response.json()
|
||||
if (jres["status"] === "ok") {
|
||||
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
|
||||
}
|
||||
}
|
||||
}, // ========== include end from 'common/admin-methods.js.j2'
|
||||
|
||||
|
||||
|
@ -285,6 +285,8 @@
|
||||
<div>
|
||||
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
|
||||
</div>
|
||||
<button class="action-button" @click="dumpAllSettings()">Сохранить бекап конфигурации</button>
|
||||
<button class="dangerous-button" @click="restoreAllSettings()">Восстановить бекап конфигурации</button>
|
||||
</div>
|
||||
|
||||
<h2>Обновление ПО</h2>
|
||||
@ -756,6 +758,133 @@
|
||||
}
|
||||
this.submitStatus.modemReboot = 30
|
||||
fetch('/api/reboot', { method: 'POST' }).then((r) => {})
|
||||
},
|
||||
async restoreAllSettings() {
|
||||
// Порядок применения настроек
|
||||
const settingsApplyOrder = ['qos', 'tcpaccel', 'rxtx', 'buclnb', 'network'];
|
||||
|
||||
// 1. Чтение JSON-файла, выбранного пользователем
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
|
||||
const filePromise = new Promise((resolve, reject) => {
|
||||
fileInput.onchange = e => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
reject(new Error('Файл не выбран'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
try {
|
||||
const jsonData = JSON.parse(event.target.result);
|
||||
resolve(jsonData);
|
||||
} catch (error) {
|
||||
reject(new Error('Ошибка парсинга JSON'));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error('Ошибка чтения файла'));
|
||||
reader.readAsText(file);
|
||||
};
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
try {
|
||||
const settingsToApply = await filePromise;
|
||||
const errors = [];
|
||||
|
||||
// 2. Перебор групп параметров в заданном порядке
|
||||
for (const groupName of settingsApplyOrder) {
|
||||
if (!settingsToApply.hasOwnProperty(groupName)) {
|
||||
continue; // Пропускаем группы, которых нет в файле
|
||||
}
|
||||
|
||||
const groupSettings = settingsToApply[groupName];
|
||||
if (typeof groupSettings !== 'object' || groupSettings === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2.1. POST-запрос для применения группы параметров
|
||||
const postResponse = await fetch(`/api/set/${groupName}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(groupSettings)
|
||||
});
|
||||
|
||||
if (!postResponse.ok) {
|
||||
throw new Error(`HTTP error ${postResponse.status}`);
|
||||
}
|
||||
|
||||
const postResult = await postResponse.json();
|
||||
if (postResult.status !== 'ok') {
|
||||
throw new Error(`API error: ${postResult.message || 'unknown error'}`);
|
||||
}
|
||||
|
||||
// 2.2. Проверка примененных параметров
|
||||
const getResponse = await fetch('/api/get/settings', { method: 'GET' });
|
||||
if (!getResponse.ok) {
|
||||
throw new Error(`HTTP error ${getResponse.status}`);
|
||||
}
|
||||
|
||||
const fetchSettingsResult = await getResponse.json();
|
||||
if (fetchSettingsResult.status !== 'ok') {
|
||||
throw new Error('Не удалось получить текущие настройки');
|
||||
}
|
||||
|
||||
// Проверка соответствия параметров
|
||||
const appliedGroup = fetchSettingsResult.settings[groupName] || {};
|
||||
const failedSettings = [];
|
||||
|
||||
for (const [key, value] of Object.entries(groupSettings)) {
|
||||
if (appliedGroup[key] !== value) {
|
||||
failedSettings.push(`${key} (ожидалось: ${value}, получено: ${appliedGroup[key]})`);
|
||||
}
|
||||
}
|
||||
|
||||
if (failedSettings.length > 0) {
|
||||
throw new Error(`Не совпадают параметры: ${failedSettings.join(', ')}`);
|
||||
}
|
||||
|
||||
} catch (groupError) {
|
||||
errors.push(`Группа ${groupName}: ${groupError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Показ ошибок, если они есть
|
||||
if (errors.length > 0) {
|
||||
const errorMessage = errors.join('\n\n') +
|
||||
'\n\nНекоторые настройки могли примениться некорректно.\nСтраница будет перезагружена.';
|
||||
alert(errorMessage);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
alert(`Ошибка при восстановлении настроек: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Перезагрузка страницы
|
||||
location.reload();
|
||||
},
|
||||
async dumpAllSettings() {
|
||||
function downloadAsFile(data, filename) {
|
||||
let a = document.createElement("a");
|
||||
let file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
const response = await fetch('/api/get/settings', { method: 'GET' })
|
||||
if (response.ok) {
|
||||
const jres = await response.json()
|
||||
if (jres["status"] === "ok") {
|
||||
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
|
||||
}
|
||||
}
|
||||
}, // ========== include end from 'common/admin-methods.js.j2'
|
||||
|
||||
|
||||
|
@ -266,6 +266,8 @@
|
||||
<div>
|
||||
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
|
||||
</div>
|
||||
<button class="action-button" @click="dumpAllSettings()">Сохранить бекап конфигурации</button>
|
||||
<button class="dangerous-button" @click="restoreAllSettings()">Восстановить бекап конфигурации</button>
|
||||
</div>
|
||||
|
||||
<h2>Обновление ПО</h2>
|
||||
@ -752,6 +754,133 @@
|
||||
}
|
||||
this.submitStatus.modemReboot = 30
|
||||
fetch('/api/reboot', { method: 'POST' }).then((r) => {})
|
||||
},
|
||||
async restoreAllSettings() {
|
||||
// Порядок применения настроек
|
||||
const settingsApplyOrder = ['qos', 'tcpaccel', 'rxtx', 'buclnb', 'network'];
|
||||
|
||||
// 1. Чтение JSON-файла, выбранного пользователем
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
|
||||
const filePromise = new Promise((resolve, reject) => {
|
||||
fileInput.onchange = e => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
reject(new Error('Файл не выбран'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
try {
|
||||
const jsonData = JSON.parse(event.target.result);
|
||||
resolve(jsonData);
|
||||
} catch (error) {
|
||||
reject(new Error('Ошибка парсинга JSON'));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error('Ошибка чтения файла'));
|
||||
reader.readAsText(file);
|
||||
};
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
try {
|
||||
const settingsToApply = await filePromise;
|
||||
const errors = [];
|
||||
|
||||
// 2. Перебор групп параметров в заданном порядке
|
||||
for (const groupName of settingsApplyOrder) {
|
||||
if (!settingsToApply.hasOwnProperty(groupName)) {
|
||||
continue; // Пропускаем группы, которых нет в файле
|
||||
}
|
||||
|
||||
const groupSettings = settingsToApply[groupName];
|
||||
if (typeof groupSettings !== 'object' || groupSettings === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2.1. POST-запрос для применения группы параметров
|
||||
const postResponse = await fetch(`/api/set/${groupName}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(groupSettings)
|
||||
});
|
||||
|
||||
if (!postResponse.ok) {
|
||||
throw new Error(`HTTP error ${postResponse.status}`);
|
||||
}
|
||||
|
||||
const postResult = await postResponse.json();
|
||||
if (postResult.status !== 'ok') {
|
||||
throw new Error(`API error: ${postResult.message || 'unknown error'}`);
|
||||
}
|
||||
|
||||
// 2.2. Проверка примененных параметров
|
||||
const getResponse = await fetch('/api/get/settings', { method: 'GET' });
|
||||
if (!getResponse.ok) {
|
||||
throw new Error(`HTTP error ${getResponse.status}`);
|
||||
}
|
||||
|
||||
const fetchSettingsResult = await getResponse.json();
|
||||
if (fetchSettingsResult.status !== 'ok') {
|
||||
throw new Error('Не удалось получить текущие настройки');
|
||||
}
|
||||
|
||||
// Проверка соответствия параметров
|
||||
const appliedGroup = fetchSettingsResult.settings[groupName] || {};
|
||||
const failedSettings = [];
|
||||
|
||||
for (const [key, value] of Object.entries(groupSettings)) {
|
||||
if (appliedGroup[key] !== value) {
|
||||
failedSettings.push(`${key} (ожидалось: ${value}, получено: ${appliedGroup[key]})`);
|
||||
}
|
||||
}
|
||||
|
||||
if (failedSettings.length > 0) {
|
||||
throw new Error(`Не совпадают параметры: ${failedSettings.join(', ')}`);
|
||||
}
|
||||
|
||||
} catch (groupError) {
|
||||
errors.push(`Группа ${groupName}: ${groupError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Показ ошибок, если они есть
|
||||
if (errors.length > 0) {
|
||||
const errorMessage = errors.join('\n\n') +
|
||||
'\n\nНекоторые настройки могли примениться некорректно.\nСтраница будет перезагружена.';
|
||||
alert(errorMessage);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
alert(`Ошибка при восстановлении настроек: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Перезагрузка страницы
|
||||
location.reload();
|
||||
},
|
||||
async dumpAllSettings() {
|
||||
function downloadAsFile(data, filename) {
|
||||
let a = document.createElement("a");
|
||||
let file = new Blob([data], {type: 'application/json'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
const response = await fetch('/api/get/settings', { method: 'GET' })
|
||||
if (response.ok) {
|
||||
const jres = await response.json()
|
||||
if (jres["status"] === "ok") {
|
||||
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
|
||||
}
|
||||
}
|
||||
}, // ========== include end from 'common/admin-methods.js.j2'
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user