Compare commits
2 Commits
73bfc4a4b6
...
a313332699
| Author | SHA1 | Date |
|---|---|---|
|
|
a313332699 | |
|
|
aff02e11f6 |
|
|
@ -59,7 +59,7 @@ public class App implements Context, Closeable {
|
|||
} catch (IOException e) {
|
||||
loadedConfig = new Config();
|
||||
loadedConfig.setSubscriptions(new ArrayList<>(List.of(
|
||||
new RepositoryConfig("updates", GitSubscription.class, "https://git.kirillius.ru/kirillius/protected-resources-list.git")
|
||||
new RepositoryConfig("updates", GitSubscription.class, "https://git.kirillius.ru/kirillius/protected-resources-list.git", "")
|
||||
)));
|
||||
try {
|
||||
Config.store(loadedConfig, launcherConfig.getConfigFile());
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ package ru.kirillius.pf.sdn.External.API;
|
|||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
import ru.kirillius.json.JSONUtility;
|
||||
import ru.kirillius.pf.sdn.core.Context;
|
||||
import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
|
||||
import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig;
|
||||
|
|
@ -14,14 +11,16 @@ import ru.kirillius.pf.sdn.core.Util.HashUtil;
|
|||
import ru.kirillius.utils.logging.SystemLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Subscription provider that pulls JSON resource bundles from a Git repository cache.
|
||||
*/
|
||||
public class GitSubscription implements SubscriptionProvider {
|
||||
private final static String CTX = GitSubscription.class.getSimpleName();
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
|
|
@ -31,90 +30,43 @@ public class GitSubscription implements SubscriptionProvider {
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones or updates the configured repository and loads resource bundles from the {@code resources} directory.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, NetworkResourceBundle> getResources(RepositoryConfig config) {
|
||||
try {
|
||||
var repoDir = new File(context.getConfig().getCacheDirectory(), "git/" + HashUtil.md5(config.getName()));
|
||||
|
||||
if (!repoDir.exists()) {
|
||||
if (!repoDir.mkdirs()) {
|
||||
throw new IOException("Unable to create directory: " + repoDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
var existing = isGitRepository(repoDir);
|
||||
var repository = existing ? openRepository(repoDir) : cloneRepository(config.getSource(), repoDir);
|
||||
if (existing) {
|
||||
SystemLogger.message("Fetching git repository " + config.getName() + " (" + config.getSource() + ")", CTX);
|
||||
checkAndPullUpdates(repository);
|
||||
}
|
||||
repository.close();
|
||||
|
||||
var resourcesDir = new File(repoDir, "resources");
|
||||
if (!resourcesDir.exists()) {
|
||||
SystemLogger.error(resourcesDir + " is not exist in repo (" + config.getSource() + ")", CTX);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
var map = new HashMap<String, NetworkResourceBundle>();
|
||||
for (var file : Objects.requireNonNull(resourcesDir.listFiles())) {
|
||||
try (var stream = new FileInputStream(file)) {
|
||||
var name = file.getName();
|
||||
if (!name.endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
var bundle = JSONUtility.deserializeStructure(new JSONObject(new JSONTokener(stream)), NetworkResourceBundle.class);
|
||||
map.put(name.substring(0, name.length() - 5), bundle);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
} catch (Exception e) {
|
||||
SystemLogger.error("Error while reading git repository", CTX, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs {@code git fetch} and {@code git pull} when remote updates are available.
|
||||
*/
|
||||
private static void checkAndPullUpdates(Git git) throws GitAPIException {
|
||||
private static void checkAndPullUpdates(Git git) {
|
||||
try {
|
||||
|
||||
var fetchResult = git.fetch()
|
||||
.setCheckFetchedObjects(true)
|
||||
.call();
|
||||
|
||||
var fetchResult = git.fetch()
|
||||
.setCheckFetchedObjects(true)
|
||||
.call();
|
||||
if (fetchResult.getTrackingRefUpdates() != null && !fetchResult.getTrackingRefUpdates().isEmpty()) {
|
||||
SystemLogger.message("Downloading updates...", CTX);
|
||||
var pullResult = git.pull().call();
|
||||
|
||||
if (fetchResult.getTrackingRefUpdates() != null && !fetchResult.getTrackingRefUpdates().isEmpty()) {
|
||||
SystemLogger.message("Downloading updates...", CTX);
|
||||
var pullResult = git.pull().call();
|
||||
if (pullResult.isSuccessful()) {
|
||||
SystemLogger.message("Git pull is successful", CTX);
|
||||
|
||||
if (pullResult.isSuccessful()) {
|
||||
SystemLogger.message("Git pull is successful", CTX);
|
||||
|
||||
// Проверяем были ли обновлены файлы
|
||||
if (pullResult.getFetchResult() != null && !pullResult.getFetchResult().getTrackingRefUpdates().isEmpty()) {
|
||||
var updatedFiles = new StringJoiner("\n");
|
||||
pullResult.getFetchResult().getTrackingRefUpdates().forEach(refUpdate -> updatedFiles.add(" - " + refUpdate.getLocalName() +
|
||||
" : " + refUpdate.getOldObjectId().abbreviate(7).name() +
|
||||
" -> " + refUpdate.getNewObjectId().abbreviate(7).name()));
|
||||
SystemLogger.message("Updated files: " + updatedFiles, CTX);
|
||||
// Проверяем были ли обновлены файлы
|
||||
if (pullResult.getFetchResult() != null && !pullResult.getFetchResult().getTrackingRefUpdates().isEmpty()) {
|
||||
var updatedFiles = new StringJoiner("\n");
|
||||
pullResult.getFetchResult().getTrackingRefUpdates().forEach(refUpdate -> updatedFiles.add(" - " + refUpdate.getLocalName() +
|
||||
" : " + refUpdate.getOldObjectId().abbreviate(7).name() +
|
||||
" -> " + refUpdate.getNewObjectId().abbreviate(7).name()));
|
||||
SystemLogger.message("Updated files: " + updatedFiles, CTX);
|
||||
}
|
||||
} else {
|
||||
SystemLogger.error("Download failed", CTX);
|
||||
}
|
||||
} else {
|
||||
SystemLogger.error("Download failed", CTX);
|
||||
SystemLogger.message("There is no updates", CTX);
|
||||
}
|
||||
} else {
|
||||
SystemLogger.message("There is no updates", CTX);
|
||||
} catch (Exception e) {
|
||||
SystemLogger.error("Failed to fetch & pull repository", CTX, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final static String CTX = GitSubscription.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Clones the repository into the given path.
|
||||
*/
|
||||
|
|
@ -150,4 +102,49 @@ public class GitSubscription implements SubscriptionProvider {
|
|||
var gitDir = new File(directory, ".git");
|
||||
return gitDir.exists() && gitDir.isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones or updates the configured repository and loads resource bundles from the {@code resources} directory.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, NetworkResourceBundle> getResources(RepositoryConfig config) {
|
||||
try {
|
||||
var repoDir = new File(context.getConfig().getCacheDirectory(), "git/" + HashUtil.md5(config.getName()));
|
||||
|
||||
if (!repoDir.exists()) {
|
||||
if (!repoDir.mkdirs()) {
|
||||
throw new IOException("Unable to create directory: " + repoDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
var existing = isGitRepository(repoDir);
|
||||
var repository = existing ? openRepository(repoDir) : cloneRepository(config.getSource(), repoDir);
|
||||
if (existing) {
|
||||
SystemLogger.message("Fetching git repository " + config.getName() + " (" + config.getSource() + ")", CTX);
|
||||
checkAndPullUpdates(repository);
|
||||
}
|
||||
repository.close();
|
||||
|
||||
if(!config.getScript().isBlank()){
|
||||
try (var shell = new ShellExecutor(ShellExecutor.Config.builder().useSSH(false).build())) {
|
||||
var result = shell.executeCommand(new String[]{
|
||||
config.getScript(),
|
||||
config.getSource()
|
||||
});
|
||||
SystemLogger.message("Shell command execution result:"+result, CTX);
|
||||
}
|
||||
}
|
||||
|
||||
var resourcesDir = new File(repoDir, "resources");
|
||||
if (!resourcesDir.exists()) {
|
||||
SystemLogger.error(resourcesDir + " is not exist in repo (" + config.getSource() + ")", CTX);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return LocalFilesystemSubscription.scanLocalDirectory(resourcesDir);
|
||||
} catch (Exception e) {
|
||||
SystemLogger.error("Error while reading git repository", CTX, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
77
app/src/main/java/ru/kirillius/pf/sdn/External/API/LocalFilesystemSubscription.java
vendored
Normal file
77
app/src/main/java/ru/kirillius/pf/sdn/External/API/LocalFilesystemSubscription.java
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package ru.kirillius.pf.sdn.External.API;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
import ru.kirillius.json.JSONUtility;
|
||||
import ru.kirillius.pf.sdn.core.Context;
|
||||
import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
|
||||
import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig;
|
||||
import ru.kirillius.pf.sdn.core.Subscription.SubscriptionProvider;
|
||||
import ru.kirillius.utils.logging.SystemLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Subscription provider that pulls JSON resource bundles from a Git repository cache.
|
||||
*/
|
||||
public class LocalFilesystemSubscription implements SubscriptionProvider {
|
||||
private final static String CTX = LocalFilesystemSubscription.class.getSimpleName();
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Creates the provider using the shared application context.
|
||||
*/
|
||||
public LocalFilesystemSubscription(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones or updates the configured repository and loads resource bundles from the {@code resources} directory.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, NetworkResourceBundle> getResources(RepositoryConfig config) {
|
||||
try {
|
||||
var resourcesDir = new File(config.getSource());
|
||||
if (!resourcesDir.exists()) {
|
||||
SystemLogger.error(resourcesDir + " is not exist directory", CTX);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
if(!config.getScript().isBlank()){
|
||||
try (var shell = new ShellExecutor(ShellExecutor.Config.builder().useSSH(false).build())) {
|
||||
var result = shell.executeCommand(new String[]{
|
||||
config.getScript(),
|
||||
config.getSource()
|
||||
});
|
||||
SystemLogger.message("Shell command execution result:"+result, CTX);
|
||||
}
|
||||
}
|
||||
return scanLocalDirectory(resourcesDir);
|
||||
|
||||
} catch (Exception e) {
|
||||
SystemLogger.error("Error while reading local FS repository", CTX, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, NetworkResourceBundle> scanLocalDirectory(File resourcesDir) throws IOException {
|
||||
var map = new HashMap<String, NetworkResourceBundle>();
|
||||
for (var file : Objects.requireNonNull(resourcesDir.listFiles())) {
|
||||
try (var stream = new FileInputStream(file)) {
|
||||
var name = file.getName();
|
||||
if (!name.endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
var bundle = JSONUtility.deserializeStructure(new JSONObject(new JSONTokener(stream)), NetworkResourceBundle.class);
|
||||
map.put(name.substring(0, name.length() - 5), bundle);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import java.net.http.HttpClient;
|
|||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
|
@ -29,7 +30,7 @@ public class TDNSAPI implements Closeable {
|
|||
*/
|
||||
public TDNSAPI(String server, String authToken) {
|
||||
this.server = server;
|
||||
httpClient = HttpClient.newBuilder().build();
|
||||
httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import ru.kirillius.json.JSONUtility;
|
|||
import ru.kirillius.json.rpc.Annotations.JRPCArgument;
|
||||
import ru.kirillius.json.rpc.Annotations.JRPCMethod;
|
||||
import ru.kirillius.pf.sdn.External.API.GitSubscription;
|
||||
import ru.kirillius.pf.sdn.External.API.LocalFilesystemSubscription;
|
||||
import ru.kirillius.pf.sdn.InMemoryLogHandler;
|
||||
import ru.kirillius.pf.sdn.core.*;
|
||||
import ru.kirillius.pf.sdn.web.ProtectedMethod;
|
||||
|
|
@ -124,6 +125,8 @@ public class System implements RPC {
|
|||
public JSONArray getRepositoryTypes() {
|
||||
var array = new JSONArray();
|
||||
array.put(GitSubscription.class.getName());
|
||||
array.put(LocalFilesystemSubscription.class.getName());
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,4 +28,9 @@ public class RepositoryConfig {
|
|||
@Getter
|
||||
@JSONProperty
|
||||
private String source;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@JSONProperty(required = false)
|
||||
private String script = "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ const CLASS_NAMES = {
|
|||
subscriptionRemove: 'settings-subscription-remove',
|
||||
subscriptionName: 'settings-subscription-name',
|
||||
subscriptionType: 'settings-subscription-type',
|
||||
subscriptionSource: 'settings-subscription-source'
|
||||
subscriptionSource: 'settings-subscription-source',
|
||||
subscriptionScript: 'settings-subscription-script'
|
||||
};
|
||||
|
||||
let currentConfig = {};
|
||||
|
|
@ -151,6 +152,7 @@ function createSubscriptionRow(subscription = {}, index = 0) {
|
|||
const name = subscription.name || '';
|
||||
const type = subscription.type || '';
|
||||
const source = subscription.source || '';
|
||||
const script = subscription.script || '';
|
||||
const uid = `${Date.now()}-${index}`;
|
||||
|
||||
return `
|
||||
|
|
@ -173,6 +175,10 @@ function createSubscriptionRow(subscription = {}, index = 0) {
|
|||
<label for="subscription-source-${uid}">Источник</label>
|
||||
<input type="text" id="subscription-source-${uid}" class="form-control ${CLASS_NAMES.subscriptionSource}" value="${source}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subscription-script-${uid}">Скрипт</label>
|
||||
<input type="text" id="subscription-script-${uid}" class="form-control ${CLASS_NAMES.subscriptionScript}" value="${script}">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -342,14 +348,15 @@ function collectSubscriptions() {
|
|||
const name = $entry.find(`.${CLASS_NAMES.subscriptionName}`).val().trim();
|
||||
const type = $entry.find(`.${CLASS_NAMES.subscriptionType}`).val();
|
||||
const source = $entry.find(`.${CLASS_NAMES.subscriptionSource}`).val().trim();
|
||||
const script = $entry.find(`.${CLASS_NAMES.subscriptionScript}`).val().trim();
|
||||
|
||||
if (!type) {
|
||||
hasEmptyType = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name || type || source) {
|
||||
subscriptions.push({ name, type, source });
|
||||
if (name || type || source || script) {
|
||||
subscriptions.push({ name, type, source, script });
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue