+ обновление при смене конфига
+ страница со списком ресурсов + удаление старых версий + фиксы 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.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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;
|
||||
|
||||
|
|
@ -106,6 +109,41 @@ public class App implements Context, Closeable {
|
|||
|
||||
serviceManager.getService(ComponentHandlerService.class).syncComponentsWithConfig();
|
||||
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);
|
||||
}
|
||||
});
|
||||
} 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.message("Update is completed", CTX);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,15 @@ public class System implements RPC {
|
|||
|
||||
@ProtectedMethod
|
||||
@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 initial = new Config();
|
||||
initial.merge(context.getConfig());
|
||||
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.
|
||||
*/
|
||||
public class AppUpdateService extends AppService {
|
||||
public final static String EXTENSION = ".pfapp";
|
||||
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);
|
||||
|
||||
|
|
@ -140,7 +141,7 @@ public class AppUpdateService extends AppService {
|
|||
return;
|
||||
}
|
||||
|
||||
var fileName = version + ".pfapp";
|
||||
var fileName = version + EXTENSION;
|
||||
var targetDirectory = appLibraryPath;
|
||||
var tempFile = targetDirectory.resolve(fileName + ".download");
|
||||
var targetFile = targetDirectory.resolve(fileName);
|
||||
|
|
@ -207,7 +208,7 @@ public class AppUpdateService extends AppService {
|
|||
*/
|
||||
private URI buildVersionUri(String version) {
|
||||
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()) {
|
||||
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()) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package ru.kirillius.pf.sdn.core;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import ru.kirillius.java.utils.events.ConcurrentEventHandler;
|
||||
import ru.kirillius.java.utils.events.EventHandler;
|
||||
|
|
@ -25,4 +26,15 @@ public final class ContextEventsHandler {
|
|||
*/
|
||||
@Getter
|
||||
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.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Container for grouped network identifiers used to configure filtering and subscriptions.
|
||||
|
|
@ -33,6 +34,17 @@ public class NetworkResourceBundle {
|
|||
@JSONArrayProperty(type = String.class)
|
||||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ import ru.kirillius.java.utils.events.EventListener;
|
|||
import ru.kirillius.json.JSONUtility;
|
||||
import ru.kirillius.pf.sdn.core.AppService;
|
||||
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.utils.logging.SystemLogger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
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 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.
|
||||
|
|
@ -32,12 +43,13 @@ public class NetworkingService extends AppService {
|
|||
super(context);
|
||||
inputResources.clear();
|
||||
inputResources.add(context.getConfig().getCustomResources());
|
||||
subscription = context.getEventsHandler().getSubscriptionsUpdateEvent().add(bundle -> {
|
||||
var config = context.getConfig();
|
||||
inputResources.clear();
|
||||
inputResources.add(config.getCustomResources());
|
||||
inputResources.add(bundle);
|
||||
triggerUpdate(false);
|
||||
resourceUpdateSubscription = context.getEventsHandler().getSubscriptionsUpdateEvent().add(bundle -> rebuildInputs());
|
||||
configChangeSubscription = context.getEventsHandler().getConfigChangeEvent().add(changeContext -> {
|
||||
var filtersChanges = !changeContext.getCurrent().getFilteredResources().equals(changeContext.getInitial().getFilteredResources());
|
||||
var resChanges = !changeContext.getCurrent().getCustomResources().equals(changeContext.getInitial().getCustomResources());
|
||||
if (resChanges || filtersChanges) {
|
||||
NetworkingService.this.rebuildInputs();
|
||||
}
|
||||
});
|
||||
cacheFile = new File(context.getConfig().getCacheDirectory(), "as-cache.json");
|
||||
if (cacheFile.exists() && context.getConfig().isCachingAS()) {
|
||||
|
|
@ -119,8 +131,14 @@ public class NetworkingService extends AppService {
|
|||
SystemLogger.message("Trying to summary " + subnets.size() + " subnets...", CTX);
|
||||
|
||||
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());
|
||||
filteredResources.getDomains().forEach(domains::remove);
|
||||
|
|
@ -147,7 +165,7 @@ public class NetworkingService extends AppService {
|
|||
try {
|
||||
context.getEventsHandler().getNetworkManagerUpdateEvent().invoke(outputResources);
|
||||
} 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
|
||||
public void close() throws IOException {
|
||||
context.getEventsHandler().getSubscriptionsUpdateEvent().remove(subscription);
|
||||
context.getEventsHandler().getSubscriptionsUpdateEvent().remove(resourceUpdateSubscription);
|
||||
context.getEventsHandler().getConfigChangeEvent().remove(configChangeSubscription);
|
||||
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { TDNSConfig } from '../pages/TDNS.js';
|
|||
import { FRRConfig } from '../pages/FRR.js';
|
||||
import { SettingsPage } from '../pages/Settings.js';
|
||||
import { LogsPage } from '../pages/Logs.js';
|
||||
import { NetworkResourcesPage } from '../pages/NetworkResources.js';
|
||||
|
||||
|
||||
// Переменная для отслеживания текущего активного хеша (для корректного unmount)
|
||||
|
|
@ -28,6 +29,7 @@ const allMenuItems = [
|
|||
{ 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: 'Журнал', path: 'logs', component: null },
|
||||
{ label: 'Сетевые ресурсы', path: 'network-resources', component: null },
|
||||
];
|
||||
|
||||
// 2. Определение страниц
|
||||
|
|
@ -76,6 +78,11 @@ const routes = {
|
|||
render: LogsPage.render,
|
||||
mount: LogsPage.mount,
|
||||
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;
|
||||
}
|
||||
|
||||
#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 {
|
||||
font-weight: 600; /* Жирный шрифт */
|
||||
|
|
|
|||
Loading…
Reference in New Issue