Compare commits

..

No commits in common. "a3133326991dfde05ae5bf1b2262d5792d5d2fc5" and "73bfc4a4b6dc1357c983ba98a180fe249e3d1a62" have entirely different histories.

7 changed files with 80 additions and 170 deletions

View File

@ -59,7 +59,7 @@ public class App implements Context, Closeable {
} catch (IOException e) { } catch (IOException e) {
loadedConfig = new Config(); loadedConfig = new Config();
loadedConfig.setSubscriptions(new ArrayList<>(List.of( 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 { try {
Config.store(loadedConfig, launcherConfig.getConfigFile()); Config.store(loadedConfig, launcherConfig.getConfigFile());

View File

@ -3,6 +3,9 @@ package ru.kirillius.pf.sdn.External.API;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder; 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.Context;
import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle; import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig; import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig;
@ -11,16 +14,14 @@ import ru.kirillius.pf.sdn.core.Util.HashUtil;
import ru.kirillius.utils.logging.SystemLogger; import ru.kirillius.utils.logging.SystemLogger;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.*;
import java.util.Map;
import java.util.StringJoiner;
/** /**
* Subscription provider that pulls JSON resource bundles from a Git repository cache. * Subscription provider that pulls JSON resource bundles from a Git repository cache.
*/ */
public class GitSubscription implements SubscriptionProvider { public class GitSubscription implements SubscriptionProvider {
private final static String CTX = GitSubscription.class.getSimpleName();
private final Context context; private final Context context;
/** /**
@ -30,43 +31,90 @@ public class GitSubscription implements SubscriptionProvider {
this.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 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. * Runs {@code git fetch} and {@code git pull} when remote updates are available.
*/ */
private static void checkAndPullUpdates(Git git) { private static void checkAndPullUpdates(Git git) throws GitAPIException {
try {
var fetchResult = git.fetch()
.setCheckFetchedObjects(true)
.call();
if (fetchResult.getTrackingRefUpdates() != null && !fetchResult.getTrackingRefUpdates().isEmpty()) { var fetchResult = git.fetch()
SystemLogger.message("Downloading updates...", CTX); .setCheckFetchedObjects(true)
var pullResult = git.pull().call(); .call();
if (pullResult.isSuccessful()) { if (fetchResult.getTrackingRefUpdates() != null && !fetchResult.getTrackingRefUpdates().isEmpty()) {
SystemLogger.message("Git pull is successful", CTX); SystemLogger.message("Downloading updates...", CTX);
var pullResult = git.pull().call();
// Проверяем были ли обновлены файлы if (pullResult.isSuccessful()) {
if (pullResult.getFetchResult() != null && !pullResult.getFetchResult().getTrackingRefUpdates().isEmpty()) { SystemLogger.message("Git pull is successful", CTX);
var updatedFiles = new StringJoiner("\n");
pullResult.getFetchResult().getTrackingRefUpdates().forEach(refUpdate -> updatedFiles.add(" - " + refUpdate.getLocalName() + // Проверяем были ли обновлены файлы
" : " + refUpdate.getOldObjectId().abbreviate(7).name() + if (pullResult.getFetchResult() != null && !pullResult.getFetchResult().getTrackingRefUpdates().isEmpty()) {
" -> " + refUpdate.getNewObjectId().abbreviate(7).name())); var updatedFiles = new StringJoiner("\n");
SystemLogger.message("Updated files: " + updatedFiles, CTX); pullResult.getFetchResult().getTrackingRefUpdates().forEach(refUpdate -> updatedFiles.add(" - " + refUpdate.getLocalName() +
} " : " + refUpdate.getOldObjectId().abbreviate(7).name() +
} else { " -> " + refUpdate.getNewObjectId().abbreviate(7).name()));
SystemLogger.error("Download failed", CTX); SystemLogger.message("Updated files: " + updatedFiles, CTX);
} }
} else { } else {
SystemLogger.message("There is no updates", CTX); SystemLogger.error("Download failed", CTX);
} }
} catch (Exception e) { } else {
SystemLogger.error("Failed to fetch & pull repository", CTX, e); SystemLogger.message("There is no updates", CTX);
} }
} }
private final static String CTX = GitSubscription.class.getSimpleName();
/** /**
* Clones the repository into the given path. * Clones the repository into the given path.
*/ */
@ -102,49 +150,4 @@ public class GitSubscription implements SubscriptionProvider {
var gitDir = new File(directory, ".git"); var gitDir = new File(directory, ".git");
return gitDir.exists() && gitDir.isDirectory(); 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);
}
}
} }

View File

@ -1,77 +0,0 @@
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;
}
}

View File

@ -14,7 +14,6 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*; import java.util.*;
/** /**
@ -30,7 +29,7 @@ public class TDNSAPI implements Closeable {
*/ */
public TDNSAPI(String server, String authToken) { public TDNSAPI(String server, String authToken) {
this.server = server; this.server = server;
httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); httpClient = HttpClient.newBuilder().build();
this.authToken = authToken; this.authToken = authToken;
} }

