server.cljs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. (ns frontend.components.server
  2. (:require
  3. [clojure.string :as string]
  4. [electron.ipc :as ipc]
  5. [frontend.handler.notification :as notification]
  6. [frontend.state :as state]
  7. [frontend.ui :as ui]
  8. [frontend.util :as util]
  9. [logseq.shui.hooks :as hooks]
  10. [logseq.shui.ui :as shui]
  11. [medley.core :as medley]
  12. [promesa.core :as p]
  13. [rum.core :as rum]))
  14. (rum/defcs panel-of-tokens
  15. < rum/reactive
  16. (rum/local nil ::tokens)
  17. {:will-mount
  18. (fn [s]
  19. (let [*tokens (s ::tokens)]
  20. (reset! *tokens (get-in @state/state [:electron/server :tokens])) s))}
  21. [_state close-panel]
  22. (let [server-state (state/sub :electron/server)
  23. *tokens (::tokens _state)
  24. changed? (not= @*tokens (:tokens server-state))]
  25. [:div.cp__server-tokens-panel.pt-6
  26. [:h2.text-3xl.-translate-y-4 "Authorization tokens"]
  27. ;; items
  28. (let [update-value! (fn [idx k v] (swap! *tokens assoc-in [idx k] v))]
  29. (for [[idx {:keys [value name]}] (medley/indexed @*tokens)]
  30. [:div.item.py-2.flex.space-x-2.items-center
  31. {:key idx}
  32. [:input.form-input.basis-36
  33. {:auto-focus true
  34. :placeholder "name"
  35. :value name
  36. :on-change #(let [value (.-value (.-target %))]
  37. (update-value! idx :name value))}]
  38. [:input.form-input
  39. {:placeholder "value"
  40. :value value
  41. :on-change #(let [value (.-value (.-target %))]
  42. (update-value! idx :value value))}]
  43. [:button.px-2.opacity-50.hover:opacity-90.active:opacity-100
  44. {:on-click #(reset! *tokens (into [] (medley/remove-nth idx @*tokens)))}
  45. [:span.flex.items-center (ui/icon "trash-x")]]]))
  46. [:p.flex.justify-end.pt-6.space-x-3
  47. (ui/button "+ Add new token"
  48. :on-click #(swap! *tokens conj {})
  49. :variant :outline)
  50. (ui/button "Save"
  51. :on-click (fn [] (-> (ipc/ipc :server/set-config {:tokens @*tokens})
  52. (p/then #(notification/show! "Update tokens successfully!" :success))
  53. (p/catch #(js/console.error %))
  54. (p/finally #(close-panel))))
  55. :disabled (not changed?))]]))
  56. (rum/defcs panel-of-configs
  57. < rum/reactive
  58. (rum/local nil ::configs)
  59. {:will-mount
  60. (fn [s]
  61. (let [*configs (s ::configs)]
  62. (reset! *configs (:electron/server @state/state)) s))}
  63. [_state close-panel]
  64. (let [server-state (state/sub :electron/server)
  65. *configs (::configs _state)
  66. {:keys [host port autostart]} @*configs
  67. hp-changed? (or (not= host (:host server-state))
  68. (not= (util/safe-parse-int (or port 0))
  69. (util/safe-parse-int (or (:port server-state) 0))))
  70. changed? (or hp-changed? (->> [autostart (:autostart server-state)]
  71. (mapv #(cond-> % (nil? %) boolean))
  72. (apply not=)))]
  73. [:div.cp__server-configs-panel.pt-5
  74. [:h2.text-3xl.-translate-y-4 "Server configurations"]
  75. [:div.item.flex.items-center.space-x-3
  76. [:label.basis-96
  77. [:strong "Host"]
  78. [:input.form-input
  79. {:value host
  80. :on-change #(let [value (.-value (.-target %))]
  81. (swap! *configs assoc :host value))}]]
  82. [:label
  83. [:strong "Port (1 ~ 65535)"]
  84. [:input.form-input
  85. {:auto-focus true
  86. :value port
  87. :min "1"
  88. :max "65535"
  89. :type "number"
  90. :on-change #(let [value (.-value (.-target %))]
  91. (swap! *configs assoc :port value))}]]]
  92. [:p.py-3.px-1
  93. [:label.flex.space-x-2.items-center
  94. (ui/checkbox
  95. {:on-change #(let [checked (.-checked (.-target %))]
  96. (swap! *configs assoc :autostart checked))
  97. :checked (not (false? autostart))})
  98. [:strong.select-none "Auto start server with the app launched"]]]
  99. [:p.flex.justify-end.pt-6.space-x-3
  100. (ui/button "Reset" :variant :outline
  101. :on-click #(reset! *configs (select-keys server-state [:host :port :autostart])))
  102. (ui/button "Save & Apply"
  103. :disabled (not changed?)
  104. :on-click (fn []
  105. (let [configs (select-keys @*configs [:host :port :autostart])]
  106. (-> (ipc/ipc :server/set-config configs)
  107. (p/then #(p/let [_ (close-panel)
  108. _ (p/delay 1000)]
  109. (when hp-changed?
  110. (ipc/ipc :server/do :restart))))
  111. (p/catch #(notification/show! (str %) :error))))))]]))
  112. (rum/defc server-indicator
  113. [server-state]
  114. (hooks/use-effect!
  115. (fn []
  116. (p/let [_ (p/delay 1000)
  117. _ (ipc/ipc :server/load-state)]
  118. (let [t (js/setTimeout #(when (state/sub [:electron/server :autostart])
  119. (ipc/ipc :server/do :restart)) 1000)]
  120. #(js/clearTimeout t))))
  121. [])
  122. (let [{:keys [status error]} server-state
  123. status (keyword (util/safe-lower-case status))
  124. running? (= :running status)
  125. href (and running? (str "http://" (:host server-state) ":" (:port server-state)))]
  126. (hooks/use-effect!
  127. #(when error
  128. (notification/show! (str "[Server] " error) :error))
  129. [error])
  130. [:div.cp__server-indicator
  131. (shui/button-ghost-icon (if running? "api" "api-off")
  132. {:on-click (fn [^js e]
  133. (shui/popup-show!
  134. (.-target e)
  135. (fn [{:keys [_close]}]
  136. (let [items [{:hr? true}
  137. (cond
  138. running?
  139. {:title "Stop server"
  140. :options {:on-click #(ipc/ipc :server/do :stop)}
  141. :icon [:span.text-red-500.flex.items-center (ui/icon "player-stop")]}
  142. :else
  143. {:title "Start server"
  144. :options {:on-click #(ipc/ipc :server/do :restart)}
  145. :icon [:span.text-green-500.flex.items-center (ui/icon "player-play")]})
  146. {:title "Authorization tokens"
  147. :options {:on-click #(shui/dialog-open!
  148. (fn []
  149. (panel-of-tokens shui/dialog-close!)))}
  150. :icon (ui/icon "key")}
  151. {:title "Server configurations"
  152. :options {:on-click #(shui/dialog-open!
  153. (fn []
  154. (panel-of-configs shui/dialog-close!)))}
  155. :icon (ui/icon "server-cog")}]]
  156. (cons
  157. [:div.links-header.flex.justify-center.py-2
  158. [:span.ml-1.text-sm.opacity-70
  159. (if-not running?
  160. (string/upper-case (or (:status server-state) "stopped"))
  161. [:a.hover:underline {:href href} href])]]
  162. (for [{:keys [hr? title options icon]} items]
  163. (cond
  164. hr?
  165. (shui/dropdown-menu-separator)
  166. :else
  167. (shui/dropdown-menu-item options
  168. [:span.flex.items-center icon [:span.pl-1 title]]))))))
  169. {:as-dropdown? true
  170. :content-props {:onClick #(shui/popup-hide!)}}))})]))