diff --git a/src/main/logseq/cli/auth.cljs b/src/main/logseq/cli/auth.cljs index ed485d834b..d9019758d1 100644 --- a/src/main/logseq/cli/auth.cljs +++ b/src/main/logseq/cli/auth.cljs @@ -319,14 +319,35 @@ (js/clearTimeout timeout-id) (stop-server! server))}))))))) +(defn- browser-open-command + [platform url] + (letfn [(unwrap-double-quoted [s] + (if (and (string/starts-with? s "\"") + (string/ends-with? s "\"") + (> (count s) 1)) + (subs s 1 (dec (count s))) + s)) + (windows-start-url-command [s] + (str "start \"\" \"" (unwrap-double-quoted s) "\""))] + (case platform + "darwin" ["open" [url]] + "linux" ["xdg-open" [url]] + "win32" ["cmd.exe" ["/d" "/c" (windows-start-url-command url)]] + [nil nil]))) + +(defn- browser-open-spawn-options + [platform] + (cond-> {:detached true + :stdio "ignore" + :shell false} + (= platform "win32") + (assoc :windowsVerbatimArguments true))) + (defn open-browser! [url] (let [platform (.-platform js/process) - [command args] (case platform - "darwin" ["open" [url]] - "linux" ["xdg-open" [url]] - "win32" ["cmd" ["/c" "start" "" url]] - [nil nil])] + [command args] (browser-open-command platform url) + spawn-opts (browser-open-spawn-options platform)] (if-not (seq command) (p/rejected (ex-info "unsupported platform for browser open" {:code :browser-open-unsupported-platform @@ -334,10 +355,7 @@ (p/create (fn [resolve reject] (try - (let [child (.spawn child-process command (clj->js args) - #js {:detached true - :stdio "ignore" - :shell false})] + (let [child (.spawn child-process command (clj->js args) (clj->js spawn-opts))] (.unref child) (resolve {:opened? true :command command})) diff --git a/src/test/logseq/cli/auth_test.cljs b/src/test/logseq/cli/auth_test.cljs index 15da22fd94..e1003ff1d8 100644 --- a/src/test/logseq/cli/auth_test.cljs +++ b/src/test/logseq/cli/auth_test.cljs @@ -137,6 +137,39 @@ (p/finally (fn [] (done))))))) +(deftest test-open-browser-uses-cmd-start-on-windows + (let [open-command (fn [platform url] + (call-private 'browser-open-command platform url)) + [command args] (open-command "win32" + "https://example.com/oauth2/authorize?response_type=code&client_id=abc&redirect_uri=http%3A%2F%2Flocalhost%3A8765%2Fauth%2Fcallback")] + (is (= "cmd.exe" command)) + (is (= ["/d" "/c" + "start \"\" \"https://example.com/oauth2/authorize?response_type=code&client_id=abc&redirect_uri=http%3A%2F%2Flocalhost%3A8765%2Fauth%2Fcallback\""] + args)))) + +(deftest test-open-browser-normalizes-prequoted-windows-url + (let [open-command (fn [platform url] + (call-private 'browser-open-command platform url)) + [command args] (open-command "win32" + "\"https://example.com/oauth2/authorize?response_type=code&client_id=abc&redirect_uri=http%3A%2F%2Flocalhost%3A8765%2Fauth%2Fcallback\"")] + (is (= "cmd.exe" command)) + (is (= ["/d" "/c" + "start \"\" \"https://example.com/oauth2/authorize?response_type=code&client_id=abc&redirect_uri=http%3A%2F%2Flocalhost%3A8765%2Fauth%2Fcallback\""] + args)))) + +(deftest test-open-browser-uses-verbatim-windows-cmd-arguments + (let [spawn-options (fn [platform] + (call-private 'browser-open-spawn-options platform))] + (is (= {:detached true + :stdio "ignore" + :shell false + :windowsVerbatimArguments true} + (spawn-options "win32"))) + (is (= {:detached true + :stdio "ignore" + :shell false} + (spawn-options "linux"))))) + (deftest test-parse-jwt-fails-fast-on-invalid-token (let [parse-jwt (fn [jwt] (call-private 'parse-jwt jwt))]