From 31af3d6045e41dab282ca7367ea370dddb4dd912 Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 11 Jun 2024 19:43:03 +0000 Subject: [PATCH] More rework --- .../xpipe/app/browser/file/BrowserEntry.java | 7 +- .../xpipe/app/comp/base/StoreToggleComp.java | 3 +- .../xpipe/app/comp/base/ToggleSwitchComp.java | 2 + .../app/comp/store/StoreEntryWrapper.java | 2 +- .../app/ext/EnabledParentStoreProvider.java | 40 +++ .../xpipe/app/ext/EnabledStoreProvider.java | 25 ++ .../ext/SingletonSessionStoreProvider.java | 7 +- .../io/xpipe/app/resources/misc/api.md | 253 ++++++++++++------ .../io/xpipe/app/resources/style/style.css | 4 +- .../xpipe/core/store/EnabledStoreState.java | 24 ++ .../core/store/NetworkTunnelSession.java | 4 + .../xpipe/core/store/NetworkTunnelStore.java | 7 + .../io/xpipe/core/store/SessionChain.java | 6 + dist/changelogs/10.0.md | 4 + .../base/script/ScriptGroupStoreProvider.java | 26 +- .../io/xpipe/ext/base/script/ScriptStore.java | 21 +- .../script/SimpleScriptStoreProvider.java | 35 +-- ext/base/src/main/java/module-info.java | 1 + .../gradle_scripts/atlantafx-base-2.0.2.jar | Bin 710068 -> 710112 bytes openapi.yaml | 6 - 20 files changed, 322 insertions(+), 155 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/ext/EnabledParentStoreProvider.java create mode 100644 app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java create mode 100644 core/src/main/java/io/xpipe/core/store/EnabledStoreState.java diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserEntry.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserEntry.java index bcc4e940..d8e61799 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserEntry.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserEntry.java @@ -27,8 +27,9 @@ public class BrowserEntry { if (rawFileEntry == null) { return null; } + rawFileEntry = rawFileEntry.resolved(); - if (rawFileEntry.getKind() == FileKind.DIRECTORY) { + if (rawFileEntry.getKind() != FileKind.FILE) { return null; } @@ -45,6 +46,7 @@ public class BrowserEntry { if (rawFileEntry == null) { return null; } + rawFileEntry = rawFileEntry.resolved(); if (rawFileEntry.getKind() != FileKind.DIRECTORY) { return null; @@ -58,13 +60,14 @@ public class BrowserEntry { return null; } + public String getIcon() { if (fileType != null) { return fileType.getIcon(); } else if (directoryType != null) { return directoryType.getIcon(rawFileEntry, false); } else { - return rawFileEntry.getKind() == FileKind.DIRECTORY + return rawFileEntry != null && rawFileEntry.resolved().getKind() == FileKind.DIRECTORY ? "default_folder.svg" : "default_file.svg"; } diff --git a/app/src/main/java/io/xpipe/app/comp/base/StoreToggleComp.java b/app/src/main/java/io/xpipe/app/comp/base/StoreToggleComp.java index 4956e6d9..210920b8 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/StoreToggleComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/StoreToggleComp.java @@ -49,7 +49,7 @@ public class StoreToggleComp extends SimpleComp { public static StoreToggleComp enableToggle( String nameKey, StoreSection section, Function initial, BiConsumer setter) { var val = new SimpleBooleanProperty(); - ObservableValue g = val.map(aBoolean -> aBoolean ? + ObservableValue g = val.map(aBoolean -> aBoolean ? new LabelGraphic.IconGraphic("mdi2c-circle-slice-8") : new LabelGraphic.IconGraphic("mdi2p-power")); var t = new StoreToggleComp( nameKey, @@ -60,6 +60,7 @@ public class StoreToggleComp extends SimpleComp { v -> { setter.accept(section.getWrapper().getEntry().getStore().asNeeded(), v); }); + t.tooltipKey("enabled"); t.value.subscribe((newValue) -> { val.set(newValue); }); diff --git a/app/src/main/java/io/xpipe/app/comp/base/ToggleSwitchComp.java b/app/src/main/java/io/xpipe/app/comp/base/ToggleSwitchComp.java index 49a504c3..94960c97 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ToggleSwitchComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ToggleSwitchComp.java @@ -6,6 +6,7 @@ import io.xpipe.app.fxcomps.util.LabelGraphic; import io.xpipe.app.fxcomps.util.PlatformThread; import javafx.beans.property.Property; import javafx.beans.value.ObservableValue; +import javafx.css.PseudoClass; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Region; @@ -44,6 +45,7 @@ public class ToggleSwitchComp extends SimpleComp { } if (graphic != null) { s.graphicProperty().bind(PlatformThread.sync(graphic.map(labelGraphic -> labelGraphic.createGraphicNode()))); + s.pseudoClassStateChanged(PseudoClass.getPseudoClass("has-graphic"),true); } return s; } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java index 70529544..3cd10647 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java @@ -167,7 +167,7 @@ public class StoreEntryWrapper { try { var newProviders = ActionProvider.ALL.stream() .filter(dataStoreActionProvider -> { - return !dataStoreActionProvider.equals(defaultProvider) && showActionProvider(dataStoreActionProvider); + return showActionProvider(dataStoreActionProvider); }) .sorted(Comparator.comparing( actionProvider -> actionProvider.getLeafDataStoreCallSite() != null && diff --git a/app/src/main/java/io/xpipe/app/ext/EnabledParentStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/EnabledParentStoreProvider.java new file mode 100644 index 00000000..24935e30 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/EnabledParentStoreProvider.java @@ -0,0 +1,40 @@ +package io.xpipe.app.ext; + +import io.xpipe.app.comp.base.StoreToggleComp; +import io.xpipe.app.comp.store.StoreEntryComp; +import io.xpipe.app.comp.store.StoreSection; +import io.xpipe.app.comp.store.StoreViewState; +import io.xpipe.app.fxcomps.util.BindingsHelper; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.core.store.EnabledStoreState; +import io.xpipe.core.store.StatefulDataStore; + +public interface EnabledParentStoreProvider extends DataStoreProvider { + + @Override + public default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) { + if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) { + return StoreEntryComp.create(sec.getWrapper(), null, preferLarge); + } + + var enabled = StoreToggleComp.>enableToggle( + null, sec, s -> s.getState().isEnabled(), (s, aBoolean) -> { + var state = s.getState().toBuilder().enabled(aBoolean).build(); + s.setState(state); + }); + + var e = sec.getWrapper().getEntry(); + var parent = DataStorage.get().getDefaultDisplayParent(e); + if (parent.isPresent()) { + var parentWrapper = StoreViewState.get().getEntryWrapper(parent.get()); + // Disable selection if parent is already made enabled + enabled.setCustomVisibility(BindingsHelper.map(parentWrapper.getPersistentState(), o -> { + EnabledStoreState state = (EnabledStoreState) o; + return !state.isEnabled(); + })); + } + + return StoreEntryComp.create(sec.getWrapper(), enabled, preferLarge); + } +} diff --git a/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java new file mode 100644 index 00000000..d67a9b20 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java @@ -0,0 +1,25 @@ +package io.xpipe.app.ext; + +import io.xpipe.app.comp.base.StoreToggleComp; +import io.xpipe.app.comp.store.StoreEntryComp; +import io.xpipe.app.comp.store.StoreSection; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.core.store.EnabledStoreState; +import io.xpipe.core.store.StatefulDataStore; + +public interface EnabledStoreProvider extends DataStoreProvider { + + @Override + public default StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) { + if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) { + return StoreEntryComp.create(sec.getWrapper(), null, preferLarge); + } + + var enabled = StoreToggleComp.>enableToggle( + null, sec, s -> s.getState().isEnabled(), (s, aBoolean) -> { + var state = s.getState().toBuilder().enabled(aBoolean).build(); + s.setState(state); + }); + return StoreEntryComp.create(sec.getWrapper(), enabled, preferLarge); + } +} diff --git a/app/src/main/java/io/xpipe/app/ext/SingletonSessionStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/SingletonSessionStoreProvider.java index 39772ecf..720da111 100644 --- a/app/src/main/java/io/xpipe/app/ext/SingletonSessionStoreProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/SingletonSessionStoreProvider.java @@ -6,12 +6,14 @@ import io.xpipe.app.comp.store.StoreEntryComp; import io.xpipe.app.comp.store.StoreEntryWrapper; import io.xpipe.app.comp.store.StoreSection; import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.util.LabelGraphic; import io.xpipe.app.util.ThreadHelper; import io.xpipe.core.store.SingletonSessionStore; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ObservableBooleanValue; +import javafx.beans.value.ObservableValue; public interface SingletonSessionStoreProvider extends DataStoreProvider { @@ -38,7 +40,9 @@ public interface SingletonSessionStoreProvider extends DataStoreProvider { enabled.set(s.isSessionEnabled()); }); - var t = new StoreToggleComp(null, null, sec, enabled, aBoolean -> { + ObservableValue g = enabled.map(aBoolean -> aBoolean ? + new LabelGraphic.IconGraphic("mdi2c-circle-slice-8") : new LabelGraphic.IconGraphic("mdi2p-power")); + var t = new StoreToggleComp(null, g, sec, enabled, aBoolean -> { SingletonSessionStore s = sec.getWrapper().getEntry().getStore().asNeeded(); if (s.isSessionEnabled() != aBoolean) { ThreadHelper.runFailableAsync(() -> { @@ -50,6 +54,7 @@ public interface SingletonSessionStoreProvider extends DataStoreProvider { }); } }); + t.tooltipKey("enabled"); return t; } diff --git a/app/src/main/resources/io/xpipe/app/resources/misc/api.md b/app/src/main/resources/io/xpipe/app/resources/misc/api.md index 0f59f86d..8a4b6301 100644 --- a/app/src/main/resources/io/xpipe/app/resources/misc/api.md +++ b/app/src/main/resources/io/xpipe/app/resources/misc/api.md @@ -29,12 +29,12 @@ The XPipe application will start up an HTTP server that can be used to send requ You can change the port of it in the settings menu. Note that this server is HTTP-only for now as it runs only on localhost. HTTPS requests are not accepted. -The main use case for the API right now is programmatically managing remote systems. +This allows you to programmatically manage remote systems. To start off, you can query connections based on various filters. With the matched connections, you can start remote shell sessions for each one and run arbitrary commands in them. You get the command exit code and output as a response, allowing you to adapt your control flow based on command outputs. -Any kind of passwords another secret are automatically provided by XPipe when establishing a shell connection. -If required password is not stored and is set to be dynamically prompted, the running XPipe application will ask you to enter any required passwords. +Any kind of passwords and other secrets are automatically provided by XPipe when establishing a shell connection. +If a required password is not stored and is set to be dynamically prompted, the running XPipe application will ask you to enter any required passwords. You can quickly get started by either using this page as an API reference or alternatively import the [OpenAPI definition file](/openapi.yaml) into your API client of choice. See the authentication handshake below on how to authenticate prior to sending requests. @@ -102,9 +102,6 @@ Note that for development you can also turn off the required authentication in t |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|The handshake was successful. The returned token can be used for authentication in this session. The token is valid as long as XPipe is running.|[HandshakeResponse](#schemahandshakeresponse)| |400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Please check error message and your parameters.|None| -|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Authorization failed. Please supply a `Bearer` token via the `Authorization` header.|None| -|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|Authorization failed. Please supply a valid `Bearer` token via the `Authorization` header.|None| -|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|The requested resource could not be found.|None| |500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal error.|None|