Various fixes

This commit is contained in:
crschnick 2024-07-05 15:31:52 +00:00
parent bbe3f09837
commit f5edcb69d3
23 changed files with 353 additions and 20 deletions

View file

@ -110,6 +110,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
.replace("$RequestBuilder", "")
.replace("Exchange$Request","Request")
.replace("at [Source: UNKNOWN; byte offset: #UNKNOWN]", "")
.replaceAll("(\\w+) is marked non-null but is null", "field $1 is missing from object")
.trim();
writeError(exchange, new BeaconClientErrorResponse(message), 400);
}

View file

@ -37,6 +37,7 @@ public class ConnectionInfoExchangeImpl extends ConnectionInfoExchange {
.usageCategory(e.getProvider().getUsageCategory())
.type(e.getProvider().getId())
.state(e.getStorePersistentState() != null ? e.getStorePersistentState() : new Object())
.cache(e.getStoreCache())
.build();
list.add(apply);
}

View file

@ -0,0 +1,26 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionRemoveExchange;
import java.util.ArrayList;
import java.util.UUID;
public class ConnectionRemoveExchangeImpl extends ConnectionRemoveExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var entries = new ArrayList<DataStoreEntry>();
for (UUID uuid : msg.getConnections()) {
var e = DataStorage.get()
.getStoreEntryIfPresent(uuid)
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + uuid));
entries.add(e);
}
DataStorage.get().deleteWithChildren(entries.toArray(DataStoreEntry[]::new));
return Response.builder().build();
}
}

View file

@ -30,8 +30,9 @@ public final class BrowserBookmarkHeaderComp extends SimpleComp {
StoreViewState.get().getAllConnectionsCategory(),
StoreViewState.get().getActiveCategory(),
this.category)
.styleClass(Styles.LEFT_PILL);
var filter = new FilterComp(this.filter).styleClass(Styles.RIGHT_PILL).hgrow();
.styleClass(Styles.LEFT_PILL)
.minWidth(Region.USE_PREF_SIZE);
var filter = new FilterComp(this.filter).styleClass(Styles.RIGHT_PILL).minWidth(0).hgrow();
var top = new HorizontalComp(List.of(category, filter))
.apply(struc -> struc.get().setFillHeight(true))

View file

@ -45,14 +45,14 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
var selectedBorder = Bindings.createObjectBinding(
() -> {
var c = Platform.getPreferences().getAccentColor().desaturate();
return new Background(new BackgroundFill(c,new CornerRadii(8), new Insets(5, 1, 5, 2)));
return new Background(new BackgroundFill(c,new CornerRadii(8), new Insets(10, 1, 10, 2)));
},
Platform.getPreferences().accentColorProperty());
var hoverBorder = Bindings.createObjectBinding(
() -> {
var c = Platform.getPreferences().getAccentColor().darker().desaturate();
return new Background(new BackgroundFill(c,new CornerRadii(8), new Insets(5, 1, 5, 2)));
return new Background(new BackgroundFill(c,new CornerRadii(8), new Insets(10, 1, 10, 2)));
},
Platform.getPreferences().accentColorProperty());

View file