View File

@ -6,7 +6,6 @@ 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.External.API.GitSubscription; 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.InMemoryLogHandler;
import ru.kirillius.pf.sdn.core.*; import ru.kirillius.pf.sdn.core.*;
import ru.kirillius.pf.sdn.web.ProtectedMethod; import ru.kirillius.pf.sdn.web.ProtectedMethod;
@ -125,8 +124,6 @@ public class System implements RPC {
public JSONArray getRepositoryTypes() { public JSONArray getRepositoryTypes() {
var array = new JSONArray(); var array = new JSONArray();
array.put(GitSubscription.class.getName()); array.put(GitSubscription.class.getName());
array.put(LocalFilesystemSubscription.class.getName());
return array; return array;
} }

View File

@ -28,9 +28,4 @@ public class RepositoryConfig {
@Getter @Getter
@JSONProperty @JSONProperty
private String source; private String source;
@Setter
@Getter
@JSONProperty(required = false)
private String script = "";
} }

View File

@ -30,8 +30,7 @@ const CLASS_NAMES = {
subscriptionRemove: 'settings-subscription-remove', subscriptionRemove: 'settings-subscription-remove',
subscriptionName: 'settings-subscription-name', subscriptionName: 'settings-subscription-name',
subscriptionType: 'settings-subscription-type', subscriptionType: 'settings-subscription-type',
subscriptionSource: 'settings-subscription-source', subscriptionSource: 'settings-subscription-source'
subscriptionScript: 'settings-subscription-script'
}; };
let currentConfig = {}; let currentConfig = {};
@ -152,7 +151,6 @@ function createSubscriptionRow(subscription = {}, index = 0) {
const name = subscription.name || ''; const name = subscription.name || '';
const type = subscription.type || ''; const type = subscription.type || '';
const source = subscription.source || ''; const source = subscription.source || '';
const script = subscription.script || '';
const uid = `${Date.now()}-${index}`; const uid = `${Date.now()}-${index}`;
return ` return `
@ -175,10 +173,6 @@ function createSubscriptionRow(subscription = {}, index = 0) {
<label for="subscription-source-${uid}">Источник</label> <label for="subscription-source-${uid}">Источник</label>
<input type="text" id="subscription-source-${uid}" class="form-control ${CLASS_NAMES.subscriptionSource}" value="${source}"> <input type="text" id="subscription-source-${uid}" class="form-control ${CLASS_NAMES.subscriptionSource}" value="${source}">
</div> </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> </div>
`; `;
} }
@ -348,15 +342,14 @@ function collectSubscriptions() {
const name = $entry.find(`.${CLASS_NAMES.subscriptionName}`).val().trim(); const name = $entry.find(`.${CLASS_NAMES.subscriptionName}`).val().trim();
const type = $entry.find(`.${CLASS_NAMES.subscriptionType}`).val(); const type = $entry.find(`.${CLASS_NAMES.subscriptionType}`).val();
const source = $entry.find(`.${CLASS_NAMES.subscriptionSource}`).val().trim(); const source = $entry.find(`.${CLASS_NAMES.subscriptionSource}`).val().trim();
const script = $entry.find(`.${CLASS_NAMES.subscriptionScript}`).val().trim();
if (!type) { if (!type) {
hasEmptyType = true; hasEmptyType = true;
return false; return false;
} }
if (name || type || source || script) { if (name || type || source) {
subscriptions.push({ name, type, source, script }); subscriptions.push({ name, type, source });
} }
return true; return true;
}); });