mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-03-01 03:51:59 +01:00
feat: first pass at writing connections to hujson
This replaces the current connection writing system with one that can write to a hujson file non-destructively. This not only allows us to write to hujson but also to preserve comments in the process.
This commit is contained in:
parent
cca2f0ed7b
commit
6ecc5412a6
@ -5,6 +5,7 @@ import { WindowService } from "@/app/store/services";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { Notification } from "electron";
|
||||
import { getResolvedUpdateChannel } from "emain/updater";
|
||||
import { createPatch } from "rfc6902";
|
||||
import { RpcResponseHelper, WshClient } from "../frontend/app/store/wshclient";
|
||||
import { getWebContentsByBlockId, webGetSelector } from "./emain-web";
|
||||
import { createBrowserWindow, getWaveWindowById, getWaveWindowByWorkspaceId } from "./emain-window";
|
||||
@ -66,6 +67,11 @@ export class ElectronWshClientType extends WshClient {
|
||||
// workspaceMenu.submenu = Menu.buildFromTemplate(updatedWorkspaceMenu);
|
||||
// });
|
||||
// }
|
||||
|
||||
async handle_createjsonpatch(rh: RpcResponseHelper, jsonCompare: JsonCompare): Promise<string> {
|
||||
const patch = createPatch(jsonCompare.original, jsonCompare.modified);
|
||||
return JSON.stringify(patch);
|
||||
}
|
||||
}
|
||||
|
||||
export let ElectronWshClient: ElectronWshClientType;
|
||||
|
@ -92,6 +92,11 @@ class RpcApiType {
|
||||
return client.wshRpcCall("createblock", data, opts);
|
||||
}
|
||||
|
||||
// command "createjsonpatch" [call]
|
||||
CreateJsonPatchCommand(client: WshClient, data: JsonCompare, opts?: RpcOpts): Promise<string> {
|
||||
return client.wshRpcCall("createjsonpatch", data, opts);
|
||||
}
|
||||
|
||||
// command "createsubblock" [call]
|
||||
CreateSubBlockCommand(client: WshClient, data: CommandCreateSubBlockData, opts?: RpcOpts): Promise<ORef> {
|
||||
return client.wshRpcCall("createsubblock", data, opts);
|
||||
|
6
frontend/types/gotypes.d.ts
vendored
6
frontend/types/gotypes.d.ts
vendored
@ -473,6 +473,12 @@ declare global {
|
||||
configerrors: ConfigError[];
|
||||
};
|
||||
|
||||
// wshrpc.JsonCompare
|
||||
type JsonCompare = {
|
||||
original: MetaType;
|
||||
modified: MetaType;
|
||||
};
|
||||
|
||||
// waveobj.LayoutActionData
|
||||
type LayoutActionData = {
|
||||
actiontype: string;
|
||||
|
@ -147,6 +147,7 @@
|
||||
"remark-flexible-toc": "^1.1.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-github-blockquote-alert": "^1.3.0",
|
||||
"rfc6902": "^5.1.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"sharp": "^0.33.5",
|
||||
"shell-quote": "^1.8.2",
|
||||
|
@ -33,6 +33,7 @@ import (
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wps"
|
||||
"github.com/wavetermdev/waveterm/pkg/wsaveconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
||||
"golang.org/x/crypto/ssh"
|
||||
@ -450,7 +451,7 @@ func (conn *SSHConn) getPermissionToInstallWsh(ctx context.Context, clientDispla
|
||||
meta := make(map[string]any)
|
||||
meta["conn:wshenabled"] = response.Confirm
|
||||
conn.Infof(ctx, "writing conn:wshenabled=%v to connections.json\n", response.Confirm)
|
||||
err = wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
err = wsaveconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
log.Printf("warning: error writing to connections file: %v", err)
|
||||
}
|
||||
@ -608,7 +609,7 @@ func (conn *SSHConn) Connect(ctx context.Context, connFlags *wconfig.ConnKeyword
|
||||
}
|
||||
meta["ssh:identityfile"] = identityFiles
|
||||
}
|
||||
err = wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
err = wsaveconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
// i do not consider this a critical failure
|
||||
log.Printf("config write error: unable to save connection %s: %v", conn.GetName(), err)
|
||||
@ -733,7 +734,7 @@ func (conn *SSHConn) persistWshInstalled(ctx context.Context, result WshCheckRes
|
||||
}
|
||||
meta := make(map[string]any)
|
||||
meta["conn:wshenabled"] = result.WshEnabled
|
||||
err := wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
err := wsaveconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
conn.Infof(ctx, "WARN could not write conn:wshenabled=%v to connections.json: %v\n", result.WshEnabled, err)
|
||||
log.Printf("warning: error writing to connections file: %v", err)
|
||||
|
107
pkg/wsaveconfig/wsaveconfig.go
Normal file
107
pkg/wsaveconfig/wsaveconfig.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright 2025, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package wsaveconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/tailscale/hujson"
|
||||
"github.com/wavetermdev/waveterm/pkg/wavebase"
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
||||
)
|
||||
|
||||
func readConfigFileFSRaw(fsys fs.FS, fileName string) ([]byte, error) {
|
||||
barr, readErr := fs.ReadFile(fsys, fileName)
|
||||
if readErr != nil {
|
||||
// If we get an error, we may be using the wrong path separator for the given FS interface. Try switching the separator.
|
||||
barr, readErr = fs.ReadFile(fsys, filepath.ToSlash(fileName))
|
||||
}
|
||||
return barr, readErr
|
||||
}
|
||||
|
||||
func SetConnectionsConfigValue(connName string, toMerge waveobj.MetaMapType) error {
|
||||
originalM, cerrs := wconfig.ReadWaveHomeConfigFile(wconfig.ConnectionsFile)
|
||||
if len(cerrs) > 0 {
|
||||
return fmt.Errorf("error reading config file: %v", cerrs[0])
|
||||
}
|
||||
mergedM, err := createMergedConnections(connName, toMerge)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error merging config: %w", err)
|
||||
}
|
||||
jsonCompare := wshrpc.JsonCompare{
|
||||
Original: originalM,
|
||||
Modified: mergedM,
|
||||
}
|
||||
rpcClient := wshclient.GetBareRpcClient()
|
||||
patchStr, err := wshclient.CreateJsonPatchCommand(rpcClient, jsonCompare, &wshrpc.RpcOpts{
|
||||
Route: wshutil.ElectronRoute,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating json patch: %w", err)
|
||||
}
|
||||
patchBarr := []byte(patchStr)
|
||||
|
||||
mTarget, err := jsonMarshallHujson(wconfig.ConnectionsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse hujson: %w", err)
|
||||
}
|
||||
err = mTarget.Patch(patchBarr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply patch: %w", err)
|
||||
}
|
||||
outBarr := mTarget.Pack()
|
||||
outBarr, err = hujson.Format(outBarr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to format: %w", err)
|
||||
}
|
||||
return writeWaveHomeConfigFile(wconfig.ConnectionsFile, outBarr)
|
||||
}
|
||||
|
||||
func writeWaveHomeConfigFile(fileName string, barr []byte) error {
|
||||
configDirAbsPath := wavebase.GetWaveConfigDir()
|
||||
fullFileName := filepath.Join(configDirAbsPath, fileName)
|
||||
return os.WriteFile(fullFileName, barr, 0644)
|
||||
}
|
||||
|
||||
func createMergedConnections(connName string, toMerge waveobj.MetaMapType) (waveobj.MetaMapType, error) {
|
||||
m, cerrs := wconfig.ReadWaveHomeConfigFile(wconfig.ConnectionsFile)
|
||||
if len(cerrs) > 0 {
|
||||
return nil, fmt.Errorf("error reading config file: %v", cerrs[0])
|
||||
}
|
||||
if m == nil {
|
||||
m = make(waveobj.MetaMapType)
|
||||
}
|
||||
connData := m.GetMap(connName)
|
||||
if connData == nil {
|
||||
connData = make(waveobj.MetaMapType)
|
||||
}
|
||||
for configKey, val := range toMerge {
|
||||
if val == nil {
|
||||
delete(connData, configKey)
|
||||
continue
|
||||
}
|
||||
connData[configKey] = val
|
||||
}
|
||||
m[connName] = connData
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func jsonMarshallHujson(filename string) (*hujson.Value, error) {
|
||||
configDirAbsPath := wavebase.GetWaveConfigDir()
|
||||
configDirFsys := os.DirFS(configDirAbsPath)
|
||||
barr, err := readConfigFileFSRaw(configDirFsys, filename)
|
||||
if err != nil {
|
||||
// TODO just create one from scratch instead (still can fail if the newly created cannot be opened)
|
||||
return nil, fmt.Errorf("unable to read existing config file: %w", err)
|
||||
}
|
||||
outConfig, err := hujson.Parse(barr)
|
||||
return &outConfig, err
|
||||
}
|
@ -118,6 +118,12 @@ func CreateBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandCreateBlockData, o
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// command "createjsonpatch", wshserver.CreateJsonPatchCommand
|
||||
func CreateJsonPatchCommand(w *wshutil.WshRpc, data wshrpc.JsonCompare, opts *wshrpc.RpcOpts) (string, error) {
|
||||
resp, err := sendRpcRequestCallHelper[string](w, "createjsonpatch", data, opts)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// command "createsubblock", wshserver.CreateSubBlockCommand
|
||||
func CreateSubBlockCommand(w *wshutil.WshRpc, data wshrpc.CommandCreateSubBlockData, opts *wshrpc.RpcOpts) (waveobj.ORef, error) {
|
||||
resp, err := sendRpcRequestCallHelper[waveobj.ORef](w, "createsubblock", data, opts)
|
||||
|
@ -124,6 +124,7 @@ const (
|
||||
Command_Notify = "notify"
|
||||
Command_FocusWindow = "focuswindow"
|
||||
Command_GetUpdateChannel = "getupdatechannel"
|
||||
Command_CreateJsonPatch = "createjsonpatch"
|
||||
|
||||
Command_VDomCreateContext = "vdomcreatecontext"
|
||||
Command_VDomAsyncInitiation = "vdomasyncinitiation"
|
||||
@ -234,6 +235,7 @@ type WshRpcInterface interface {
|
||||
|
||||
WorkspaceListCommand(ctx context.Context) ([]WorkspaceInfoData, error)
|
||||
GetUpdateChannelCommand(ctx context.Context) (string, error)
|
||||
CreateJsonPatchCommand(ctx context.Context, jsonCompare JsonCompare) (string, error)
|
||||
|
||||
// terminal
|
||||
VDomCreateContextCommand(ctx context.Context, data vdom.VDomCreateContext) (*waveobj.ORef, error)
|
||||
@ -763,3 +765,8 @@ type SuggestionType struct {
|
||||
FileName string `json:"file:name,omitempty"`
|
||||
UrlUrl string `json:"url:url,omitempty"`
|
||||
}
|
||||
|
||||
type JsonCompare struct {
|
||||
Original waveobj.MetaMapType `json:"original"`
|
||||
Modified waveobj.MetaMapType `json:"modified"`
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import (
|
||||
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wcore"
|
||||
"github.com/wavetermdev/waveterm/pkg/wps"
|
||||
"github.com/wavetermdev/waveterm/pkg/wsaveconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
||||
"github.com/wavetermdev/waveterm/pkg/wsl"
|
||||
@ -516,7 +517,7 @@ func (ws *WshServer) SetConfigCommand(ctx context.Context, data wshrpc.MetaSetti
|
||||
|
||||
func (ws *WshServer) SetConnectionsConfigCommand(ctx context.Context, data wshrpc.ConnConfigRequest) error {
|
||||
log.Printf("SET CONNECTIONS CONFIG: %v\n", data)
|
||||
return wconfig.SetConnectionsConfigValue(data.Host, data.MetaMapType)
|
||||
return wsaveconfig.SetConnectionsConfigValue(data.Host, data.MetaMapType)
|
||||
}
|
||||
|
||||
func (ws *WshServer) GetFullConfigCommand(ctx context.Context) (wconfig.FullConfigType, error) {
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wps"
|
||||
"github.com/wavetermdev/waveterm/pkg/wsaveconfig"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
||||
"github.com/wavetermdev/waveterm/pkg/wsl"
|
||||
@ -421,7 +422,7 @@ func (conn *WslConn) getPermissionToInstallWsh(ctx context.Context, clientDispla
|
||||
meta := make(map[string]any)
|
||||
meta["conn:wshenabled"] = response.Confirm
|
||||
conn.Infof(ctx, "writing conn:wshenabled=%v to connections.json\n", response.Confirm)
|
||||
err = wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
err = wsaveconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
log.Printf("warning: error writing to connections file: %v", err)
|
||||
}
|
||||
@ -680,7 +681,7 @@ func (conn *WslConn) persistWshInstalled(ctx context.Context, result WshCheckRes
|
||||
}
|
||||
meta := make(map[string]any)
|
||||
meta["conn:wshenabled"] = result.WshEnabled
|
||||
err := wconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
err := wsaveconfig.SetConnectionsConfigValue(conn.GetName(), meta)
|
||||
if err != nil {
|
||||
conn.Infof(ctx, "WARN could not write conn:wshenabled=%v to connections.json: %v\n", result.WshEnabled, err)
|
||||
log.Printf("warning: error writing to connections file: %v", err)
|
||||
|
@ -18774,6 +18774,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rfc6902@npm:^5.1.2":
|
||||
version: 5.1.2
|
||||
resolution: "rfc6902@npm:5.1.2"
|
||||
checksum: 10c0/29d944ea416230e9589476a646e68a8684437f6ea2883b6447abd3a1a741f12bbedd7a15e300055b35a938ff3944116e5f3ae95a30b3d2f9f0c980957566b364
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rimraf@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "rimraf@npm:3.0.2"
|
||||
@ -21823,6 +21830,7 @@ __metadata:
|
||||
remark-flexible-toc: "npm:^1.1.1"
|
||||
remark-gfm: "npm:^4.0.1"
|
||||
remark-github-blockquote-alert: "npm:^1.3.0"
|
||||
rfc6902: "npm:^5.1.2"
|
||||
rollup-plugin-flow: "npm:^1.1.1"
|
||||
rxjs: "npm:^7.8.1"
|
||||
sass: "npm:^1.84.0"
|
||||
|
Loading…
Reference in New Issue
Block a user