diff --git a/front-generator/template/common/admin-methods.js.j2 b/front-generator/template/common/admin-methods.js.j2
index b7863f7..edc863c 100644
--- a/front-generator/template/common/admin-methods.js.j2
+++ b/front-generator/template/common/admin-methods.js.j2
@@ -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")
+ }
+ }
+ },
diff --git a/front-generator/template/common/admin.html.j2 b/front-generator/template/common/admin.html.j2
index 3eedc7c..f9ba9b7 100644
--- a/front-generator/template/common/admin.html.j2
+++ b/front-generator/template/common/admin.html.j2
@@ -21,6 +21,8 @@
+
+
Обновление ПО
diff --git a/static/main-scpc.html b/static/main-scpc.html
index 54a1b2e..c6c3389 100644
--- a/static/main-scpc.html
+++ b/static/main-scpc.html
@@ -533,6 +533,8 @@
+
+
Обновление ПО
@@ -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'
diff --git a/static/main-shps.html b/static/main-shps.html
index 13a1c82..fe726c6 100644
--- a/static/main-shps.html
+++ b/static/main-shps.html
@@ -285,6 +285,8 @@
+
+
Обновление ПО
@@ -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'
diff --git a/static/main-tdma.html b/static/main-tdma.html
index 2d4a300..f2c3cd2 100644
--- a/static/main-tdma.html
+++ b/static/main-tdma.html
@@ -266,6 +266,8 @@
+
+
Обновление ПО
@@ -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'