diff --git a/src/models/model.ts b/src/models/model.ts index 75f7a830..a548d6c2 100644 --- a/src/models/model.ts +++ b/src/models/model.ts @@ -36,6 +36,7 @@ import { Cmd } from "./cmd"; import { GlobalCommandRunner } from "./global"; import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure"; import type { TermWrap } from "@/plugins/terminal/term"; +import * as util from "@/util/util"; type SWLinePtr = { line: LineType; @@ -143,6 +144,8 @@ class Model { }); this.ws.reconnect(); this.keybindManager = new KeybindManager(); + this.readConfigKeybindings(); + this.initSystemKeybindings(); this.inputModel = new InputModel(this); this.pluginsModel = new PluginsModel(this); this.bookmarksModel = new BookmarksModel(this); @@ -171,13 +174,6 @@ class Model { } return fontSize; }); - this.keybindManager.registerKeybinding("system", "electron", "any", (waveEvent) => { - if (this.keybindManager.checkKeyPressed(waveEvent, "system:toggleDeveloperTools")) { - getApi().toggleDeveloperTools(); - return true; - } - return false; - }); getApi().onTCmd(this.onTCmd.bind(this)); getApi().onICmd(this.onICmd.bind(this)); getApi().onLCmd(this.onLCmd.bind(this)); @@ -208,6 +204,31 @@ class Model { }; } + readConfigKeybindings() { + const url = new URL(this.getBaseHostPort() + "/config/keybindings.json"); + let prtn = fetch(url, { method: "get", body: null, headers: this.getFetchHeaders() }); + prtn.then((resp) => { + if (resp.status == 404) { + return []; + } else if (!resp.ok) { + util.handleNotOkResp(resp, url); + } + return resp.json(); + }).then((userKeybindings) => { + this.keybindManager.setUserKeybindings(userKeybindings); + }); + } + + initSystemKeybindings() { + this.keybindManager.registerKeybinding("system", "electron", "any", (waveEvent) => { + if (this.keybindManager.checkKeyPressed(waveEvent, "system:toggleDeveloperTools")) { + getApi().toggleDeveloperTools(); + return true; + } + return false; + }); + } + static getInstance(): Model { if (!(window as any).GlobalModel) { (window as any).GlobalModel = new Model(); @@ -1298,7 +1319,7 @@ class Model { }; /** console.log( - "CMD", + "CMD" pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""), pk.args, pk.kwargs, @@ -1526,7 +1547,7 @@ class Model { .then((resp) => { if (!resp.ok) { badResponseStr = sprintf( - "Bad fetch response for /api/read-file: %d %s", + "Bad fetch response for /apiread-file: %d %s", resp.status, resp.statusText ); diff --git a/src/util/keyutil.ts b/src/util/keyutil.ts index dac4b9d6..3e030936 100644 --- a/src/util/keyutil.ts +++ b/src/util/keyutil.ts @@ -1,8 +1,10 @@ import * as React from "react"; +import * as mobx from "mobx"; import * as electron from "electron"; import { parse } from "node:path"; import { v4 as uuidv4 } from "uuid"; -import keybindings from "../../assets/keybindings.json"; +import defaultKeybindingsFile from "../../assets/keybindings.json"; +const defaultKeybindings: KeybindConfig = defaultKeybindingsFile; type KeyPressDecl = { mods: { @@ -22,6 +24,7 @@ const KeyTypeKey = "key"; const KeyTypeCode = "code"; type KeybindCallback = (event: WaveKeyboardEvent) => boolean; +type KeybindConfig = Array<{ command: string; keys: Array }>; type Keybind = { domain: string; @@ -36,6 +39,66 @@ class KeybindManager { levelMap: Map>; levelArray: Array; keyDescriptionsMap: Map>; + userKeybindings: KeybindConfig; + userKeybindingError: OV; + + constructor() { + this.levelMap = new Map(); + this.domainCallbacks = new Map(); + this.levelArray = KeybindLevels; + for (let index = 0; index < this.levelArray.length; index++) { + let curLevel = this.levelArray[index]; + this.levelMap.set(curLevel, new Array()); + } + this.userKeybindingError = mobx.observable.box(null, { + name: "keyutil-userKeybindingError", + }); + this.initKeyDescriptionsMap(); + } + + initKeyDescriptionsMap() { + mobx.action(() => { + this.userKeybindingError.set(null); + })(); + let newKeyDescriptions = new Map(); + for (let index = 0; index < defaultKeybindings.length; index++) { + let curKeybind = defaultKeybindings[index]; + newKeyDescriptions.set(curKeybind.command, curKeybind.keys); + } + let curUserCommand = ""; + if (this.userKeybindings != null && this.userKeybindings instanceof Array) { + try { + console.log("setting user keybindings"); + for (let index = 0; index < this.userKeybindings.length; index++) { + let curKeybind = this.userKeybindings[index]; + if (curKeybind == null) { + throw new Error("keybind entry is null"); + } + curUserCommand = curKeybind.command; + if (typeof curKeybind.command != "string") { + throw new Error("invalid keybind command"); + } + if (curKeybind.keys == null || !(curKeybind.keys instanceof Array)) { + throw new Error("invalid keybind keys"); + } + for (let key of curKeybind.keys) { + if (typeof key != "string") { + throw new Error("invalid keybind key"); + } + } + newKeyDescriptions.set(curKeybind.command, curKeybind.keys); + } + } catch (e) { + let userError = `${curUserCommand} is invalid: error: ${e}`; + console.log(userError); + mobx.action(() => { + this.userKeybindingError.set(userError); + })(); + } + } + this.keyDescriptionsMap = newKeyDescriptions; + console.log("key desc map:", this.keyDescriptionsMap); + } processLevel(nativeEvent: any, event: WaveKeyboardEvent, keybindsArray: Array): boolean { // iterate through keybinds in backwards order @@ -196,50 +259,11 @@ class KeybindManager { this.domainCallbacks.set(domain, callback); } - constructor() { - this.levelMap = new Map(); - this.domainCallbacks = new Map(); - this.levelArray = KeybindLevels; - for (let index = 0; index < this.levelArray.length; index++) { - let curLevel = this.levelArray[index]; - this.levelMap.set(curLevel, new Array()); - } + setUserKeybindings(userKeybindings) { + this.userKeybindings = userKeybindings; this.initKeyDescriptionsMap(); } - initKeyDescriptionsMap() { - this.keyDescriptionsMap = new Map(); - for (let index = 0; index < keybindings.length; index++) { - let curKeybind = keybindings[index]; - this.keyDescriptionsMap.set(curKeybind.command, curKeybind.keys); - } - let error = false; - let numberedTabKeybinds = []; - for (let index = 1; index <= 9; index++) { - let curKeybind = this.keyDescriptionsMap.get("app:selectTab-" + index); - if (curKeybind == null) { - error = true; - break; - } - numberedTabKeybinds = numberedTabKeybinds.concat(curKeybind); - } - if (!error) { - this.keyDescriptionsMap.set("app:selectNumberedTab", numberedTabKeybinds); - } - let numberedWorkspaceKeybinds = []; - for (let index = 1; index <= 9; index++) { - let curKeybind = this.keyDescriptionsMap.get("app:selectTab-" + index); - if (curKeybind == null) { - error = true; - break; - } - numberedWorkspaceKeybinds = numberedWorkspaceKeybinds.concat(curKeybind); - } - if (!error) { - this.keyDescriptionsMap.set("app:selectNumberedTab", numberedWorkspaceKeybinds); - } - } - checkKeyPressed(event: WaveKeyboardEvent, keyDescription: string): boolean { if (keyDescription == "any") { return true; diff --git a/src/util/util.ts b/src/util/util.ts index a5a9bef6..e91d47c7 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -398,6 +398,7 @@ function fireAndForget(f: () => Promise) { } export { + handleNotOkResp, handleJsonFetchResponse, base64ToString, stringToBase64, diff --git a/wavesrv/cmd/main-server.go b/wavesrv/cmd/main-server.go index 224b4a4e..c2d13377 100644 --- a/wavesrv/cmd/main-server.go +++ b/wavesrv/cmd/main-server.go @@ -17,6 +17,7 @@ import ( "net/http" "os" "os/signal" + "path" "path/filepath" "regexp" "runtime" @@ -676,6 +677,24 @@ func HandleRunCommand(w http.ResponseWriter, r *http.Request) { WriteJsonSuccess(w, update) } +func AuthKeyMiddleWare(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + reqAuthKey := r.Header.Get("X-AuthKey") + w.Header().Set(CacheControlHeaderKey, CacheControlHeaderNoCache) + if reqAuthKey == "" { + w.WriteHeader(500) + w.Write([]byte("no x-authkey header")) + return + } + if reqAuthKey != GlobalAuthKey { + w.WriteHeader(500) + w.Write([]byte("x-authkey header is invalid")) + return + } + next.ServeHTTP(w, r) + }) +} + func AuthKeyWrap(fn WebFnType) WebFnType { return func(w http.ResponseWriter, r *http.Request) { reqAuthKey := r.Header.Get("X-AuthKey") @@ -904,6 +923,10 @@ func main() { gr.HandleFunc("/api/log-active-state", AuthKeyWrap(HandleLogActiveState)) gr.HandleFunc("/api/read-file", AuthKeyWrap(HandleReadFile)) gr.HandleFunc("/api/write-file", AuthKeyWrap(HandleWriteFile)).Methods("POST") + configPath := path.Join(scbase.GetWaveHomeDir(), "config") + "/" + log.Printf("[wave] config path: %q\n", configPath) + gr.PathPrefix("/config/").Handler(AuthKeyMiddleWare(http.StripPrefix("/config/", http.FileServer(http.Dir(configPath))))) + serverAddr := MainServerAddr if scbase.IsDevMode() { serverAddr = MainServerDevAddr