@ -204,6 +204,11 @@ public class AppTheme {
Application.setUserAgentStylesheet(Styles.toDataURI(builder.toString()));
}
public List<String> getAdditionalStylesheets() {
return List.of();
}
@Override
public ObservableValue<String> toTranslatedString() {
return new SimpleStringProperty(name);

View file

@ -135,6 +135,7 @@ open module io.xpipe.app {
ShellExecExchangeImpl,
ConnectionQueryExchangeImpl,
ConnectionInfoExchangeImpl,
ConnectionRemoveExchangeImpl,
ConnectionAddExchangeImpl,
ConnectionBrowseExchangeImpl,
ConnectionTerminalExchangeImpl,

View file

@ -623,6 +623,7 @@ curl -X POST http://localhost:21721/connection/info \
`POST /connection/add`
Creates the new connection in the xpipe vault from raw json data.
This can also perform an optional validation first to make sure that the connection can be established.
If an equivalent connection already exists, no new one will be added.
@ -631,6 +632,7 @@ If an equivalent connection already exists, no new one will be added.
```json
{
"name": "my connection",
"validate": true,
"category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40",
"data": {
"type": "shellEnvironment",
@ -690,6 +692,7 @@ bearerAuth
```javascript
const inputBody = '{
"name": "my connection",
"validate": true,
"category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40",
"data": {
"type": "shellEnvironment",
@ -732,6 +735,7 @@ headers = {
data = """
{
"name": "my connection",
"validate": true,
"category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40",
"data": {
"type": "shellEnvironment",
@ -762,6 +766,7 @@ var request = HttpRequest
.POST(HttpRequest.BodyPublishers.ofString("""
{
"name": "my connection",
"validate": true,
"category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40",
"data": {
"type": "shellEnvironment",
@ -815,6 +820,7 @@ curl -X POST http://localhost:21721/connection/add \
--data '
{
"name": "my connection",
"validate": true,
"category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40",
"data": {
"type": "shellEnvironment",
@ -832,6 +838,174 @@ curl -X POST http://localhost:21721/connection/add \
</details>
## Remove connection
<a id="opIdconnectionRemove"></a>
`POST /connection/remove`
Removes a set of connection. This includes any possible children associated with the connection.
Some connections, for example the local machine, can not be removed.
> Body parameter
```json
{
"connections": [
"36ad9716-a209-4f7f-9814-078d3349280c"
]
}
```
<h3 id="remove-connection-parameters">Parameters</h3>
|Name|In|Type|Required|Description|
|---|---|---|---|---|
|body|body|[ConnectionRemoveRequest](#schemaconnectionremoverequest)|true|none|
> Example responses
> 400 Response
```json
{
"message": "string"
}
```
<h3 id="remove-connection-responses">Responses</h3>
|Status|Meaning|Description|Schema|
|---|---|---|---|
|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|The removal was successful.|None|
|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request. Please check error message and your parameters.|[ClientErrorResponse](#schemaclienterrorresponse)|
|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|
|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal error.|[ServerErrorResponse](#schemaservererrorresponse)|
<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 = '{
"connections": [
"36ad9716-a209-4f7f-9814-078d3349280c"
]
}';
const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'Authorization':'Bearer {access-token}'
};
fetch('http://localhost:21721/connection/remove',
{
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 = """
{
"connections": [
"36ad9716-a209-4f7f-9814-078d3349280c"
]
}
"""
r = requests.post('http://localhost:21721/connection/remove', headers = headers, data = data)
print(r.json())
```
```java
var uri = URI.create("http://localhost:21721/connection/remove");
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("""
{
"connections": [
"36ad9716-a209-4f7f-9814-078d3349280c"
]
}
"""))
.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:21721/connection/remove", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
```
```shell
# You can also use wget
curl -X POST http://localhost:21721/connection/remove \
-H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' \
--data '
{
"connections": [
"36ad9716-a209-4f7f-9814-078d3349280c"
]
}
'
```
</details>
## Open connection in file browser
<a id="opIdconnectionBrowse"></a>
@ -1307,7 +1481,7 @@ curl -X POST http://localhost:21721/connection/toggle \
</details>
## Refreshes state of a connection
## Refresh state of a connection
<a id="opIdconnectionRefresh"></a>
@ -1325,7 +1499,7 @@ This will update the connection state information and also any children if the c
}
```
<h3 id="refreshes-state-of-a-connection-parameters">Parameters</h3>
<h3 id="refresh-state-of-a-connection-parameters">Parameters</h3>
|Name|In|Type|Required|Description|
|---|---|---|---|---|
@ -1341,7 +1515,7 @@ This will update the connection state information and also any children if the c
}
```
<h3 id="refreshes-state-of-a-connection-responses">Responses</h3>
<h3 id="refresh-state-of-a-connection-responses">Responses</h3>
|Status|Meaning|Description|Schema|
|---|---|---|---|
@ -2644,7 +2818,8 @@ Retrieves version information from the daemon
"version": "string",
"canonicalVersion": "string",
"buildVersion": "string",
"jvmVersion": "string"
"jvmVersion": "string",
"pro": true
}
```
@ -3072,7 +3247,8 @@ undefined
"usageCategory": "shell",
"lastModified": "string",
"lastUsed": "string",
"state": {}
"state": {},
"cache": {}
}
]
@ -3091,6 +3267,7 @@ undefined
|lastModified|string|true|none|The timestamp of when the connection configuration was last modified in ISO 8601|
|lastUsed|string|true|none|The timestamp of when the connection was last launched in ISO 8601|
|state|object|true|none|The internal persistent state information about the connection|
|cache|object|true|none|The temporary cache data for the connection|
#### Enumerated Values
@ -3134,7 +3311,8 @@ undefined
```json
{
"name": "string",
"data": {}
"data": {},
"validate": true
}
```
@ -3145,6 +3323,7 @@ undefined
|---|---|---|---|---|
|name|string|true|none|The connection name|
|data|object|true|none|The raw connection store data. Schemas for connection types are not documented but you can find the connection data of your existing connections in the xpipe vault.|
|validate|boolean|true|none|Whether to perform a connection validation before adding it, i.e., probe the connection first. If validation is enabled and fails, the connection will not be added|
<h2 id="tocS_ConnectionAddResponse">ConnectionAddResponse</h2>
@ -3166,6 +3345,28 @@ undefined
|---|---|---|---|---|
|connection|string|true|none|The connection uuid|
<h2 id="tocS_ConnectionRemoveRequest">ConnectionRemoveRequest</h2>
<a id="schemaconnectionremoverequest"></a>
<a id="schema_ConnectionRemoveRequest"></a>
<a id="tocSconnectionremoverequest"></a>
<a id="tocsconnectionremoverequest"></a>
```json
{
"connections": [
"string"
]
}
```
<h3>Properties</h3>
|Name|Type|Required|Restrictions|Description|
|---|---|---|---|---|
|connections|[string]|true|none|The connections to remove|
<h2 id="tocS_ConnectionBrowseRequest">ConnectionBrowseRequest</h2>
<a id="schemaconnectionbrowserequest"></a>
@ -3291,7 +3492,8 @@ undefined
"version": "string",
"canonicalVersion": "string",
"buildVersion": "string",
"jvmVersion": "string"
"jvmVersion": "string",
"pro": true
}
```
@ -3304,6 +3506,7 @@ undefined
|canonicalVersion|string|true|none|The canonical version of the running daemon|
|buildVersion|string|true|none|The build timestamp|
|jvmVersion|string|true|none|The version of the Java Virtual Machine in which the daemon is running|
|pro|boolean|true|none|Whether the daemon supports professional edition features|
<h2 id="tocS_AuthMethod">AuthMethod</h2>

View file

@ -52,8 +52,9 @@
.browser .welcome {
-fx-border-color: -color-bg-inset, -color-border-default;
-fx-border-width: 2.65em 0 0 0, 0.05em 0 0 0;
-fx-border-width: 2.65em 10 0 0, 0.05em 0 0 0;
-fx-border-insets: 0, 2.65em 0 0 0;
-fx-border-radius: 0 10 0 0, 0;
}
.root:seamless-frame .browser .welcome {

View file

@ -65,3 +65,8 @@
.prefs .theme-switcher .combo-box-popup .list-view {
-fx-effect: NONE;
}
.root:seamless-frame .prefs .scroll-bar:vertical {
-fx-padding: 9 1 5 1;
}

View file

@ -1,7 +1,7 @@
.scroll-bar:vertical {
-fx-min-width: 0.4em;
-fx-pref-width: 0.4em;
-fx-padding: 0.1em 1 0.1em 1;
-fx-min-width: 6px;
-fx-pref-width: 6px;
-fx-padding: 1;
-fx-background-color: transparent;
}

View file

@ -8,7 +8,7 @@
}
.store-list-comp.scroll-pane .scroll-bar:vertical {
-fx-padding: 0.6em 1 0.3em 1;
-fx-padding: 9 1 5 1;
}
/* Grid */

View file

@ -1 +1 @@
.root { -color-bg-default-transparent: #0d1117d2; }
.root { -color-bg-default-transparent: #1C1C1ED2; }

View file

@ -0,0 +1 @@
.root { -color-bg-default-transparent: #FFFFFFCC; }

View file

@ -0,0 +1 @@
.root { -color-bg-default-transparent: #282a36D2; }

View file

@ -1 +1 @@
.root { -color-bg-default-transparent: #FFFFFF99; }
.root { -color-bg-default-transparent: #FFFFFFCC; }

View file

@ -56,6 +56,7 @@
-color-fg-subtle: rgb(166, 173, 200);
-color-fg-emphasis: rgb(180, 190, 254);
-color-bg-default: rgb(30, 30, 46);
-color-bg-default-transparent: #1E1E2ED2;
-color-bg-overlay: rgb(24, 24, 37);
-color-bg-subtle: rgb(49, 50, 68);
-color-bg-inset: rgb(17, 17, 27);

View file

@ -1 +1 @@
.root { -color-bg-default-transparent: #0d1117d2; }
.root { -color-bg-default-transparent: #2E3440d2; }

View file

@ -0,0 +1 @@
.root { -color-bg-default-transparent: #fafafcCC; }

View file

@ -10,6 +10,7 @@ import lombok.extern.jackson.Jacksonized;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class ConnectionInfoExchange extends BeaconInterface<ConnectionInfoExchange.Request> {
@ -65,5 +66,8 @@ public class ConnectionInfoExchange extends BeaconInterface<ConnectionInfoExchan
@NonNull
Object state;
@NonNull
Map<String, Object> cache;
}
}

View file

@ -0,0 +1,31 @@
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.List;
import java.util.UUID;
public class ConnectionRemoveExchange extends BeaconInterface<ConnectionRemoveExchange.Request> {
@Override
public String getPath() {
return "/connection/remove";
}
@Jacksonized
@Builder
@Value
public static class Request {
@NonNull
List<UUID> connections;
}
@Jacksonized
@Builder
@Value
public static class Response {}
}

View file

@ -38,6 +38,7 @@ open module io.xpipe.beacon {
HandshakeExchange,
ConnectionQueryExchange,
ConnectionInfoExchange,
ConnectionRemoveExchange,
ConnectionAddExchange,
ConnectionBrowseExchange,
ConnectionTerminalExchange,

View file

@ -158,6 +158,7 @@ paths:
summary: Add new connection
description: |
Creates the new connection in the xpipe vault from raw json data.
This can also perform an optional validation first to make sure that the connection can be established.
If an equivalent connection already exists, no new one will be added.
operationId: connectionAdd
@ -170,7 +171,7 @@ paths:
examples:
simple:
summary: Add new pwsh shell environment
value: { "name": "my connection", "category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40", "data":
value: { "name": "my connection", "validate": true, "category": "97458c07-75c0-4f9d-a06e-92d8cdf67c40", "data":
{
"type": "shellEnvironment",
"commands": null,
@ -200,6 +201,35 @@ paths:
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
/connection/remove:
post:
summary: Remove connection
description: |
Removes a set of connection. This includes any possible children associated with the connection.
Some connections, for example the local machine, can not be removed.
operationId: connectionRemove
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionRemoveRequest'
examples:
simple:
summary: Remove single connection
value: { "connections": [ "36ad9716-a209-4f7f-9814-078d3349280c" ] }
responses:
'200':
description: The removal was successful.
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
/connection/browse:
post:
summary: Open connection in file browser
@ -779,6 +809,9 @@ components:
state:
type: object
description: The internal persistent state information about the connection
cache:
type: object
description: The temporary cache data for the connection
required:
- connection
- category
@ -789,6 +822,7 @@ components:
- lastUsed
- lastModified
- state
- cache
ConnectionRefreshRequest:
type: object
properties:
@ -806,9 +840,13 @@ components:
data:
type: object
description: The raw connection store data. Schemas for connection types are not documented but you can find the connection data of your existing connections in the xpipe vault.
validate:
type: boolean
description: Whether to perform a connection validation before adding it, i.e., probe the connection first. If validation is enabled and fails, the connection will not be added
required:
- name
- data
- validate
ConnectionAddResponse:
type: object
properties:
@ -817,6 +855,17 @@ components:
description: The connection uuid
required:
- connection
ConnectionRemoveRequest:
type: object
properties:
connections:
type: array
description: The connections to remove
items:
type: string
description: The unique id of the connection
required:
- connections
ConnectionBrowseRequest:
type: object
properties: