diff --git a/app/src/main/java/io/xpipe/app/ext/LocalStore.java b/app/src/main/java/io/xpipe/app/ext/LocalStore.java index 8e18ba074..66ee92b2b 100644 --- a/app/src/main/java/io/xpipe/app/ext/LocalStore.java +++ b/app/src/main/java/io/xpipe/app/ext/LocalStore.java @@ -4,6 +4,7 @@ import io.xpipe.app.process.ShellControl; import io.xpipe.app.process.ShellStoreState; import com.fasterxml.jackson.annotation.JsonTypeName; +import io.xpipe.app.storage.DataStoreEntryRef; import lombok.Value; @JsonTypeName("local") @@ -30,7 +31,7 @@ public class LocalStore implements NetworkTunnelStore, ShellStore, StatefulDataS } @Override - public DataStore getNetworkParent() { + public DataStoreEntryRef getNetworkParent() { return null; } diff --git a/app/src/main/java/io/xpipe/app/ext/NetworkTunnelStore.java b/app/src/main/java/io/xpipe/app/ext/NetworkTunnelStore.java index dff30ba04..8ba0b0eb8 100644 --- a/app/src/main/java/io/xpipe/app/ext/NetworkTunnelStore.java +++ b/app/src/main/java/io/xpipe/app/ext/NetworkTunnelStore.java @@ -1,8 +1,25 @@ package io.xpipe.app.ext; -public interface NetworkTunnelStore extends DataStore { +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntryRef; - DataStore getNetworkParent(); +import java.util.Optional; + +public interface NetworkTunnelStore extends DataStore, SelfReferentialStore { + + static void checkTunneable(DataStoreEntryRef ref) throws ValidationException { + if (!(ref.getStore() instanceof NetworkTunnelStore t)) { + throw new ValidationException(AppI18n.get("parentHostDoesNotSupportTunneling", ref.get().getName())); + } + + var unsupported = t.getUnsupportedParent(); + if (unsupported.isPresent()) { + throw new ValidationException(AppI18n.get("parentHostDoesNotSupportTunneling", unsupported.get().get().getName())); + } + } + + DataStoreEntryRef getNetworkParent(); default boolean requiresTunnel() { return getNetworkParent() != null; @@ -12,21 +29,25 @@ public interface NetworkTunnelStore extends DataStore { return null; } - default boolean isLocallyTunnelable() { - NetworkTunnelStore current = this; + default Optional> getUnsupportedParent() { + DataStoreEntryRef current = getSelfEntry().ref(); while (true) { - var p = current.getNetworkParent(); + var p = current.getStore().getNetworkParent(); if (p == null) { - return true; + return Optional.empty(); } - if (p instanceof NetworkTunnelStore t) { - current = t; + if (p.getStore() instanceof NetworkTunnelStore) { + current = p.asNeeded(); } else { - return false; + return Optional.of(current); } } } + default boolean isLocallyTunnelable() { + return getUnsupportedParent().isEmpty(); + } + NetworkTunnelSession createTunnelSession(int localPort, int remotePort, String address) throws Exception; } diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationModel.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationModel.java index 667f8a2b5..c2fd94ec0 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationModel.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationModel.java @@ -38,7 +38,6 @@ public class StoreCreationModel { Property validator = new SimpleObjectProperty<>(new SimpleValidator()); BooleanProperty finished = new SimpleBooleanProperty(); ObservableValue entry; - BooleanProperty changedSinceError = new SimpleBooleanProperty(); BooleanProperty skippable = new SimpleBooleanProperty(); BooleanProperty connectable = new SimpleBooleanProperty(); StringProperty name; @@ -61,12 +60,6 @@ public class StoreCreationModel { this.existingEntry = existingEntry; this.staticDisplay = staticDisplay; this.consumer = consumer; - this.store.addListener((c, o, n) -> { - changedSinceError.setValue(true); - }); - this.name.addListener((c, o, n) -> { - changedSinceError.setValue(true); - }); this.provider.addListener((c, o, n) -> { store.unbind(); @@ -212,12 +205,11 @@ public class StoreCreationModel { .getFirst() .getText(); ErrorEventFactory.fromMessage(msg).expected().handle(); - changedSinceError.setValue(false); return; } // We didn't change anything - if (!wasChanged()) { + if (store.getValue().isComplete() && !wasChanged()) { commit(false); return; } @@ -248,8 +240,6 @@ public class StoreCreationModel { ErrorEventFactory.expected(ex); } - changedSinceError.setValue(false); - ErrorEventFactory.fromThrowable(ex).handle(); } finally { if (DataStorage.get() != null) { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStore.java b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStore.java index 734cca334..4324ebd44 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/service/AbstractServiceStore.java @@ -34,7 +34,7 @@ public abstract class AbstractServiceStore implements SingletonSessionStore