реализовал вэбку обновления ПО
This commit is contained in:
parent
95d09e3c79
commit
adbd4ea6d4
|
|
@ -33,7 +33,6 @@ public class App implements Context, Closeable {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SystemLogger.initializeLogging(Level.INFO, List.of(InMemoryLogHandler.class));
|
SystemLogger.initializeLogging(Level.INFO, List.of(InMemoryLogHandler.class));
|
||||||
SystemLogger.setExceptionDumping(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AtomicBoolean shouldRestart = new AtomicBoolean(false);
|
private final AtomicBoolean shouldRestart = new AtomicBoolean(false);
|
||||||
|
|
@ -71,8 +70,6 @@ public class App implements Context, Closeable {
|
||||||
private ServiceManager loadServiceManager() {
|
private ServiceManager loadServiceManager() {
|
||||||
var manager = new ServiceManager(this, List.of(AuthManager.class, ComponentHandlerService.class, TokenService.class, AppUpdateService.class, BGPInfoService.class, NetworkingService.class, SubscriptionService.class, ResourceUpdateService.class, WebService.class));
|
var manager = new ServiceManager(this, List.of(AuthManager.class, ComponentHandlerService.class, TokenService.class, AppUpdateService.class, BGPInfoService.class, NetworkingService.class, SubscriptionService.class, ResourceUpdateService.class, WebService.class));
|
||||||
manager.getService(BGPInfoService.class).setProvider(new HEInfoProvider());
|
manager.getService(BGPInfoService.class).setProvider(new HEInfoProvider());
|
||||||
manager.getService(ResourceUpdateService.class).start();
|
|
||||||
manager.getService(ComponentHandlerService.class).syncComponentsWithConfig();
|
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,9 +90,16 @@ public class App implements Context, Closeable {
|
||||||
public App(LauncherConfig launcherConfig) {
|
public App(LauncherConfig launcherConfig) {
|
||||||
this.launcherConfig = launcherConfig;
|
this.launcherConfig = launcherConfig;
|
||||||
config = loadConfig();
|
config = loadConfig();
|
||||||
|
if (config.isDisplayDebuggingInfo()) {
|
||||||
|
SystemLogger.setExceptionDumping(true);
|
||||||
|
}
|
||||||
|
|
||||||
serviceManager = loadServiceManager();
|
serviceManager = loadServiceManager();
|
||||||
serviceManager.getService(SubscriptionService.class).triggerUpdate();
|
serviceManager.getService(SubscriptionService.class).triggerUpdate();
|
||||||
checkDefaultPassword();
|
checkDefaultPassword();
|
||||||
|
|
||||||
|
serviceManager.getService(ComponentHandlerService.class).syncComponentsWithConfig();
|
||||||
|
serviceManager.getService(ResourceUpdateService.class).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import ru.kirillius.json.rpc.Servlet.JSONRPCServlet;
|
||||||
import ru.kirillius.pf.sdn.External.API.ShellExecutor;
|
import ru.kirillius.pf.sdn.External.API.ShellExecutor;
|
||||||
import ru.kirillius.pf.sdn.core.AbstractComponent;
|
import ru.kirillius.pf.sdn.core.AbstractComponent;
|
||||||
import ru.kirillius.pf.sdn.core.Context;
|
import ru.kirillius.pf.sdn.core.Context;
|
||||||
|
import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
|
||||||
import ru.kirillius.pf.sdn.core.Networking.NetworkingService;
|
import ru.kirillius.pf.sdn.core.Networking.NetworkingService;
|
||||||
import ru.kirillius.pf.sdn.core.Util.IPv4Util;
|
import ru.kirillius.pf.sdn.core.Util.IPv4Util;
|
||||||
import ru.kirillius.pf.sdn.web.ProtectedMethod;
|
import ru.kirillius.pf.sdn.web.ProtectedMethod;
|
||||||
|
|
@ -25,21 +26,30 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public final class OVPN extends AbstractComponent<OVPN.OVPNConfig> {
|
public final class OVPN extends AbstractComponent<OVPN.OVPNConfig> {
|
||||||
private final static String CTX = OVPN.class.getSimpleName();
|
private final static String CTX = OVPN.class.getSimpleName();
|
||||||
private final EventListener<JSONRPCServlet> subscription;
|
private final EventListener<JSONRPCServlet> rpcEvent;
|
||||||
|
private final EventListener<NetworkResourceBundle> updateEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the component with the JSON-RPC servlet or defers until it becomes available.
|
* Registers the component with the JSON-RPC servlet or defers until it becomes available.
|
||||||
*/
|
*/
|
||||||
public OVPN(Context context) {
|
public OVPN(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
var eventsHandler = context.getEventsHandler();
|
||||||
|
if (config.restartOnUpdate) {
|
||||||
|
updateEvent = eventsHandler.getNetworkManagerUpdateEvent().add(bundle -> restartSystemService());
|
||||||
|
} else {
|
||||||
|
updateEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var RPC = context.getServiceManager().getService(WebService.class).getJSONRPC();
|
var RPC = context.getServiceManager().getService(WebService.class).getJSONRPC();
|
||||||
if (RPC != null) {
|
if (RPC != null) {
|
||||||
RPC.addTargetInstance(OVPN.class, this);
|
RPC.addTargetInstance(OVPN.class, this);
|
||||||
subscription = null;
|
rpcEvent = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
subscription = context.getEventsHandler().getRPCInitEvent()
|
rpcEvent = eventsHandler.getRPCInitEvent()
|
||||||
.add(servlet -> servlet.addTargetInstance(OVPN.class, OVPN.this));
|
.add(servlet -> servlet.addTargetInstance(OVPN.class, OVPN.this)); //TODO поисследовать. Возможно событие уже не нужно
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,8 +88,12 @@ public final class OVPN extends AbstractComponent<OVPN.OVPNConfig> {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (subscription != null) {
|
var eventsHandler = context.getEventsHandler();
|
||||||
context.getEventsHandler().getRPCInitEvent().remove(subscription);
|
if (rpcEvent != null) {
|
||||||
|
eventsHandler.getRPCInitEvent().remove(rpcEvent);
|
||||||
|
}
|
||||||
|
if (updateEvent != null) {
|
||||||
|
eventsHandler.getNetworkManagerUpdateEvent().remove(updateEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,5 +111,10 @@ public final class OVPN extends AbstractComponent<OVPN.OVPNConfig> {
|
||||||
@Setter
|
@Setter
|
||||||
@JSONProperty
|
@JSONProperty
|
||||||
private volatile String restartCommand = "rc-service openvpn restart";
|
private volatile String restartCommand = "rc-service openvpn restart";
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@JSONProperty
|
||||||
|
private volatile boolean restartOnUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,10 @@ import org.json.JSONObject;
|
||||||
import ru.kirillius.json.JSONUtility;
|
import ru.kirillius.json.JSONUtility;
|
||||||
import ru.kirillius.json.rpc.Annotations.JRPCArgument;
|
import ru.kirillius.json.rpc.Annotations.JRPCArgument;
|
||||||
import ru.kirillius.json.rpc.Annotations.JRPCMethod;
|
import ru.kirillius.json.rpc.Annotations.JRPCMethod;
|
||||||
import ru.kirillius.pf.sdn.core.AppUpdateService;
|
import ru.kirillius.pf.sdn.core.*;
|
||||||
import ru.kirillius.pf.sdn.core.Component;
|
|
||||||
import ru.kirillius.pf.sdn.core.ComponentHandlerService;
|
|
||||||
import ru.kirillius.pf.sdn.core.Context;
|
|
||||||
import ru.kirillius.pf.sdn.web.ProtectedMethod;
|
import ru.kirillius.pf.sdn.web.ProtectedMethod;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,6 +23,7 @@ public class System implements RPC {
|
||||||
public System(Context context) {
|
public System(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests an application restart.
|
* Requests an application restart.
|
||||||
*/
|
*/
|
||||||
|
|
@ -61,6 +60,16 @@ public class System implements RPC {
|
||||||
return context.getConfig().isModified();
|
return context.getConfig().isModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ProtectedMethod
|
||||||
|
@JRPCMethod
|
||||||
|
public void saveConfig() {
|
||||||
|
try {
|
||||||
|
Config.store(context.getConfig(), context.getLauncherConfig().getConfigFile());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the latest version available for update.
|
* Retrieves the latest version available for update.
|
||||||
*/
|
*/
|
||||||
|
|
@ -85,11 +94,20 @@ public class System implements RPC {
|
||||||
@ProtectedMethod
|
@ProtectedMethod
|
||||||
@JRPCMethod
|
@JRPCMethod
|
||||||
public JSONObject getVersionInfo() {
|
public JSONObject getVersionInfo() {
|
||||||
var available = context.getServiceManager().getService(AppUpdateService.class).checkVersionForUpdate();
|
var updateService = context.getServiceManager().getService(AppUpdateService.class);
|
||||||
|
var json = new JSONObject();
|
||||||
return null;
|
json.put("available", updateService.getLatestVersion());
|
||||||
|
json.put("current", updateService.getAppVersion());
|
||||||
|
json.put("downloaded", updateService.getDownloadedVersion());
|
||||||
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ProtectedMethod
|
||||||
|
@JRPCMethod
|
||||||
|
public void doUpdate() {
|
||||||
|
var updateService = context.getServiceManager().getService(AppUpdateService.class);
|
||||||
|
updateService.updateApp();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of enabled component class names.
|
* Returns the list of enabled component class names.
|
||||||
|
|
@ -141,7 +159,7 @@ public class System implements RPC {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Updates the configuration of the specified component and reloads it.
|
* Updates the configuration of the specified component and reloads it.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
@ProtectedMethod
|
@ProtectedMethod
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package ru.kirillius.pf.sdn.core;
|
package ru.kirillius.pf.sdn.core;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import ru.kirillius.utils.logging.SystemLogger;
|
import ru.kirillius.utils.logging.SystemLogger;
|
||||||
|
|
||||||
|
|
@ -29,7 +30,11 @@ public class AppUpdateService extends AppService {
|
||||||
private final Path appLibraryPath;
|
private final Path appLibraryPath;
|
||||||
private final Class<?> anchorClass;
|
private final Class<?> anchorClass;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
private volatile String cachedLatestVersion;
|
@Getter
|
||||||
|
private volatile String latestVersion;
|
||||||
|
@Getter
|
||||||
|
private volatile String downloadedVersion;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the service bound to the provided context and initialises HTTP access helpers.
|
* Creates the service bound to the provided context and initialises HTTP access helpers.
|
||||||
|
|
@ -94,19 +99,20 @@ public class AppUpdateService extends AppService {
|
||||||
* @return newest version string or the previously cached value when fetch fails.
|
* @return newest version string or the previously cached value when fetch fails.
|
||||||
*/
|
*/
|
||||||
public synchronized String checkVersionForUpdate() {
|
public synchronized String checkVersionForUpdate() {
|
||||||
|
SystemLogger.message("Checking application version", CTX);
|
||||||
var latest = fetchLatestVersion();
|
var latest = fetchLatestVersion();
|
||||||
if (latest != null) {
|
if (latest != null) {
|
||||||
cachedLatestVersion = latest;
|
latestVersion = latest;
|
||||||
return latest;
|
return latest;
|
||||||
}
|
}
|
||||||
return cachedLatestVersion;
|
return latestVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads the application package corresponding to the latest known version.
|
* Downloads the application package corresponding to the latest known version.
|
||||||
*/
|
*/
|
||||||
public void updateApp() {
|
public void updateApp() {
|
||||||
var version = cachedLatestVersion;
|
var version = latestVersion;
|
||||||
if (version == null || version.isBlank()) {
|
if (version == null || version.isBlank()) {
|
||||||
version = checkVersionForUpdate();
|
version = checkVersionForUpdate();
|
||||||
}
|
}
|
||||||
|
|
@ -128,6 +134,11 @@ public class AppUpdateService extends AppService {
|
||||||
var tempFile = targetDirectory.resolve(fileName + ".download");
|
var tempFile = targetDirectory.resolve(fileName + ".download");
|
||||||
var targetFile = targetDirectory.resolve(fileName);
|
var targetFile = targetDirectory.resolve(fileName);
|
||||||
|
|
||||||
|
if (targetFile.toFile().exists()) {
|
||||||
|
SystemLogger.error("Latest version is downloaded already", CTX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(targetDirectory);
|
Files.createDirectories(targetDirectory);
|
||||||
Files.deleteIfExists(tempFile);
|
Files.deleteIfExists(tempFile);
|
||||||
|
|
@ -142,7 +153,8 @@ public class AppUpdateService extends AppService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Files.move(tempFile, targetFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.move(tempFile, targetFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
cachedLatestVersion = version;
|
latestVersion = version;
|
||||||
|
downloadedVersion = version;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
SystemLogger.error("Failed to download update", CTX, e);
|
SystemLogger.error("Failed to download update", CTX, e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,11 @@ public class Config {
|
||||||
@JSONProperty
|
@JSONProperty
|
||||||
private volatile boolean mergeSubnets = true;
|
private volatile boolean mergeSubnets = true;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@JSONProperty
|
||||||
|
private volatile boolean displayDebuggingInfo = true;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
@JSONProperty
|
@JSONProperty
|
||||||
|
|
@ -113,8 +118,10 @@ public class Config {
|
||||||
public static void store(Config config, File file) throws IOException {
|
public static void store(Config config, File file) throws IOException {
|
||||||
try (var fileInputStream = new FileOutputStream(file)) {
|
try (var fileInputStream = new FileOutputStream(file)) {
|
||||||
try (var writer = new BufferedWriter(new OutputStreamWriter(fileInputStream))) {
|
try (var writer = new BufferedWriter(new OutputStreamWriter(fileInputStream))) {
|
||||||
writer.write(serialize(config).toString());
|
var json = serialize(config);
|
||||||
|
writer.write(json.toString());
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
config.initialJSON = json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +159,6 @@ public class Config {
|
||||||
* Indicates whether the in-memory configuration diverges from the initially loaded snapshot.
|
* Indicates whether the in-memory configuration diverges from the initially loaded snapshot.
|
||||||
*/
|
*/
|
||||||
public boolean isModified() {
|
public boolean isModified() {
|
||||||
return !initialJSON.equals(serialize(this));
|
return !initialJSON.toString().equals(serialize(this).toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,17 @@ public class ResourceUpdateService extends AppService {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
var updateService = context.getServiceManager().getService(AppUpdateService.class);
|
||||||
var uptime = 0L;
|
var uptime = 0L;
|
||||||
var config = context.getConfig();
|
var config = context.getConfig();
|
||||||
|
updateService.checkVersionForUpdate();
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
Thread.sleep(Duration.ofMinutes(1));
|
Thread.sleep(Duration.ofMinutes(1));
|
||||||
uptime++;
|
uptime++;
|
||||||
|
|
||||||
|
if (uptime % 15 == 0) {
|
||||||
|
updateService.checkVersionForUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
if (config.getUpdateSubscriptionsInterval() > 0 && uptime % (config.getUpdateSubscriptionsInterval() * 60L) == 0) {
|
if (config.getUpdateSubscriptionsInterval() > 0 && uptime % (config.getUpdateSubscriptionsInterval() * 60L) == 0) {
|
||||||
SystemLogger.message("Updating subscriptions", CTX);
|
SystemLogger.message("Updating subscriptions", CTX);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,6 @@ public final class CommandLineUtils {
|
||||||
if (first.isEmpty()) {
|
if (first.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Missing required argument: -" + argname);
|
throw new IllegalArgumentException("Missing required argument: -" + argname);
|
||||||
}
|
}
|
||||||
return first.get();
|
return first.get().substring(argname.length() + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,27 @@ let updateInterval = null;
|
||||||
|
|
||||||
const $content = () => $('#statistics-content');
|
const $content = () => $('#statistics-content');
|
||||||
|
|
||||||
const createStatusCard = (title, status, buttonId, buttonLabel, isUpdating, resourceStatsHtml = '') => {
|
const createStatusCard = (title, status, buttons = [], resourceStatsHtml = '', customStatusContent = null) => {
|
||||||
const statusClass = status === 'Обновляется' || status === 'Обнаружено' ? 'text-yellow-500' : 'text-green-500';
|
const warningStatuses = ['Обновляется', 'Обнаружено', 'Готово к установке'];
|
||||||
|
const statusClass = warningStatuses.includes(status) ? 'text-yellow-500' : 'text-green-500';
|
||||||
|
const statusSection = customStatusContent || `
|
||||||
|
<p class="status-line">
|
||||||
|
Статус: <span class="${statusClass}">${status}</span>
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
const buttonsHtml = buttons.length
|
||||||
|
? `<div class="button-group">${buttons.map(({ id, label, disabled, isLoading = false, loadingLabel = 'Обновление...' }) => `
|
||||||
|
<button id="${id}" class="btn-secondary" ${disabled || isLoading ? 'disabled' : ''}>
|
||||||
|
${isLoading ? loadingLabel : label}
|
||||||
|
</button>
|
||||||
|
`).join('')}</div>`
|
||||||
|
: '';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<h3>${title}</h3>
|
<h3>${title}</h3>
|
||||||
<p class="status-line">
|
${statusSection}
|
||||||
Статус: <span class="${statusClass}">${status}</span>
|
${buttonsHtml}
|
||||||
</p>
|
|
||||||
<button id="${buttonId}" class="btn-secondary" ${isUpdating ? 'disabled' : ''}>
|
|
||||||
${isUpdating ? 'Обновление...' : buttonLabel}
|
|
||||||
</button>
|
|
||||||
${resourceStatsHtml}
|
${resourceStatsHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -72,7 +81,32 @@ async function getNetworkResources() {
|
||||||
async function renderSystemAndManagerStatus() {
|
async function renderSystemAndManagerStatus() {
|
||||||
const dataHtml = [];
|
const dataHtml = [];
|
||||||
|
|
||||||
const hasUpdates = await JSONRPC.System.hasUpdates();
|
const versionInfo = await JSONRPC.System.getVersionInfo();
|
||||||
|
const availableVersion = (versionInfo && versionInfo.available) || '';
|
||||||
|
const currentVersion = (versionInfo && versionInfo.current) || '';
|
||||||
|
const downloadedVersion = (versionInfo && versionInfo.downloaded) || '';
|
||||||
|
const isConfigChanged = await JSONRPC.System.isConfigChanged();
|
||||||
|
const hasAvailableUpdate = availableVersion && availableVersion !== currentVersion;
|
||||||
|
const isDownloaded = hasAvailableUpdate && downloadedVersion === availableVersion;
|
||||||
|
const updateButtonDisabled = !hasAvailableUpdate || isDownloaded;
|
||||||
|
const systemStatus = (() => {
|
||||||
|
if (!hasAvailableUpdate) {
|
||||||
|
return 'Нет обновлений';
|
||||||
|
}
|
||||||
|
if (isDownloaded) {
|
||||||
|
return 'Готово к установке';
|
||||||
|
}
|
||||||
|
return 'Обнаружено';
|
||||||
|
})();
|
||||||
|
const versionStatusHtml = `
|
||||||
|
<div class="status-line version-line">
|
||||||
|
<span>Текущая версия: <span class="${hasAvailableUpdate ? 'text-yellow-500' : 'text-green-500'}">${currentVersion || '-'}</span></span><br />
|
||||||
|
<span class="last-version">Последняя версия: <span class="${hasAvailableUpdate ? 'text-yellow-500' : 'text-green-500'}">${availableVersion || '-'}</span></span>
|
||||||
|
</div>
|
||||||
|
<p class="status-line ${isConfigChanged ? 'text-yellow-500' : 'text-green-500'}">
|
||||||
|
Конфигурация: ${isConfigChanged ? 'изменена' : 'актуальна'}
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
const isSubUpdating = await JSONRPC.SubscriptionManager.isUpdating();
|
const isSubUpdating = await JSONRPC.SubscriptionManager.isUpdating();
|
||||||
const isNetUpdating = await JSONRPC.NetworkManager.isUpdating();
|
const isNetUpdating = await JSONRPC.NetworkManager.isUpdating();
|
||||||
const subCount = await getSubscriptionCount();
|
const subCount = await getSubscriptionCount();
|
||||||
|
|
@ -80,10 +114,27 @@ async function renderSystemAndManagerStatus() {
|
||||||
|
|
||||||
dataHtml.push(createStatusCard(
|
dataHtml.push(createStatusCard(
|
||||||
'Обновление ПО',
|
'Обновление ПО',
|
||||||
hasUpdates ? 'Обнаружено' : 'Нет обновлений',
|
systemStatus,
|
||||||
'update-software-btn',
|
[
|
||||||
'Проверить',
|
{
|
||||||
false
|
id: 'update-software-btn',
|
||||||
|
label: 'Обновить',
|
||||||
|
disabled: updateButtonDisabled
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'restart-system-btn',
|
||||||
|
label: 'Перезагрузить',
|
||||||
|
loadingLabel: 'Перезагрузка...'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'save-config-btn',
|
||||||
|
label: 'Сохранить',
|
||||||
|
disabled: !isConfigChanged,
|
||||||
|
loadingLabel: 'Сохранение...'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
versionStatusHtml
|
||||||
));
|
));
|
||||||
|
|
||||||
const subStatsHtml = `<div class="resource-group" style="margin-top: 20px;">
|
const subStatsHtml = `<div class="resource-group" style="margin-top: 20px;">
|
||||||
|
|
@ -95,9 +146,14 @@ async function renderSystemAndManagerStatus() {
|
||||||
dataHtml.push(createStatusCard(
|
dataHtml.push(createStatusCard(
|
||||||
'Менеджер Подписок',
|
'Менеджер Подписок',
|
||||||
isSubUpdating ? 'Обновляется' : 'Активен',
|
isSubUpdating ? 'Обновляется' : 'Активен',
|
||||||
'update-sub-btn',
|
[
|
||||||
'Обновить',
|
{
|
||||||
isSubUpdating,
|
id: 'update-sub-btn',
|
||||||
|
label: 'Обновить',
|
||||||
|
isLoading: isSubUpdating,
|
||||||
|
loadingLabel: 'Обновление...'
|
||||||
|
}
|
||||||
|
],
|
||||||
subStatsHtml
|
subStatsHtml
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -105,9 +161,14 @@ async function renderSystemAndManagerStatus() {
|
||||||
dataHtml.push(createStatusCard(
|
dataHtml.push(createStatusCard(
|
||||||
'Менеджер Сетей',
|
'Менеджер Сетей',
|
||||||
isNetUpdating ? 'Обновляется' : 'Активен',
|
isNetUpdating ? 'Обновляется' : 'Активен',
|
||||||
'update-net-btn',
|
[
|
||||||
'Обновить',
|
{
|
||||||
isNetUpdating,
|
id: 'update-net-btn',
|
||||||
|
label: 'Обновить',
|
||||||
|
isLoading: isNetUpdating,
|
||||||
|
loadingLabel: 'Обновление...'
|
||||||
|
}
|
||||||
|
],
|
||||||
netResourcesHtml
|
netResourcesHtml
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -153,8 +214,57 @@ function attachEventHandlers() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#update-software-btn').off('click').on('click', function () {
|
$('#update-software-btn').off('click').on('click', async function () {
|
||||||
alert('Проверка обновлений ПО запущена...');
|
const $btn = $(this);
|
||||||
|
if ($btn.prop('disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$btn.prop('disabled', true).text('Обновление...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await JSONRPC.System.doUpdate();
|
||||||
|
alert('Обновление ПО запущено!');
|
||||||
|
await renderSystemAndManagerStatus();
|
||||||
|
} catch (e) {
|
||||||
|
alert('Ошибка при запуске обновления ПО!');
|
||||||
|
$btn.prop('disabled', false).text('Обновить');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#restart-system-btn').off('click').on('click', async function () {
|
||||||
|
const $btn = $(this);
|
||||||
|
|
||||||
|
$btn.prop('disabled', true).text('Перезагрузка...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await JSONRPC.System.restart();
|
||||||
|
alert('Перезагрузка системы инициирована!');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 10000);
|
||||||
|
} catch (e) {
|
||||||
|
alert('Ошибка при перезагрузке системы!');
|
||||||
|
$btn.prop('disabled', false).text('Перезагрузить');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#save-config-btn').off('click').on('click', async function () {
|
||||||
|
const $btn = $(this);
|
||||||
|
if ($btn.prop('disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$btn.prop('disabled', true).text('Сохранение...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await JSONRPC.System.saveConfig();
|
||||||
|
alert('Конфигурация сохранена!');
|
||||||
|
await renderSystemAndManagerStatus();
|
||||||
|
} catch (e) {
|
||||||
|
alert('Ошибка при сохранении конфигурации!');
|
||||||
|
$btn.prop('disabled', false).text('Сохранить');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue