mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
More rework
This commit is contained in:
parent
02b9ba6062
commit
31af3d6045
20 changed files with 322 additions and 155 deletions
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class StoreToggleComp extends SimpleComp {
|
|||
public static <T extends DataStore> StoreToggleComp enableToggle(
|
||||
String nameKey, StoreSection section, Function<T, Boolean> initial, BiConsumer<T, Boolean> setter) {
|
||||
var val = new SimpleBooleanProperty();
|
||||
ObservableValue<LabelGraphic> g = val.map(aBoolean -> aBoolean ?
|
||||
ObservableValue<LabelGraphic> 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);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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.<StatefulDataStore<EnabledStoreState>>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);
|
||||
}
|
||||
}
|
25
app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java
Normal file
25
app/src/main/java/io/xpipe/app/ext/EnabledStoreProvider.java
Normal file
|
@ -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.<StatefulDataStore<EnabledStoreState>>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);
|
||||
}
|
||||
}
|
|
@ -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<LabelGraphic> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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|
|
||||
|
||||
<aside class="success">
|
||||
|
@ -152,26 +149,48 @@ headers = {
|
|||
'Accept': 'application/json'
|
||||
}
|
||||
|
||||
r = requests.post('http://localhost:21721/handshake', headers = headers)
|
||||
data = """
|
||||
{
|
||||
"auth": {
|
||||
"type": "ApiKey",
|
||||
"key": "<API key>"
|
||||
},
|
||||
"client": {
|
||||
"type": "Api",
|
||||
"name": "My client name"
|
||||
}
|
||||
}
|
||||
"""
|
||||
r = requests.post('http://localhost:21721/handshake', headers = headers, data = data)
|
||||
|
||||
print(r.json())
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
URL obj = new URL("http://localhost:21721/handshake");
|
||||
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
int responseCode = con.getResponseCode();
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuffer response = new StringBuffer();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
var uri = URI.create("http://localhost:21721/handshake");
|
||||
var client = HttpClient.newHttpClient();
|
||||
var request = HttpRequest
|
||||
.newBuilder()
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||
{
|
||||
"auth": {
|
||||
"type": "ApiKey",
|
||||
"key": "<API key>"
|
||||
},
|
||||
"client": {
|
||||
"type": "Api",
|
||||
"name": "My client name"
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
System.out.println(response.toString());
|
||||
"""))
|
||||
.build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.statusCode());
|
||||
System.out.println(response.body());
|
||||
|
||||
```
|
||||
|
||||
|
@ -204,8 +223,19 @@ func main() {
|
|||
```shell
|
||||
# You can also use wget
|
||||
curl -X POST http://localhost:21721/handshake \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json'
|
||||
-H 'Content-Type: application/json' \ -H 'Accept: application/json' \
|
||||
--data '
|
||||
{
|
||||
"auth": {
|
||||
"type": "ApiKey",
|
||||
"key": "<API key>"
|
||||
},
|
||||
"client": {
|
||||
"type": "Api",
|
||||
"name": "My client name"
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
```
|
||||
|
||||
|
@ -326,26 +356,39 @@ headers = {
|
|||
'Authorization': 'Bearer {access-token}'
|
||||
}
|
||||
|
||||
r = requests.post('http://localhost:21721/connection/query', headers = headers)
|
||||
data = """
|
||||
{
|
||||
"categoryFilter": "*",
|
||||
"connectionFilter": "*",
|
||||
"typeFilter": "*"
|
||||
}
|
||||
"""
|
||||
r = requests.post('http://localhost:21721/connection/query', headers = headers, data = data)
|
||||
|
||||
print(r.json())
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
URL obj = new URL("http://localhost:21721/connection/query");
|
||||
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
int responseCode = con.getResponseCode();
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuffer response = new StringBuffer();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
var uri = URI.create("http://localhost:21721/connection/query");
|
||||
var client = HttpClient.newHttpClient();
|
||||
var request = HttpRequest
|
||||
.newBuilder()
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer {access-token}")
|
||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||
{
|
||||
"categoryFilter": "*",
|
||||
"connectionFilter": "*",
|
||||
"typeFilter": "*"
|
||||
}
|
||||
in.close();
|
||||
System.out.println(response.toString());
|
||||
"""))
|
||||
.build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.statusCode());
|
||||
System.out.println(response.body());
|
||||
|
||||
```
|
||||
|
||||
|
@ -379,9 +422,14 @@ func main() {
|
|||
```shell
|
||||
# You can also use wget
|
||||
curl -X POST http://localhost:21721/connection/query \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Authorization: Bearer {access-token}'
|
||||
-H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||
--data '
|
||||
{
|
||||
"categoryFilter": "*",
|
||||
"connectionFilter": "*",
|
||||
"typeFilter": "*"
|
||||
}
|
||||
'
|
||||
|
||||
```
|
||||
|
||||
|
@ -462,26 +510,34 @@ headers = {
|
|||
'Authorization': 'Bearer {access-token}'
|
||||
}
|
||||
|
||||
r = requests.post('http://localhost:21721/shell/start', headers = headers)
|
||||
data = """
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||
}
|
||||
"""
|
||||
r = requests.post('http://localhost:21721/shell/start', headers = headers, data = data)
|
||||
|
||||
print(r.json())
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
URL obj = new URL("http://localhost:21721/shell/start");
|
||||
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
int responseCode = con.getResponseCode();
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuffer response = new StringBuffer();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
var uri = URI.create("http://localhost:21721/shell/start");
|
||||
var client = HttpClient.newHttpClient();
|
||||
var request = HttpRequest
|
||||
.newBuilder()
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer {access-token}")
|
||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||
}
|
||||
in.close();
|
||||
System.out.println(response.toString());
|
||||
"""))
|
||||
.build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.statusCode());
|
||||
System.out.println(response.body());
|
||||
|
||||
```
|
||||
|
||||
|
@ -514,8 +570,12 @@ func main() {
|
|||
```shell
|
||||
# You can also use wget
|
||||
curl -X POST http://localhost:21721/shell/start \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer {access-token}'
|
||||
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||
--data '
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||
}
|
||||
'
|
||||
|
||||
```
|
||||
|
||||
|
@ -596,26 +656,34 @@ headers = {
|
|||
'Authorization': 'Bearer {access-token}'
|
||||
}
|
||||
|
||||
r = requests.post('http://localhost:21721/shell/stop', headers = headers)
|
||||
data = """
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||
}
|
||||
"""
|
||||
r = requests.post('http://localhost:21721/shell/stop', headers = headers, data = data)
|
||||
|
||||
print(r.json())
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
URL obj = new URL("http://localhost:21721/shell/stop");
|
||||
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
int responseCode = con.getResponseCode();
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuffer response = new StringBuffer();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
var uri = URI.create("http://localhost:21721/shell/stop");
|
||||
var client = HttpClient.newHttpClient();
|
||||
var request = HttpRequest
|
||||
.newBuilder()
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer {access-token}")
|
||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||
}
|
||||
in.close();
|
||||
System.out.println(response.toString());
|
||||
"""))
|
||||
.build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.statusCode());
|
||||
System.out.println(response.body());
|
||||
|
||||
```
|
||||
|
||||
|
@ -648,8 +716,12 @@ func main() {
|
|||
```shell
|
||||
# You can also use wget
|
||||
curl -X POST http://localhost:21721/shell/stop \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer {access-token}'
|
||||
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||
--data '
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||
}
|
||||
'
|
||||
|
||||
```
|
||||
|
||||
|
@ -755,26 +827,37 @@ headers = {
|
|||
'Authorization': 'Bearer {access-token}'
|
||||
}
|
||||
|
||||
r = requests.post('http://localhost:21721/shell/exec', headers = headers)
|
||||
data = """
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||
"command": "echo $USER"
|
||||
}
|
||||
"""
|
||||
r = requests.post('http://localhost:21721/shell/exec', headers = headers, data = data)
|
||||
|
||||
print(r.json())
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
URL obj = new URL("http://localhost:21721/shell/exec");
|
||||
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
int responseCode = con.getResponseCode();
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream()));
|
||||
String inputLine;
|
||||
StringBuffer response = new StringBuffer();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
response.append(inputLine);
|
||||
var uri = URI.create("http://localhost:21721/shell/exec");
|
||||
var client = HttpClient.newHttpClient();
|
||||
var request = HttpRequest
|
||||
.newBuilder()
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer {access-token}")
|
||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||
"command": "echo $USER"
|
||||
}
|
||||
in.close();
|
||||
System.out.println(response.toString());
|
||||
"""))
|
||||
.build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.statusCode());
|
||||
System.out.println(response.body());
|
||||
|
||||
```
|
||||
|
||||
|
@ -808,9 +891,13 @@ func main() {
|
|||
```shell
|
||||
# You can also use wget
|
||||
curl -X POST http://localhost:21721/shell/exec \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Authorization: Bearer {access-token}'
|
||||
-H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||
--data '
|
||||
{
|
||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||
"command": "echo $USER"
|
||||
}
|
||||
'
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
}
|
||||
*/
|
||||
|
||||
.toggle-switch .label {
|
||||
.toggle-switch:has-graphic .label {
|
||||
-fx-font-size: 1.7em;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
.toggle-switch:has-graphic {
|
||||
-fx-font-size: 0.8em;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper=true)
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@Jacksonized
|
||||
public class EnabledStoreState extends DataStoreState {
|
||||
|
||||
boolean enabled;
|
||||
|
||||
@Override
|
||||
public DataStoreState mergeCopy(DataStoreState newer) {
|
||||
var n = (EnabledStoreState) newer;
|
||||
return EnabledStoreState.builder().enabled(n.enabled).build();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
public abstract class NetworkTunnelSession extends Session {
|
||||
|
||||
protected NetworkTunnelSession(SessionListener listener) {
|
||||
|
@ -9,4 +11,6 @@ public abstract class NetworkTunnelSession extends Session {
|
|||
public abstract int getLocalPort();
|
||||
|
||||
public abstract int getRemotePort();
|
||||
|
||||
public abstract ShellControl getShellControl();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -119,6 +121,11 @@ public interface NetworkTunnelStore extends DataStore {
|
|||
public int getRemotePort() {
|
||||
return remotePort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellControl getShellControl() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SessionChain extends NetworkTunnelSession {
|
||||
|
@ -15,6 +17,10 @@ public class SessionChain extends NetworkTunnelSession {
|
|||
}));
|
||||
}
|
||||
|
||||
public ShellControl getShellControl() {
|
||||
return sessions.getLast().getShellControl();
|
||||
}
|
||||
|
||||
public int getLocalPort() {
|
||||
return sessions.getFirst().getLocalPort();
|
||||
}
|
||||
|
|
4
dist/changelogs/10.0.md
vendored
4
dist/changelogs/10.0.md
vendored
|
@ -47,9 +47,13 @@ You can now order connections relative to other sibling connections. This orderi
|
|||
|
||||
## Other
|
||||
|
||||
- Support VMs for VNC tunneling
|
||||
- The Linux installers now contain application icons from multiple sizes it should increase the icon display quality
|
||||
- The Linux builds now list socat as a dependency such that the kitty terminal integration will work without issues
|
||||
- Searching for connections has been improved to show children as well
|
||||
- The welcome screen will now also contain the option to straight up jump to the synchronization settings
|
||||
- Add support for foot terminal
|
||||
- Fix elementary terminal not launching correctly
|
||||
- Fix kubernetes not elevating correctly for non-default contexts
|
||||
- Fix ohmyzsh update notification freezing shell
|
||||
- Fix file browser icons being broken for links
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package io.xpipe.ext.base.script;
|
||||
|
||||
import io.xpipe.app.comp.base.StoreToggleComp;
|
||||
import io.xpipe.app.comp.base.SystemStateComp;
|
||||
import io.xpipe.app.comp.store.*;
|
||||
import io.xpipe.app.comp.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.store.StoreViewState;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.EnabledStoreProvider;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
|
||||
|
@ -18,21 +19,7 @@ import lombok.SneakyThrows;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public class ScriptGroupStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) {
|
||||
return new DenseStoreEntryComp(sec.getWrapper(), true, null);
|
||||
}
|
||||
|
||||
var enabled = StoreToggleComp.<ScriptGroupStore>enableToggle(
|
||||
null, sec, s -> s.getState().isEnabled(), (s, aBoolean) -> {
|
||||
var state = s.getState().toBuilder().enabled(aBoolean).build();
|
||||
s.setState(state);
|
||||
});
|
||||
return new DenseStoreEntryComp(sec.getWrapper(), true, enabled);
|
||||
}
|
||||
public class ScriptGroupStoreProvider implements EnabledStoreProvider, DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public Comp<?> stateDisplay(StoreEntryWrapper w) {
|
||||
|
@ -89,6 +76,11 @@ public class ScriptGroupStoreProvider implements DataStoreProvider {
|
|||
return new SimpleStringProperty(scriptStore.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String summaryString(StoreEntryWrapper wrapper) {
|
||||
return "Script group";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayIconFileName(DataStore store) {
|
||||
return "proc:shellEnvironment_icon.svg";
|
||||
|
|
|
@ -9,20 +9,21 @@ import io.xpipe.app.util.Validators;
|
|||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellInitCommand;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.DataStoreState;
|
||||
import io.xpipe.core.store.EnabledStoreState;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.StatefulDataStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import lombok.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Singular;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public abstract class ScriptStore extends JacksonizedValue implements DataStore, StatefulDataStore<ScriptStore.State> {
|
||||
public abstract class ScriptStore extends JacksonizedValue implements DataStore, StatefulDataStore<EnabledStoreState> {
|
||||
|
||||
protected final DataStoreEntryRef<ScriptGroupStore> group;
|
||||
|
||||
|
@ -185,8 +186,8 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class<State> getStateClass() {
|
||||
return State.class;
|
||||
public Class<EnabledStoreState> getStateClass() {
|
||||
return EnabledStoreState.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,12 +212,4 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
protected abstract void queryFlattenedScripts(LinkedHashSet<SimpleScriptStore> all);
|
||||
|
||||
public abstract List<DataStoreEntryRef<ScriptStore>> getEffectiveScripts();
|
||||
|
||||
@Value
|
||||
@EqualsAndHashCode(callSuper=true)
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@Jacksonized
|
||||
public static class State extends DataStoreState {
|
||||
boolean enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package io.xpipe.ext.base.script;
|
||||
|
||||
import io.xpipe.app.comp.base.*;
|
||||
import io.xpipe.app.comp.store.*;
|
||||
import io.xpipe.app.comp.base.IntegratedTextAreaComp;
|
||||
import io.xpipe.app.comp.base.ListSelectorComp;
|
||||
import io.xpipe.app.comp.base.SystemStateComp;
|
||||
import io.xpipe.app.comp.store.StoreEntryWrapper;
|
||||
import io.xpipe.app.comp.store.StoreViewState;
|
||||
import io.xpipe.app.core.AppExtensionManager;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.EnabledParentStoreProvider;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreChoiceComp;
|
||||
import io.xpipe.app.fxcomps.impl.DataStoreListChoiceComp;
|
||||
import io.xpipe.app.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.MarkdownBuilder;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
|
@ -31,7 +34,7 @@ import java.util.List;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SimpleScriptStoreProvider implements DataStoreProvider {
|
||||
public class SimpleScriptStoreProvider implements EnabledParentStoreProvider, DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public boolean editByDefault() {
|
||||
|
@ -43,30 +46,6 @@ public class SimpleScriptStoreProvider implements DataStoreProvider {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoreEntryComp customEntryComp(StoreSection sec, boolean preferLarge) {
|
||||
if (sec.getWrapper().getValidity().getValue() != DataStoreEntry.Validity.COMPLETE) {
|
||||
return new DenseStoreEntryComp(sec.getWrapper(), true, null);
|
||||
}
|
||||
|
||||
var enabled = StoreToggleComp.<SimpleScriptStore>enableToggle(
|
||||
null, sec, s -> s.getState().isEnabled(), (s, aBoolean) -> {
|
||||
var state = s.getState().toBuilder().enabled(aBoolean).build();
|
||||
s.setState(state);
|
||||
});
|
||||
|
||||
SimpleScriptStore s = sec.getWrapper().getEntry().getStore().asNeeded();
|
||||
var groupWrapper = StoreViewState.get().getEntryWrapper(s.getGroup().getEntry());
|
||||
|
||||
// Disable selection if parent group is already made enabled
|
||||
enabled.setCustomVisibility(BindingsHelper.map(groupWrapper.getPersistentState(), o -> {
|
||||
ScriptStore.State state = (ScriptStore.State) o;
|
||||
return !state.isEnabled();
|
||||
}));
|
||||
|
||||
return new DenseStoreEntryComp(sec.getWrapper(), true, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldHaveChildren() {
|
||||
return false;
|
||||
|
|
|
@ -32,6 +32,7 @@ open module io.xpipe.ext.base {
|
|||
requires static io.xpipe.app;
|
||||
requires org.kordamp.ikonli.javafx;
|
||||
requires atlantafx.base;
|
||||
requires jdk.jfr;
|
||||
|
||||
provides BrowserAction with RunScriptAction,
|
||||
FollowLinkAction,
|
||||
|
|
Binary file not shown.
|
@ -63,12 +63,6 @@ paths:
|
|||
$ref: '#/components/schemas/HandshakeResponse'
|
||||
400:
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
401:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
403:
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
500:
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
/connection/query:
|
||||
|
|
Loading…
Reference in a new issue