+ обновление при смене конфига
+ страница со списком ресурсов + удаление старых версий + фиксы TDNS
This commit is contained in:
parent
42ccd99b30
commit
952ba3b588
|
|
@ -22,9 +22,12 @@ import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static ru.kirillius.pf.sdn.core.Util.CommandLineUtils.getArgument;
|
import static ru.kirillius.pf.sdn.core.Util.CommandLineUtils.getArgument;
|
||||||
|
|
||||||
|
|
@ -106,6 +109,41 @@ public class App implements Context, Closeable {
|
||||||
|
|
||||||
serviceManager.getService(ComponentHandlerService.class).syncComponentsWithConfig();
|
serviceManager.getService(ComponentHandlerService.class).syncComponentsWithConfig();
|
||||||
serviceManager.getService(ResourceUpdateService.class).start();
|
serviceManager.getService(ResourceUpdateService.class).start();
|
||||||
|
|
||||||
|
removeObsoleteVersions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeObsoleteVersions() {
|
||||||
|
var appVersion = serviceManager.getService(AppUpdateService.class).getAppVersion();
|
||||||
|
var listedFiles = launcherConfig.getAppLibrary().listFiles();
|
||||||
|
if (listedFiles == null) {
|
||||||
|
SystemLogger.error("Failed to list files in library path", CTX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var versions = Arrays.stream(listedFiles)
|
||||||
|
.filter(file -> file.getName().endsWith(AppUpdateService.EXTENSION))
|
||||||
|
.collect(Collectors.toMap(file -> file.getName().replace(Pattern.quote(AppUpdateService.EXTENSION), ""), file -> file));
|
||||||
|
|
||||||
|
if (!versions.containsKey(appVersion + AppUpdateService.EXTENSION)) {
|
||||||
|
SystemLogger.error("Unable to remove obsolete version because current version file is not found!", CTX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
versions.forEach((key, file) -> {
|
||||||
|
if (key.equals(appVersion + AppUpdateService.EXTENSION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SystemLogger.message("Removing obsolete version " + key, CTX);
|
||||||
|
if (!file.delete()) {
|
||||||
|
throw new RuntimeException("Delete failed");
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
SystemLogger.error("Failed to delete file " + file.getName(), CTX, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,14 @@ public final class TDNS extends AbstractComponent<TDNS.TechnitiumConfig> {
|
||||||
api.createForwarderZone(zoneName, instance.forwarder);
|
api.createForwarderZone(zoneName, instance.forwarder);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
SystemLogger.message("Instance is updated", CTX);
|
||||||
|
} catch (Exception e) {
|
||||||
SystemLogger.error("Error happened on DNS server " + instance.server + " sync", CTX, e);
|
SystemLogger.error("Error happened on DNS server " + instance.server + " sync", CTX, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SystemLogger.message("Update is completed", CTX);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,15 @@ public class System implements RPC {
|
||||||
|
|
||||||
@ProtectedMethod
|
@ProtectedMethod
|
||||||
@JRPCMethod
|
@JRPCMethod
|
||||||
public void setConfig(@JRPCArgument(name = "config") JSONObject json) {
|
public void setConfig(@JRPCArgument(name = "config") JSONObject json) throws Exception {
|
||||||
var config = JSONUtility.deserializeStructure(json, Config.class);
|
var config = JSONUtility.deserializeStructure(json, Config.class);
|
||||||
|
var initial = new Config();
|
||||||
|
initial.merge(context.getConfig());
|
||||||
context.getConfig().merge(config);
|
context.getConfig().merge(config);
|
||||||
|
context.getEventsHandler().getConfigChangeEvent().invoke(ContextEventsHandler.ConfigChangeContext.builder()
|
||||||
|
.initial(initial)
|
||||||
|
.current(config)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package ru.kirillius.pf.sdn.External.API;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TDNS {
|
||||||
|
@Test
|
||||||
|
public void t() throws IOException {
|
||||||
|
try (TDNSAPI t = new TDNSAPI("http://8.8.8.8", "sdfdfdsf")) {
|
||||||
|
t.getZones();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ import java.util.regex.Pattern;
|
||||||
* Handles application update discovery by polling a repository and downloading new packages.
|
* Handles application update discovery by polling a repository and downloading new packages.
|
||||||
*/
|
*/
|
||||||
public class AppUpdateService extends AppService {
|
public class AppUpdateService extends AppService {
|
||||||
|
public final static String EXTENSION = ".pfapp";
|
||||||
private static final String CTX = AppUpdateService.class.getSimpleName();
|
private static final String CTX = AppUpdateService.class.getSimpleName();
|
||||||
private static final Pattern VERSION_LINK_PATTERN = Pattern.compile("<a\\s+[^>]*href=\"([0-9]+(?:\\.[0-9]+)*\\.pfapp)\"", Pattern.CASE_INSENSITIVE);
|
private static final Pattern VERSION_LINK_PATTERN = Pattern.compile("<a\\s+[^>]*href=\"([0-9]+(?:\\.[0-9]+)*\\.pfapp)\"", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
|
@ -140,7 +141,7 @@ public class AppUpdateService extends AppService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileName = version + ".pfapp";
|
var fileName = version + EXTENSION;
|
||||||
var targetDirectory = appLibraryPath;
|
var targetDirectory = appLibraryPath;
|
||||||
var tempFile = targetDirectory.resolve(fileName + ".download");
|
var tempFile = targetDirectory.resolve(fileName + ".download");
|
||||||
var targetFile = targetDirectory.resolve(fileName);
|
var targetFile = targetDirectory.resolve(fileName);
|
||||||
|
|
@ -207,7 +208,7 @@ public class AppUpdateService extends AppService {
|
||||||
*/
|
*/
|
||||||
private URI buildVersionUri(String version) {
|
private URI buildVersionUri(String version) {
|
||||||
var base = repository.endsWith("/") ? repository : repository + "/";
|
var base = repository.endsWith("/") ? repository : repository + "/";
|
||||||
return URI.create(base + version + ".pfapp");
|
return URI.create(base + version + EXTENSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -276,7 +277,7 @@ public class AppUpdateService extends AppService {
|
||||||
if (href == null || href.isBlank()) {
|
if (href == null || href.isBlank()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var version = href.endsWith(".pfapp") ? href.substring(0, href.length() - 6) : href;
|
var version = href.endsWith(EXTENSION) ? href.substring(0, href.length() - 6) : href;
|
||||||
if (version.isEmpty()) {
|
if (version.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package ru.kirillius.pf.sdn.core;
|
package ru.kirillius.pf.sdn.core;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import ru.kirillius.java.utils.events.ConcurrentEventHandler;
|
import ru.kirillius.java.utils.events.ConcurrentEventHandler;
|
||||||
import ru.kirillius.java.utils.events.EventHandler;
|
import ru.kirillius.java.utils.events.EventHandler;
|
||||||
|
|
@ -25,4 +26,15 @@ public final class ContextEventsHandler {
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private final EventHandler<JSONRPCServlet> RPCInitEvent = new ConcurrentEventHandler<>();
|
private final EventHandler<JSONRPCServlet> RPCInitEvent = new ConcurrentEventHandler<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final EventHandler<ConfigChangeContext> configChangeEvent = new ConcurrentEventHandler<>();
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public final static class ConfigChangeContext {
|
||||||
|
@Getter
|
||||||
|
private Config initial;
|
||||||
|
@Getter
|
||||||
|
private Config current;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import ru.kirillius.json.JSONSerializable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for grouped network identifiers used to configure filtering and subscriptions.
|
* Container for grouped network identifiers used to configure filtering and subscriptions.
|
||||||
|
|
@ -33,6 +34,17 @@ public class NetworkResourceBundle {
|
||||||
@JSONArrayProperty(type = String.class)
|
@JSONArrayProperty(type = String.class)
|
||||||
private List<String> domains = new ArrayList<>();
|
private List<String> domains = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof NetworkResourceBundle that)) return false;
|
||||||
|
return Objects.equals(ASN, that.ASN) && Objects.equals(subnets, that.subnets) && Objects.equals(domains, that.domains);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(ASN, subnets, domains);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all stored network identifiers.
|
* Clears all stored network identifiers.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ import ru.kirillius.java.utils.events.EventListener;
|
||||||
import ru.kirillius.json.JSONUtility;
|
import ru.kirillius.json.JSONUtility;
|
||||||
import ru.kirillius.pf.sdn.core.AppService;
|
import ru.kirillius.pf.sdn.core.AppService;
|
||||||
import ru.kirillius.pf.sdn.core.Context;
|
import ru.kirillius.pf.sdn.core.Context;
|
||||||
|
import ru.kirillius.pf.sdn.core.ContextEventsHandler;
|
||||||
|
import ru.kirillius.pf.sdn.core.Subscription.SubscriptionService;
|
||||||
import ru.kirillius.pf.sdn.core.Util.IPv4Util;
|
import ru.kirillius.pf.sdn.core.Util.IPv4Util;
|
||||||
import ru.kirillius.utils.logging.SystemLogger;
|
import ru.kirillius.utils.logging.SystemLogger;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,7 +26,15 @@ public class NetworkingService extends AppService {
|
||||||
private final static String CTX = NetworkingService.class.getSimpleName();
|
private final static String CTX = NetworkingService.class.getSimpleName();
|
||||||
|
|
||||||
private final File cacheFile;
|
private final File cacheFile;
|
||||||
private final EventListener<NetworkResourceBundle> subscription;
|
private final EventListener<NetworkResourceBundle> resourceUpdateSubscription;
|
||||||
|
private final EventListener<ContextEventsHandler.ConfigChangeContext> configChangeSubscription;
|
||||||
|
|
||||||
|
private void rebuildInputs() {
|
||||||
|
inputResources.clear();
|
||||||
|
inputResources.add(context.getConfig().getCustomResources());
|
||||||
|
inputResources.add(context.getServiceManager().getService(SubscriptionService.class).getOutputResources());
|
||||||
|
triggerUpdate(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the networking service, wiring subscriptions and restoring cached state.
|
* Creates the networking service, wiring subscriptions and restoring cached state.
|
||||||
|
|
@ -32,12 +43,13 @@ public class NetworkingService extends AppService {
|
||||||
super(context);
|
super(context);
|
||||||
inputResources.clear();
|
inputResources.clear();
|
||||||
inputResources.add(context.getConfig().getCustomResources());
|
inputResources.add(context.getConfig().getCustomResources());
|
||||||
subscription = context.getEventsHandler().getSubscriptionsUpdateEvent().add(bundle -> {
|
resourceUpdateSubscription = context.getEventsHandler().getSubscriptionsUpdateEvent().add(bundle -> rebuildInputs());
|
||||||
var config = context.getConfig();
|
configChangeSubscription = context.getEventsHandler().getConfigChangeEvent().add(changeContext -> {
|
||||||
inputResources.clear();
|
var filtersChanges = !changeContext.getCurrent().getFilteredResources().equals(changeContext.getInitial().getFilteredResources());
|
||||||
inputResources.add(config.getCustomResources());
|
var resChanges = !changeContext.getCurrent().getCustomResources().equals(changeContext.getInitial().getCustomResources());
|
||||||
inputResources.add(bundle);
|
if (resChanges || filtersChanges) {
|
||||||
triggerUpdate(false);
|
NetworkingService.this.rebuildInputs();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
cacheFile = new File(context.getConfig().getCacheDirectory(), "as-cache.json");
|
cacheFile = new File(context.getConfig().getCacheDirectory(), "as-cache.json");
|
||||||
if (cacheFile.exists() && context.getConfig().isCachingAS()) {
|
if (cacheFile.exists() && context.getConfig().isCachingAS()) {
|
||||||
|
|
@ -119,8 +131,14 @@ public class NetworkingService extends AppService {
|
||||||
SystemLogger.message("Trying to summary " + subnets.size() + " subnets...", CTX);
|
SystemLogger.message("Trying to summary " + subnets.size() + " subnets...", CTX);
|
||||||
|
|
||||||
var merged = IPv4Util.summarySubnets(subnets, config.getMergeSubnetsWithUsage());
|
var merged = IPv4Util.summarySubnets(subnets, config.getMergeSubnetsWithUsage());
|
||||||
|
var unmerged = new AtomicInteger();
|
||||||
|
subnets.forEach(subnet -> {
|
||||||
|
if (!merged.getMergedSubnets().contains(subnet)) {
|
||||||
|
unmerged.getAndIncrement();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
SystemLogger.message(merged.getMergedSubnets().size() + " subnets has been merged to " + merged.getResult().size() + " new subnets", CTX);
|
SystemLogger.message(subnets.size() + " subnets has been summarized and merged to " + merged.getResult().size() + " new subnets. Unmerged: " + unmerged.get(), CTX);
|
||||||
|
|
||||||
var domains = new HashSet<>(inputResources.getDomains());
|
var domains = new HashSet<>(inputResources.getDomains());
|
||||||
filteredResources.getDomains().forEach(domains::remove);
|
filteredResources.getDomains().forEach(domains::remove);
|
||||||
|
|
@ -147,7 +165,7 @@ public class NetworkingService extends AppService {
|
||||||
try {
|
try {
|
||||||
context.getEventsHandler().getNetworkManagerUpdateEvent().invoke(outputResources);
|
context.getEventsHandler().getNetworkManagerUpdateEvent().invoke(outputResources);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
SystemLogger.error("Unable to invoke update event", CTX, e);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +199,9 @@ public class NetworkingService extends AppService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
context.getEventsHandler().getSubscriptionsUpdateEvent().remove(subscription);
|
context.getEventsHandler().getSubscriptionsUpdateEvent().remove(resourceUpdateSubscription);
|
||||||
|
context.getEventsHandler().getConfigChangeEvent().remove(configChangeSubscription);
|
||||||
|
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { TDNSConfig } from '../pages/TDNS.js';
|
||||||
import { FRRConfig } from '../pages/FRR.js';
|
import { FRRConfig } from '../pages/FRR.js';
|
||||||
import { SettingsPage } from '../pages/Settings.js';
|
import { SettingsPage } from '../pages/Settings.js';
|
||||||
import { LogsPage } from '../pages/Logs.js';
|
import { LogsPage } from '../pages/Logs.js';
|
||||||
|
import { NetworkResourcesPage } from '../pages/NetworkResources.js';
|
||||||
|
|
||||||
|
|
||||||
// Переменная для отслеживания текущего активного хеша (для корректного unmount)
|
// Переменная для отслеживания текущего активного хеша (для корректного unmount)
|
||||||
|
|
@ -28,6 +29,7 @@ const allMenuItems = [
|
||||||
{ label: 'Настройка TDNS', path: 'tdns', component: 'ru.kirillius.pf.sdn.External.API.Components.TDNS' },
|
{ label: 'Настройка TDNS', path: 'tdns', component: 'ru.kirillius.pf.sdn.External.API.Components.TDNS' },
|
||||||
{ label: 'Настройка FRR', path: 'frr', component: 'ru.kirillius.pf.sdn.External.API.Components.FRR' },
|
{ label: 'Настройка FRR', path: 'frr', component: 'ru.kirillius.pf.sdn.External.API.Components.FRR' },
|
||||||
{ label: 'Журнал', path: 'logs', component: null },
|
{ label: 'Журнал', path: 'logs', component: null },
|
||||||
|
{ label: 'Сетевые ресурсы', path: 'network-resources', component: null },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 2. Определение страниц
|
// 2. Определение страниц
|
||||||
|
|
@ -76,6 +78,11 @@ const routes = {
|
||||||
render: LogsPage.render,
|
render: LogsPage.render,
|
||||||
mount: LogsPage.mount,
|
mount: LogsPage.mount,
|
||||||
unmount: LogsPage.unmount
|
unmount: LogsPage.unmount
|
||||||
|
},
|
||||||
|
'#network-resources': {
|
||||||
|
render: NetworkResourcesPage.render,
|
||||||
|
mount: NetworkResourcesPage.mount,
|
||||||
|
unmount: NetworkResourcesPage.unmount
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,18 @@ html.dark-theme, body {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#network-resources-content .network-resource-link,
|
||||||
|
#network-resources-content .network-resource-link:visited {
|
||||||
|
color: var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network-resources-content .network-resource-link:hover,
|
||||||
|
#network-resources-content .network-resource-link:focus {
|
||||||
|
color: var(--color-text);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
/* 🔥 ОБНОВЛЕНО: Сделаем resource-key (используется для shortName) крупнее */
|
/* 🔥 ОБНОВЛЕНО: Сделаем resource-key (используется для shortName) крупнее */
|
||||||
.resource-key {
|
.resource-key {
|
||||||
font-weight: 600; /* Жирный шрифт */
|
font-weight: 600; /* Жирный шрифт */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue