mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
api rework [stage]
This commit is contained in:
parent
33577ca7c1
commit
c5608bd23c
23 changed files with 1010 additions and 136 deletions
33
app/src/main/java/io/xpipe/app/beacon/AppBeaconCache.java
Normal file
33
app/src/main/java/io/xpipe/app/beacon/AppBeaconCache.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package io.xpipe.app.beacon;
|
||||||
|
|
||||||
|
import io.xpipe.beacon.BeaconClientException;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class AppBeaconCache {
|
||||||
|
|
||||||
|
Set<BeaconShellSession> shellSessions = new HashSet<>();
|
||||||
|
Map<UUID, byte[]> savedBlobs = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public BeaconShellSession getShellSession(UUID uuid) throws BeaconClientException {
|
||||||
|
var found = shellSessions.stream().filter(beaconShellSession -> beaconShellSession.getEntry().getUuid().equals(uuid)).findFirst();
|
||||||
|
if (found.isEmpty()) {
|
||||||
|
throw new BeaconClientException("No active shell session known for id " + uuid);
|
||||||
|
}
|
||||||
|
return found.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBlob(UUID uuid) throws BeaconClientException {
|
||||||
|
var found = savedBlobs.get(uuid);
|
||||||
|
if (found == null) {
|
||||||
|
throw new BeaconClientException("No saved data known for id " + uuid);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ public class AppBeaconServer {
|
||||||
private final Set<BeaconSession> sessions = new HashSet<>();
|
private final Set<BeaconSession> sessions = new HashSet<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Set<BeaconShellSession> shellSessions = new HashSet<>();
|
private final AppBeaconCache cache = new AppBeaconCache();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String localAuthSecret;
|
private String localAuthSecret;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.xpipe.app.beacon;
|
package io.xpipe.app.beacon;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
import io.xpipe.app.core.mode.OperationMode;
|
import io.xpipe.app.core.mode.OperationMode;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
|
@ -7,15 +9,13 @@ import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
import io.xpipe.app.util.ThreadHelper;
|
||||||
import io.xpipe.beacon.*;
|
import io.xpipe.beacon.*;
|
||||||
import io.xpipe.core.util.JacksonMapper;
|
import io.xpipe.core.util.JacksonMapper;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
|
||||||
import com.sun.net.httpserver.HttpHandler;
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class BeaconRequestHandler<T> implements HttpHandler {
|
public class BeaconRequestHandler<T> implements HttpHandler {
|
||||||
|
|
||||||
|
@ -63,14 +63,19 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
||||||
Object response;
|
Object response;
|
||||||
try {
|
try {
|
||||||
try (InputStream is = exchange.getRequestBody()) {
|
try (InputStream is = exchange.getRequestBody()) {
|
||||||
var tree = JacksonMapper.getDefault().readTree(is);
|
var read = is.readAllBytes();
|
||||||
TrackEvent.trace("Parsed raw request:\n" + tree.toPrettyString());
|
var rawDataRequestClass = beaconInterface.getRequestClass().getDeclaredFields().length == 1 &&
|
||||||
var emptyRequestClass =
|
beaconInterface.getRequestClass().getDeclaredFields()[0].getType().equals(byte[].class);
|
||||||
tree.isEmpty() && beaconInterface.getRequestClass().getDeclaredFields().length == 0;
|
if (!new String(read, StandardCharsets.US_ASCII).trim().startsWith("{") && rawDataRequestClass) {
|
||||||
object = emptyRequestClass
|
object = createRawDataRequest(beaconInterface,read);
|
||||||
? createDefaultRequest(beaconInterface)
|
} else {
|
||||||
: JacksonMapper.getDefault().treeToValue(tree, beaconInterface.getRequestClass());
|
var tree = JacksonMapper.getDefault().readTree(read);
|
||||||
TrackEvent.trace("Parsed request object:\n" + object);
|
TrackEvent.trace("Parsed raw request:\n" + tree.toPrettyString());
|
||||||
|
var emptyRequestClass = tree.isEmpty() && beaconInterface.getRequestClass().getDeclaredFields().length == 0;
|
||||||
|
object = emptyRequestClass ? createDefaultRequest(beaconInterface) : JacksonMapper.getDefault().treeToValue(tree,
|
||||||
|
beaconInterface.getRequestClass());
|
||||||
|
TrackEvent.trace("Parsed request object:\n" + object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
response = beaconInterface.handle(exchange, object);
|
response = beaconInterface.handle(exchange, object);
|
||||||
} catch (BeaconClientException clientException) {
|
} catch (BeaconClientException clientException) {
|
||||||
|
@ -79,7 +84,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
||||||
return;
|
return;
|
||||||
} catch (BeaconServerException serverException) {
|
} catch (BeaconServerException serverException) {
|
||||||
var cause = serverException.getCause() != null ? serverException.getCause() : serverException;
|
var cause = serverException.getCause() != null ? serverException.getCause() : serverException;
|
||||||
ErrorEvent.fromThrowable(cause).handle();
|
ErrorEvent.fromThrowable(cause).omit().expected().handle();
|
||||||
writeError(exchange, new BeaconServerErrorResponse(cause), 500);
|
writeError(exchange, new BeaconServerErrorResponse(cause), 500);
|
||||||
return;
|
return;
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
@ -93,7 +98,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} catch (Throwable other) {
|
} catch (Throwable other) {
|
||||||
ErrorEvent.fromThrowable(other).handle();
|
ErrorEvent.fromThrowable(other).omit().expected().handle();
|
||||||
writeError(exchange, new BeaconServerErrorResponse(other), 500);
|
writeError(exchange, new BeaconServerErrorResponse(other), 500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -143,4 +148,20 @@ public class BeaconRequestHandler<T> implements HttpHandler {
|
||||||
m.setAccessible(true);
|
m.setAccessible(true);
|
||||||
return (REQ) beaconInterface.getRequestClass().cast(m.invoke(b));
|
return (REQ) beaconInterface.getRequestClass().cast(m.invoke(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <REQ> REQ createRawDataRequest(BeaconInterface<?> beaconInterface, byte[] s) {
|
||||||
|
var c = beaconInterface.getRequestClass().getDeclaredMethod("builder");
|
||||||
|
c.setAccessible(true);
|
||||||
|
|
||||||
|
var b = c.invoke(null);
|
||||||
|
var setMethod = Arrays.stream(b.getClass().getDeclaredMethods()).filter(method -> method.getParameterCount() == 1 &&
|
||||||
|
method.getParameters()[0].getType().equals(byte[].class)).findFirst().orElseThrow();
|
||||||
|
setMethod.invoke(b, (Object) s);
|
||||||
|
|
||||||
|
var m = b.getClass().getDeclaredMethod("build");
|
||||||
|
m.setAccessible(true);
|
||||||
|
return (REQ) beaconInterface.getRequestClass().cast(m.invoke(b));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package io.xpipe.app.beacon;
|
package io.xpipe.app.beacon;
|
||||||
|
|
||||||
import io.xpipe.beacon.BeaconClientInformation;
|
import io.xpipe.beacon.BeaconClientInformation;
|
||||||
|
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!typeMatcher.matcher(storeEntry.getProvider().getId()).matches()) {
|
if (!typeMatcher.matcher(storeEntry.getProvider().getId().toLowerCase()).matches()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,14 @@
|
||||||
package io.xpipe.app.beacon.impl;
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
import io.xpipe.app.core.mode.OperationMode;
|
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
|
||||||
import io.xpipe.beacon.BeaconClientException;
|
|
||||||
import io.xpipe.beacon.BeaconServerException;
|
|
||||||
import io.xpipe.beacon.api.DaemonModeExchange;
|
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import io.xpipe.app.core.mode.OperationMode;
|
||||||
import java.io.IOException;
|
import io.xpipe.beacon.BeaconClientException;
|
||||||
|
import io.xpipe.beacon.api.DaemonModeExchange;
|
||||||
|
|
||||||
public class DaemonModeExchangeImpl extends DaemonModeExchange {
|
public class DaemonModeExchangeImpl extends DaemonModeExchange {
|
||||||
@Override
|
@Override
|
||||||
public Object handle(HttpExchange exchange, Request msg)
|
public Object handle(HttpExchange exchange, Request msg)
|
||||||
throws BeaconClientException {
|
throws BeaconClientException {
|
||||||
// Wait for startup
|
|
||||||
while (OperationMode.get() == null) {
|
|
||||||
ThreadHelper.sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
var mode = OperationMode.map(msg.getMode());
|
var mode = OperationMode.map(msg.getMode());
|
||||||
if (!mode.isSupported()) {
|
if (!mode.isSupported()) {
|
||||||
throw new BeaconClientException("Unsupported mode: " + msg.getMode().getDisplayName() + ". Supported: "
|
throw new BeaconClientException("Unsupported mode: " + msg.getMode().getDisplayName() + ". Supported: "
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import io.xpipe.app.beacon.AppBeaconServer;
|
||||||
|
import io.xpipe.beacon.api.FsBlobExchange;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class FsBlobExchangeImpl extends FsBlobExchange {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public Object handle(HttpExchange exchange, Request msg) {
|
||||||
|
var id = UUID.randomUUID();
|
||||||
|
AppBeaconServer.get().getCache().getSavedBlobs().put(id, msg.getPayload());
|
||||||
|
return Response.builder().blob(id).build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import io.xpipe.app.beacon.AppBeaconServer;
|
||||||
|
import io.xpipe.app.util.ScriptHelper;
|
||||||
|
import io.xpipe.beacon.api.FsScriptExchange;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class FsScriptExchangeImpl extends FsScriptExchange {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public Object handle(HttpExchange exchange, Request msg) {
|
||||||
|
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
|
||||||
|
var data = new String(AppBeaconServer.get().getCache().getBlob(msg.getBlob()), StandardCharsets.UTF_8);
|
||||||
|
var file = ScriptHelper.getExecScriptFile(shell.getControl());
|
||||||
|
shell.getControl().getShellDialect().createScriptTextFileWriteCommand(shell.getControl(), data, file.toString());
|
||||||
|
return Response.builder().path(file).build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import io.xpipe.app.beacon.AppBeaconServer;
|
||||||
|
import io.xpipe.beacon.api.FsWriteExchange;
|
||||||
|
import io.xpipe.core.store.ConnectionFileSystem;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
public class FsWriteExchangeImpl extends FsWriteExchange {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public Object handle(HttpExchange exchange, Request msg) {
|
||||||
|
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
|
||||||
|
var data = AppBeaconServer.get().getCache().getBlob(msg.getBlob());
|
||||||
|
var fs = new ConnectionFileSystem(shell.getControl());
|
||||||
|
try (var os = fs.openOutput(msg.getPath().toString(), data.length)) {
|
||||||
|
os.write(data);
|
||||||
|
}
|
||||||
|
return Response.builder().build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,10 @@
|
||||||
package io.xpipe.app.beacon.impl;
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
import io.xpipe.app.beacon.AppBeaconServer;
|
|
||||||
import io.xpipe.app.storage.DataStorage;
|
|
||||||
import io.xpipe.beacon.BeaconClientException;
|
|
||||||
import io.xpipe.beacon.BeaconServerException;
|
|
||||||
import io.xpipe.beacon.api.ShellExecExchange;
|
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import io.xpipe.app.beacon.AppBeaconServer;
|
||||||
|
import io.xpipe.beacon.api.ShellExecExchange;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
public class ShellExecExchangeImpl extends ShellExecExchange {
|
public class ShellExecExchangeImpl extends ShellExecExchange {
|
||||||
|
@ -17,20 +12,11 @@ public class ShellExecExchangeImpl extends ShellExecExchange {
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Object handle(HttpExchange exchange, Request msg) {
|
public Object handle(HttpExchange exchange, Request msg) {
|
||||||
var e = DataStorage.get()
|
var existing = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
|
||||||
.getStoreEntryIfPresent(msg.getConnection())
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
|
||||||
var existing = AppBeaconServer.get().getShellSessions().stream()
|
|
||||||
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
|
||||||
.findFirst();
|
|
||||||
if (existing.isEmpty()) {
|
|
||||||
throw new BeaconClientException("No shell session active for connection");
|
|
||||||
}
|
|
||||||
|
|
||||||
AtomicReference<String> out = new AtomicReference<>();
|
AtomicReference<String> out = new AtomicReference<>();
|
||||||
AtomicReference<String> err = new AtomicReference<>();
|
AtomicReference<String> err = new AtomicReference<>();
|
||||||
long exitCode;
|
long exitCode;
|
||||||
try (var command = existing.get().getControl().command(msg.getCommand()).start()) {
|
try (var command = existing.getControl().command(msg.getCommand()).start()) {
|
||||||
command.accumulateStdout(s -> out.set(s));
|
command.accumulateStdout(s -> out.set(s));
|
||||||
command.accumulateStderr(s -> err.set(s));
|
command.accumulateStderr(s -> err.set(s));
|
||||||
exitCode = command.getExitCode();
|
exitCode = command.getExitCode();
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
package io.xpipe.app.beacon.impl;
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import io.xpipe.app.beacon.AppBeaconServer;
|
import io.xpipe.app.beacon.AppBeaconServer;
|
||||||
import io.xpipe.app.beacon.BeaconShellSession;
|
import io.xpipe.app.beacon.BeaconShellSession;
|
||||||
import io.xpipe.app.storage.DataStorage;
|
import io.xpipe.app.storage.DataStorage;
|
||||||
import io.xpipe.beacon.BeaconClientException;
|
import io.xpipe.beacon.BeaconClientException;
|
||||||
import io.xpipe.beacon.BeaconServerException;
|
|
||||||
import io.xpipe.beacon.api.ShellStartExchange;
|
import io.xpipe.beacon.api.ShellStartExchange;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class ShellStartExchangeImpl extends ShellStartExchange {
|
public class ShellStartExchangeImpl extends ShellStartExchange {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,7 +21,7 @@ public class ShellStartExchangeImpl extends ShellStartExchange {
|
||||||
throw new BeaconClientException("Not a shell connection");
|
throw new BeaconClientException("Not a shell connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
var existing = AppBeaconServer.get().getShellSessions().stream()
|
var existing = AppBeaconServer.get().getCache().getShellSessions().stream()
|
||||||
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (existing.isPresent()) {
|
if (existing.isPresent()) {
|
||||||
|
@ -33,7 +29,7 @@ public class ShellStartExchangeImpl extends ShellStartExchange {
|
||||||
}
|
}
|
||||||
|
|
||||||
var control = s.control().start();
|
var control = s.control().start();
|
||||||
AppBeaconServer.get().getShellSessions().add(new BeaconShellSession(e, control));
|
AppBeaconServer.get().getCache().getShellSessions().add(new BeaconShellSession(e, control));
|
||||||
return Response.builder().build();
|
return Response.builder().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,18 @@
|
||||||
package io.xpipe.app.beacon.impl;
|
package io.xpipe.app.beacon.impl;
|
||||||
|
|
||||||
import io.xpipe.app.beacon.AppBeaconServer;
|
|
||||||
import io.xpipe.app.storage.DataStorage;
|
|
||||||
import io.xpipe.beacon.BeaconClientException;
|
|
||||||
import io.xpipe.beacon.BeaconServerException;
|
|
||||||
import io.xpipe.beacon.api.ShellStopExchange;
|
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import io.xpipe.app.beacon.AppBeaconServer;
|
||||||
|
import io.xpipe.beacon.api.ShellStopExchange;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class ShellStopExchangeImpl extends ShellStopExchange {
|
public class ShellStopExchangeImpl extends ShellStopExchange {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public Object handle(HttpExchange exchange, Request msg) {
|
public Object handle(HttpExchange exchange, Request msg) {
|
||||||
var e = DataStorage.get()
|
var e = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
|
||||||
.getStoreEntryIfPresent(msg.getConnection())
|
e.getControl().close();
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Unknown connection"));
|
AppBeaconServer.get().getCache().getShellSessions().remove(e);
|
||||||
var existing = AppBeaconServer.get().getShellSessions().stream()
|
|
||||||
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
|
|
||||||
.findFirst();
|
|
||||||
if (existing.isPresent()) {
|
|
||||||
existing.get().getControl().close();
|
|
||||||
AppBeaconServer.get().getShellSessions().remove(existing.get());
|
|
||||||
}
|
|
||||||
return Response.builder().build();
|
return Response.builder().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,9 @@ open module io.xpipe.app {
|
||||||
DaemonStatusExchangeImpl,
|
DaemonStatusExchangeImpl,
|
||||||
DaemonStopExchangeImpl,
|
DaemonStopExchangeImpl,
|
||||||
HandshakeExchangeImpl,
|
HandshakeExchangeImpl,
|
||||||
DaemonModeExchangeImpl,
|
DaemonModeExchangeImpl, FsBlobExchangeImpl,
|
||||||
|
FsScriptExchangeImpl,
|
||||||
|
FsWriteExchangeImpl,
|
||||||
AskpassExchangeImpl,
|
AskpassExchangeImpl,
|
||||||
TerminalWaitExchangeImpl,
|
TerminalWaitExchangeImpl,
|
||||||
TerminalLaunchExchangeImpl,
|
TerminalLaunchExchangeImpl,
|
||||||
|
|
|
@ -26,7 +26,7 @@ headingLevel: 2
|
||||||
The XPipe API provides programmatic access to XPipe’s features.
|
The XPipe API provides programmatic access to XPipe’s features.
|
||||||
You can get started by either using this page as an API reference or alternatively import the OpenAPI definition file into your API client of choice:
|
You can get started by either using this page as an API reference or alternatively import the OpenAPI definition file into your API client of choice:
|
||||||
|
|
||||||
<a href="/openapi.yaml" style="font-size: 20px">OpenAPI .yaml specification</a>
|
<a download href="/openapi.yaml" style="font-size: 20px">OpenAPI .yaml specification</a>
|
||||||
|
|
||||||
The XPipe application will start up an HTTP server that can be used to send requests.
|
The XPipe application will start up an HTTP server that can be used to send requests.
|
||||||
You can change the port of it in the settings menu.
|
You can change the port of it in the settings menu.
|
||||||
|
@ -279,22 +279,22 @@ All matching is case insensitive.
|
||||||
{
|
{
|
||||||
"found": [
|
"found": [
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
"category": [
|
"category": [
|
||||||
"default"
|
"default"
|
||||||
],
|
],
|
||||||
"connection": [
|
"name": [
|
||||||
"local machine"
|
"local machine"
|
||||||
],
|
],
|
||||||
"type": "local"
|
"type": "local"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uuid": "e1462ddc-9beb-484c-bd91-bb666027e300",
|
"connection": "e1462ddc-9beb-484c-bd91-bb666027e300",
|
||||||
"category": [
|
"category": [
|
||||||
"default",
|
"default",
|
||||||
"category 1"
|
"category 1"
|
||||||
],
|
],
|
||||||
"connection": [
|
"name": [
|
||||||
"ssh system",
|
"ssh system",
|
||||||
"shell environments",
|
"shell environments",
|
||||||
"bash"
|
"bash"
|
||||||
|
@ -453,7 +453,7 @@ These errors will be returned with the HTTP return code 500.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -485,7 +485,7 @@ bearerAuth
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const inputBody = '{
|
const inputBody = '{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}';
|
}';
|
||||||
const headers = {
|
const headers = {
|
||||||
'Content-Type':'application/json',
|
'Content-Type':'application/json',
|
||||||
|
@ -515,7 +515,7 @@ headers = {
|
||||||
|
|
||||||
data = """
|
data = """
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
r = requests.post('http://localhost:21723/shell/start', headers = headers, data = data)
|
r = requests.post('http://localhost:21723/shell/start', headers = headers, data = data)
|
||||||
|
@ -534,7 +534,7 @@ var request = HttpRequest
|
||||||
.header("Authorization", "Bearer {access-token}")
|
.header("Authorization", "Bearer {access-token}")
|
||||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
.build();
|
.build();
|
||||||
|
@ -576,7 +576,7 @@ curl -X POST http://localhost:21723/shell/start \
|
||||||
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||||
--data '
|
--data '
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
@ -599,7 +599,7 @@ If the shell is busy or stuck, you might have to work with timeouts to account f
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -631,7 +631,7 @@ bearerAuth
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const inputBody = '{
|
const inputBody = '{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}';
|
}';
|
||||||
const headers = {
|
const headers = {
|
||||||
'Content-Type':'application/json',
|
'Content-Type':'application/json',
|
||||||
|
@ -661,7 +661,7 @@ headers = {
|
||||||
|
|
||||||
data = """
|
data = """
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
r = requests.post('http://localhost:21723/shell/stop', headers = headers, data = data)
|
r = requests.post('http://localhost:21723/shell/stop', headers = headers, data = data)
|
||||||
|
@ -680,7 +680,7 @@ var request = HttpRequest
|
||||||
.header("Authorization", "Bearer {access-token}")
|
.header("Authorization", "Bearer {access-token}")
|
||||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
.build();
|
.build();
|
||||||
|
@ -722,7 +722,7 @@ curl -X POST http://localhost:21723/shell/stop \
|
||||||
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||||
--data '
|
--data '
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
@ -746,7 +746,7 @@ However, if any other error occurs like the shell not responding or exiting unex
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
"command": "echo $USER"
|
"command": "echo $USER"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -799,7 +799,7 @@ bearerAuth
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const inputBody = '{
|
const inputBody = '{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
"command": "echo $USER"
|
"command": "echo $USER"
|
||||||
}';
|
}';
|
||||||
const headers = {
|
const headers = {
|
||||||
|
@ -832,7 +832,7 @@ headers = {
|
||||||
|
|
||||||
data = """
|
data = """
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
"command": "echo $USER"
|
"command": "echo $USER"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
@ -853,7 +853,7 @@ var request = HttpRequest
|
||||||
.header("Authorization", "Bearer {access-token}")
|
.header("Authorization", "Bearer {access-token}")
|
||||||
.POST(HttpRequest.BodyPublishers.ofString("""
|
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
"command": "echo $USER"
|
"command": "echo $USER"
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
|
@ -897,7 +897,7 @@ curl -X POST http://localhost:21723/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 '
|
--data '
|
||||||
{
|
{
|
||||||
"uuid": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
"command": "echo $USER"
|
"command": "echo $USER"
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
@ -906,6 +906,474 @@ curl -X POST http://localhost:21723/shell/exec \
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## Store a raw blob to be used later
|
||||||
|
|
||||||
|
<a id="opIdfsData"></a>
|
||||||
|
|
||||||
|
`POST /fs/blob`
|
||||||
|
|
||||||
|
Stores arbitrary binary data in a blob such that it can be used later on to for example write to a remote file.
|
||||||
|
|
||||||
|
This will return a uuid which can be used as a reference to the blob.
|
||||||
|
You can also store normal text data in blobs if you intend to create text or shell script files with it.
|
||||||
|
|
||||||
|
> Body parameter
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
string
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3 id="store-a-raw-blob-to-be-used-later-parameters">Parameters</h3>
|
||||||
|
|
||||||
|
|Name|In|Type|Required|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|body|body|string(binary)|true|none|
|
||||||
|
|
||||||
|
> Example responses
|
||||||
|
|
||||||
|
> The operation was successful. The data was stored.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3 id="store-a-raw-blob-to-be-used-later-responses">Responses</h3>
|
||||||
|
|
||||||
|
|Status|Meaning|Description|Schema|
|
||||||
|
|---|---|---|---|
|
||||||
|
|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|The operation was successful. The data was stored.|[FsBlobResponse](#schemafsblobresponse)|
|
||||||
|
|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="warning">
|
||||||
|
To perform this operation, you must be authenticated by means of one of the following methods:
|
||||||
|
bearerAuth
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>Code samples</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const inputBody = 'string';
|
||||||
|
const headers = {
|
||||||
|
'Content-Type':'application/octet-stream',
|
||||||
|
'Accept':'application/json',
|
||||||
|
'Authorization':'Bearer {access-token}'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('http://localhost:21723/fs/blob',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: inputBody,
|
||||||
|
headers: headers
|
||||||
|
})
|
||||||
|
.then(function(res) {
|
||||||
|
return res.json();
|
||||||
|
}).then(function(body) {
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/octet-stream',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': 'Bearer {access-token}'
|
||||||
|
}
|
||||||
|
|
||||||
|
data = """
|
||||||
|
string
|
||||||
|
"""
|
||||||
|
r = requests.post('http://localhost:21723/fs/blob', headers = headers, data = data)
|
||||||
|
|
||||||
|
print(r.json())
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
var uri = URI.create("http://localhost:21723/fs/blob");
|
||||||
|
var client = HttpClient.newHttpClient();
|
||||||
|
var request = HttpRequest
|
||||||
|
.newBuilder()
|
||||||
|
.uri(uri)
|
||||||
|
.header("Content-Type", "application/octet-stream")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("Authorization", "Bearer {access-token}")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||||
|
string
|
||||||
|
"""))
|
||||||
|
.build();
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
System.out.println(response.statusCode());
|
||||||
|
System.out.println(response.body());
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
headers := map[string][]string{
|
||||||
|
"Content-Type": []string{"application/octet-stream"},
|
||||||
|
"Accept": []string{"application/json"},
|
||||||
|
"Authorization": []string{"Bearer {access-token}"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data := bytes.NewBuffer([]byte{jsonReq})
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:21723/fs/blob", data)
|
||||||
|
req.Header = headers
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# You can also use wget
|
||||||
|
curl -X POST http://localhost:21723/fs/blob \
|
||||||
|
-H 'Content-Type: application/octet-stream' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||||
|
--data '
|
||||||
|
string
|
||||||
|
'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Write a blob to a remote file
|
||||||
|
|
||||||
|
<a id="opIdfsWrite"></a>
|
||||||
|
|
||||||
|
`POST /fs/write`
|
||||||
|
|
||||||
|
Writes blob data to a file through an active shell session.
|
||||||
|
|
||||||
|
> Body parameter
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304",
|
||||||
|
"path": "/home/user/myfile.txt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3 id="write-a-blob-to-a-remote-file-parameters">Parameters</h3>
|
||||||
|
|
||||||
|
|Name|In|Type|Required|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|body|body|[FsWriteRequest](#schemafswriterequest)|true|none|
|
||||||
|
|
||||||
|
<h3 id="write-a-blob-to-a-remote-file-responses">Responses</h3>
|
||||||
|
|
||||||
|
|Status|Meaning|Description|Schema|
|
||||||
|
|---|---|---|---|
|
||||||
|
|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|The operation was successful. The file was written.|None|
|
||||||
|
|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="warning">
|
||||||
|
To perform this operation, you must be authenticated by means of one of the following methods:
|
||||||
|
bearerAuth
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>Code samples</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const inputBody = '{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304",
|
||||||
|
"path": "/home/user/myfile.txt"
|
||||||
|
}';
|
||||||
|
const headers = {
|
||||||
|
'Content-Type':'application/json',
|
||||||
|
'Authorization':'Bearer {access-token}'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('http://localhost:21723/fs/write',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: inputBody,
|
||||||
|
headers: headers
|
||||||
|
})
|
||||||
|
.then(function(res) {
|
||||||
|
return res.json();
|
||||||
|
}).then(function(body) {
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer {access-token}'
|
||||||
|
}
|
||||||
|
|
||||||
|
data = """
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304",
|
||||||
|
"path": "/home/user/myfile.txt"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
r = requests.post('http://localhost:21723/fs/write', headers = headers, data = data)
|
||||||
|
|
||||||
|
print(r.json())
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
var uri = URI.create("http://localhost:21723/fs/write");
|
||||||
|
var client = HttpClient.newHttpClient();
|
||||||
|
var request = HttpRequest
|
||||||
|
.newBuilder()
|
||||||
|
.uri(uri)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Authorization", "Bearer {access-token}")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString("""
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304",
|
||||||
|
"path": "/home/user/myfile.txt"
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.build();
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
System.out.println(response.statusCode());
|
||||||
|
System.out.println(response.body());
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
headers := map[string][]string{
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
"Authorization": []string{"Bearer {access-token}"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data := bytes.NewBuffer([]byte{jsonReq})
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:21723/fs/write", data)
|
||||||
|
req.Header = headers
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# You can also use wget
|
||||||
|
curl -X POST http://localhost:21723/fs/write \
|
||||||
|
-H 'Content-Type: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||||
|
--data '
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304",
|
||||||
|
"path": "/home/user/myfile.txt"
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Create a shell script file from a blob
|
||||||
|
|
||||||
|
<a id="opIdfsScript"></a>
|
||||||
|
|
||||||
|
`POST /fs/script`
|
||||||
|
|
||||||
|
Creates a shell script in the temporary directory of the file system that is access through the shell connection.
|
||||||
|
|
||||||
|
This can be used to run more complex commands on remote systems.
|
||||||
|
|
||||||
|
> Body parameter
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3 id="create-a-shell-script-file-from-a-blob-parameters">Parameters</h3>
|
||||||
|
|
||||||
|
|Name|In|Type|Required|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|body|body|[FsScriptRequest](#schemafsscriptrequest)|true|none|
|
||||||
|
|
||||||
|
> Example responses
|
||||||
|
|
||||||
|
> The operation was successful. The script file was created.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "/tmp/exec-123.sh"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3 id="create-a-shell-script-file-from-a-blob-responses">Responses</h3>
|
||||||
|
|
||||||
|
|Status|Meaning|Description|Schema|
|
||||||
|
|---|---|---|---|
|
||||||
|
|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|The operation was successful. The script file was created.|[FsScriptResponse](#schemafsscriptresponse)|
|
||||||
|
|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="warning">
|
||||||
|
To perform this operation, you must be authenticated by means of one of the following methods:
|
||||||
|
bearerAuth
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>Code samples</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const inputBody = '{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304"
|
||||||
|
}';
|
||||||
|
const headers = {
|
||||||
|
'Content-Type':'application/json',
|
||||||
|
'Accept':'application/json',
|
||||||
|
'Authorization':'Bearer {access-token}'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('http://localhost:21723/fs/script',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: inputBody,
|
||||||
|
headers: headers
|
||||||
|
})
|
||||||
|
.then(function(res) {
|
||||||
|
return res.json();
|
||||||
|
}).then(function(body) {
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': 'Bearer {access-token}'
|
||||||
|
}
|
||||||
|
|
||||||
|
data = """
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
r = requests.post('http://localhost:21723/fs/script', headers = headers, data = data)
|
||||||
|
|
||||||
|
print(r.json())
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
var uri = URI.create("http://localhost:21723/fs/script");
|
||||||
|
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("""
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304"
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.build();
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
System.out.println(response.statusCode());
|
||||||
|
System.out.println(response.body());
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
headers := map[string][]string{
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
"Accept": []string{"application/json"},
|
||||||
|
"Authorization": []string{"Bearer {access-token}"},
|
||||||
|
}
|
||||||
|
|
||||||
|
data := bytes.NewBuffer([]byte{jsonReq})
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:21723/fs/script", data)
|
||||||
|
req.Header = headers
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# You can also use wget
|
||||||
|
curl -X POST http://localhost:21723/fs/script \
|
||||||
|
-H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \
|
||||||
|
--data '
|
||||||
|
{
|
||||||
|
"connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b",
|
||||||
|
"blob": "854afc45-eadc-49a0-a45d-9fb76a484304"
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
# Schemas
|
# Schemas
|
||||||
|
|
||||||
<h2 id="tocS_ShellStartRequest">ShellStartRequest</h2>
|
<h2 id="tocS_ShellStartRequest">ShellStartRequest</h2>
|
||||||
|
@ -994,6 +1462,92 @@ curl -X POST http://localhost:21723/shell/exec \
|
||||||
|stdout|string|true|none|The stdout output of the command|
|
|stdout|string|true|none|The stdout output of the command|
|
||||||
|stderr|string|true|none|The stderr output of the command|
|
|stderr|string|true|none|The stderr output of the command|
|
||||||
|
|
||||||
|
<h2 id="tocS_FsBlobResponse">FsBlobResponse</h2>
|
||||||
|
|
||||||
|
<a id="schemafsblobresponse"></a>
|
||||||
|
<a id="schema_FsBlobResponse"></a>
|
||||||
|
<a id="tocSfsblobresponse"></a>
|
||||||
|
<a id="tocsfsblobresponse"></a>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"blob": "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3>Properties</h3>
|
||||||
|
|
||||||
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|blob|string|true|none|The data uuid|
|
||||||
|
|
||||||
|
<h2 id="tocS_FsWriteRequest">FsWriteRequest</h2>
|
||||||
|
|
||||||
|
<a id="schemafswriterequest"></a>
|
||||||
|
<a id="schema_FsWriteRequest"></a>
|
||||||
|
<a id="tocSfswriterequest"></a>
|
||||||
|
<a id="tocsfswriterequest"></a>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"connection": "string",
|
||||||
|
"blob": "string",
|
||||||
|
"path": "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3>Properties</h3>
|
||||||
|
|
||||||
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|connection|string|true|none|The connection uuid|
|
||||||
|
|blob|string|true|none|The blob uuid|
|
||||||
|
|path|string|true|none|The target filepath|
|
||||||
|
|
||||||
|
<h2 id="tocS_FsScriptRequest">FsScriptRequest</h2>
|
||||||
|
|
||||||
|
<a id="schemafsscriptrequest"></a>
|
||||||
|
<a id="schema_FsScriptRequest"></a>
|
||||||
|
<a id="tocSfsscriptrequest"></a>
|
||||||
|
<a id="tocsfsscriptrequest"></a>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"connection": "string",
|
||||||
|
"blob": "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3>Properties</h3>
|
||||||
|
|
||||||
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|connection|string|true|none|The connection uuid|
|
||||||
|
|blob|string|true|none|The blob uuid|
|
||||||
|
|
||||||
|
<h2 id="tocS_FsScriptResponse">FsScriptResponse</h2>
|
||||||
|
|
||||||
|
<a id="schemafsscriptresponse"></a>
|
||||||
|
<a id="schema_FsScriptResponse"></a>
|
||||||
|
<a id="tocSfsscriptresponse"></a>
|
||||||
|
<a id="tocsfsscriptresponse"></a>
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<h3>Properties</h3>
|
||||||
|
|
||||||
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|path|string|true|none|The generated script file path|
|
||||||
|
|
||||||
<h2 id="tocS_ConnectionQueryRequest">ConnectionQueryRequest</h2>
|
<h2 id="tocS_ConnectionQueryRequest">ConnectionQueryRequest</h2>
|
||||||
|
|
||||||
<a id="schemaconnectionqueryrequest"></a>
|
<a id="schemaconnectionqueryrequest"></a>
|
||||||
|
@ -1029,11 +1583,11 @@ curl -X POST http://localhost:21723/shell/exec \
|
||||||
{
|
{
|
||||||
"found": [
|
"found": [
|
||||||
{
|
{
|
||||||
"uuid": "string",
|
"connection": "string",
|
||||||
"category": [
|
"category": [
|
||||||
"string"
|
"string"
|
||||||
],
|
],
|
||||||
"connection": [
|
"name": [
|
||||||
"string"
|
"string"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -1048,9 +1602,9 @@ curl -X POST http://localhost:21723/shell/exec \
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|found|[object]|true|none|The found connections|
|
|found|[object]|true|none|The found connections|
|
||||||
|» uuid|string|true|none|The unique id of the connection|
|
|» connection|string|true|none|The unique id of the connection|
|
||||||
|» category|[string]|true|none|The full category path as an array|
|
|» category|[string]|true|none|The full category path as an array|
|
||||||
|» connection|[string]|true|none|The full connection name path as an array|
|
|» name|[string]|true|none|The full connection name path as an array|
|
||||||
|» type|string|true|none|The type identifier of the connection|
|
|» type|string|true|none|The type identifier of the connection|
|
||||||
|
|
||||||
<h2 id="tocS_HandshakeRequest">HandshakeRequest</h2>
|
<h2 id="tocS_HandshakeRequest">HandshakeRequest</h2>
|
||||||
|
@ -1117,10 +1671,6 @@ curl -X POST http://localhost:21723/shell/exec \
|
||||||
|
|
||||||
<h3>Properties</h3>
|
<h3>Properties</h3>
|
||||||
|
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|
||||||
|---|---|---|---|---|
|
|
||||||
|type|string|true|none|none|
|
|
||||||
|
|
||||||
oneOf
|
oneOf
|
||||||
|
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|
@ -1152,18 +1702,10 @@ API key authentication
|
||||||
|
|
||||||
<h3>Properties</h3>
|
<h3>Properties</h3>
|
||||||
|
|
||||||
allOf - discriminator: AuthMethod.type
|
|
||||||
|
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|*anonymous*|[AuthMethod](#schemaauthmethod)|false|none|none|
|
|type|string|true|none|none|
|
||||||
|
|key|string|true|none|The API key|
|
||||||
and
|
|
||||||
|
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|
||||||
|---|---|---|---|---|
|
|
||||||
|*anonymous*|object|false|none|none|
|
|
||||||
|» key|string|true|none|The API key|
|
|
||||||
|
|
||||||
<h2 id="tocS_Local">Local</h2>
|
<h2 id="tocS_Local">Local</h2>
|
||||||
|
|
||||||
|
@ -1175,7 +1717,6 @@ and
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"key": "string",
|
|
||||||
"authFileContent": "string"
|
"authFileContent": "string"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1185,18 +1726,10 @@ Authentication method for local applications. Uses file system access as proof o
|
||||||
|
|
||||||
<h3>Properties</h3>
|
<h3>Properties</h3>
|
||||||
|
|
||||||
allOf - discriminator: AuthMethod.type
|
|
||||||
|
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|Name|Type|Required|Restrictions|Description|
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|*anonymous*|[AuthMethod](#schemaauthmethod)|false|none|none|
|
|type|string|true|none|none|
|
||||||
|
|authFileContent|string|true|none|The contents of the local file $TEMP/xpipe_auth. This file is automatically generated when XPipe starts.|
|
||||||
and
|
|
||||||
|
|
||||||
|Name|Type|Required|Restrictions|Description|
|
|
||||||
|---|---|---|---|---|
|
|
||||||
|*anonymous*|object|false|none|none|
|
|
||||||
|» authFileContent|string|true|none|The contents of the local file $TEMP/xpipe_auth. This file is automatically generated when XPipe starts.|
|
|
||||||
|
|
||||||
<h2 id="tocS_ClientInformation">ClientInformation</h2>
|
<h2 id="tocS_ClientInformation">ClientInformation</h2>
|
||||||
|
|
||||||
|
|
32
beacon/src/main/java/io/xpipe/beacon/api/FsBlobExchange.java
Normal file
32
beacon/src/main/java/io/xpipe/beacon/api/FsBlobExchange.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package io.xpipe.beacon.api;
|
||||||
|
|
||||||
|
import io.xpipe.beacon.BeaconInterface;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class FsBlobExchange extends BeaconInterface<FsBlobExchange.Request> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return "/fs/blob";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
public static class Request {
|
||||||
|
byte @NonNull [] payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
public static class Response {
|
||||||
|
@NonNull
|
||||||
|
UUID blob;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.xpipe.beacon.api;
|
||||||
|
|
||||||
|
import io.xpipe.beacon.BeaconInterface;
|
||||||
|
import io.xpipe.core.store.FilePath;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class FsScriptExchange extends BeaconInterface<FsScriptExchange.Request> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return "/fs/script";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
public static class Request {
|
||||||
|
@NonNull
|
||||||
|
UUID connection;
|
||||||
|
@NonNull
|
||||||
|
UUID blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
public static class Response {
|
||||||
|
@NonNull
|
||||||
|
FilePath path;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package io.xpipe.beacon.api;
|
||||||
|
|
||||||
|
import io.xpipe.beacon.BeaconInterface;
|
||||||
|
import io.xpipe.core.store.FilePath;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class FsWriteExchange extends BeaconInterface<FsWriteExchange.Request> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return "/fs/write";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
public static class Request {
|
||||||
|
@NonNull
|
||||||
|
UUID connection;
|
||||||
|
@NonNull
|
||||||
|
UUID blob;
|
||||||
|
@NonNull
|
||||||
|
FilePath path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Jacksonized
|
||||||
|
@Builder
|
||||||
|
@Value
|
||||||
|
public static class Response {}
|
||||||
|
}
|
|
@ -18,12 +18,8 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
protected final ShellControl shellControl;
|
protected final ShellControl shellControl;
|
||||||
|
|
||||||
@JsonIgnore
|
public ConnectionFileSystem(ShellControl shellControl) {
|
||||||
protected final ShellStore store;
|
|
||||||
|
|
||||||
public ConnectionFileSystem(ShellControl shellControl, ShellStore store) {
|
|
||||||
this.shellControl = shellControl;
|
this.shellControl = shellControl;
|
||||||
this.store = store;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,11 +28,6 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
shellControl.getShellDialect().queryFileSize(shellControl, file).readStdoutOrThrow());
|
shellControl.getShellDialect().queryFileSize(shellControl, file).readStdoutOrThrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileSystemStore getStore() {
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<ShellControl> getShell() {
|
public Optional<ShellControl> getShell() {
|
||||||
return Optional.of(shellControl);
|
return Optional.of(shellControl);
|
||||||
|
|
|
@ -20,8 +20,6 @@ public interface FileSystem extends Closeable, AutoCloseable {
|
||||||
|
|
||||||
long getFileSize(String file) throws Exception;
|
long getFileSize(String file) throws Exception;
|
||||||
|
|
||||||
FileSystemStore getStore();
|
|
||||||
|
|
||||||
Optional<ShellControl> getShell();
|
Optional<ShellControl> getShell();
|
||||||
|
|
||||||
FileSystem open() throws Exception;
|
FileSystem open() throws Exception;
|
||||||
|
|
|
@ -11,7 +11,7 @@ public interface ShellStore extends DataStore, LaunchableStore, FileSystemStore,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default FileSystem createFileSystem() {
|
default FileSystem createFileSystem() {
|
||||||
return new ConnectionFileSystem(control(), this);
|
return new ConnectionFileSystem(control());
|
||||||
}
|
}
|
||||||
|
|
||||||
default ProcessControl prepareLaunchCommand() {
|
default ProcessControl prepareLaunchCommand() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import io.xpipe.core.dialog.HeaderElement;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellDialect;
|
import io.xpipe.core.process.ShellDialect;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
|
import io.xpipe.core.store.FilePath;
|
||||||
import io.xpipe.core.store.LocalStore;
|
import io.xpipe.core.store.LocalStore;
|
||||||
import io.xpipe.core.store.StorePath;
|
import io.xpipe.core.store.StorePath;
|
||||||
|
|
||||||
|
@ -44,6 +45,12 @@ public class CoreJacksonModule extends SimpleModule {
|
||||||
context.registerSubtypes(new NamedType(t.getClass()));
|
context.registerSubtypes(new NamedType(t.getClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addSerializer(FilePath.class, new FilePathSerializer());
|
||||||
|
addDeserializer(FilePath.class, new FilePathDeserializer());
|
||||||
|
|
||||||
|
addSerializer(StorePath.class, new StorePathSerializer());
|
||||||
|
addDeserializer(StorePath.class, new StorePathDeserializer());
|
||||||
|
|
||||||
addSerializer(Charset.class, new CharsetSerializer());
|
addSerializer(Charset.class, new CharsetSerializer());
|
||||||
addDeserializer(Charset.class, new CharsetDeserializer());
|
addDeserializer(Charset.class, new CharsetDeserializer());
|
||||||
|
|
||||||
|
@ -88,6 +95,22 @@ public class CoreJacksonModule extends SimpleModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilePathSerializer extends JsonSerializer<FilePath> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(FilePath value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||||
|
jgen.writeString(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FilePathDeserializer extends JsonDeserializer<FilePath> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilePath deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||||
|
return new FilePath(p.getValueAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class CharsetSerializer extends JsonSerializer<Charset> {
|
public static class CharsetSerializer extends JsonSerializer<Charset> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
149
openapi.yaml
149
openapi.yaml
|
@ -228,6 +228,111 @@ paths:
|
||||||
$ref: '#/components/responses/NotFound'
|
$ref: '#/components/responses/NotFound'
|
||||||
'500':
|
'500':
|
||||||
$ref: '#/components/responses/InternalServerError'
|
$ref: '#/components/responses/InternalServerError'
|
||||||
|
/fs/blob:
|
||||||
|
post:
|
||||||
|
summary: Store a raw blob to be used later
|
||||||
|
description: |
|
||||||
|
Stores arbitrary binary data in a blob such that it can be used later on to for example write to a remote file.
|
||||||
|
|
||||||
|
This will return a uuid which can be used as a reference to the blob.
|
||||||
|
You can also store normal text data in blobs if you intend to create text or shell script files with it.
|
||||||
|
operationId: fsData
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/octet-stream:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The operation was successful. The data was stored.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/FsBlobResponse'
|
||||||
|
examples:
|
||||||
|
success:
|
||||||
|
summary: Success
|
||||||
|
value: { "blob": "854afc45-eadc-49a0-a45d-9fb76a484304" }
|
||||||
|
'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'
|
||||||
|
/fs/write:
|
||||||
|
post:
|
||||||
|
summary: Write a blob to a remote file
|
||||||
|
description: |
|
||||||
|
Writes blob data to a file through an active shell session.
|
||||||
|
operationId: fsWrite
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/FsWriteRequest'
|
||||||
|
examples:
|
||||||
|
simple:
|
||||||
|
summary: Write simple file
|
||||||
|
value: { "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b", "blob": "854afc45-eadc-49a0-a45d-9fb76a484304", "path": "/home/user/myfile.txt" }
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The operation was successful. The file was written.
|
||||||
|
'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'
|
||||||
|
/fs/script:
|
||||||
|
post:
|
||||||
|
summary: Create a shell script file from a blob
|
||||||
|
description: |
|
||||||
|
Creates a shell script in the temporary directory of the file system that is access through the shell connection.
|
||||||
|
|
||||||
|
This can be used to run more complex commands on remote systems.
|
||||||
|
operationId: fsScript
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/FsScriptRequest'
|
||||||
|
examples:
|
||||||
|
standard:
|
||||||
|
summary: Standard write
|
||||||
|
value: { "connection": "f0ec68aa-63f5-405c-b178-9a4454556d6b", "blob": "854afc45-eadc-49a0-a45d-9fb76a484304" }
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The operation was successful. The script file was created.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/FsScriptResponse'
|
||||||
|
examples:
|
||||||
|
success:
|
||||||
|
summary: Success
|
||||||
|
value: { "path": "/tmp/exec-123.sh" }
|
||||||
|
'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'
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
ShellStartRequest:
|
ShellStartRequest:
|
||||||
|
@ -274,6 +379,50 @@ components:
|
||||||
- exitCode
|
- exitCode
|
||||||
- stdout
|
- stdout
|
||||||
- stderr
|
- stderr
|
||||||
|
FsBlobResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
blob:
|
||||||
|
type: string
|
||||||
|
description: The data uuid
|
||||||
|
required:
|
||||||
|
- blob
|
||||||
|
FsWriteRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
connection:
|
||||||
|
type: string
|
||||||
|
description: The connection uuid
|
||||||
|
blob:
|
||||||
|
type: string
|
||||||
|
description: The blob uuid
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
description: The target filepath
|
||||||
|
required:
|
||||||
|
- connection
|
||||||
|
- blob
|
||||||
|
- path
|
||||||
|
FsScriptRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
connection:
|
||||||
|
type: string
|
||||||
|
description: The connection uuid
|
||||||
|
blob:
|
||||||
|
type: string
|
||||||
|
description: The blob uuid
|
||||||
|
required:
|
||||||
|
- connection
|
||||||
|
- blob
|
||||||
|
FsScriptResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
description: The generated script file path
|
||||||
|
required:
|
||||||
|
- path
|
||||||
ConnectionQueryRequest:
|
ConnectionQueryRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
||||||
10.0-7
|
10.0-9
|
||||||
|
|
Loading…
Reference in a new issue