فهرست منبع

fix: sync doesn't work on node adapter

Tienson Qin 6 روز پیش
والد
کامیت
084b5193c3

+ 3 - 3
deps/db-sync/README.md

@@ -46,9 +46,9 @@ Run the adapter with Cognito auth:
 
 ```bash
 DB_SYNC_PORT=8787 \
-COGNITO_ISSUER=https://cognito-idp.us-east-2.amazonaws.com/us-east-2_kAqZcxIeM \
-COGNITO_CLIENT_ID=1qi1uijg8b6ra70nejvbptis0q \
-COGNITO_JWKS_URL=https://cognito-idp.us-east-2.amazonaws.com/us-east-2_kAqZcxIeM/.well-known/jwks.json \
+COGNITO_ISSUER=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8 \
+COGNITO_CLIENT_ID=69cs1lgme7p8kbgld8n5kseii6 \
+COGNITO_JWKS_URL=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/jwks.json \
 node worker/dist/node-adapter.js
 ```
 

+ 29 - 1
deps/db-sync/src/logseq/db_sync/index.cljs

@@ -1,5 +1,6 @@
 (ns logseq.db-sync.index
-  (:require [logseq.db-sync.common :as common]
+  (:require [clojure.string :as string]
+            [logseq.db-sync.common :as common]
             [promesa.core :as p]))
 
 (def ^:private user-upsert-cache-ttl-ms (* 60 60 1000))
@@ -39,6 +40,32 @@
   [v]
   (if (false? v) 0 1))
 
+(def ^:private graph-e2ee-migration-sql
+  "alter table graphs add column graph_e2ee INTEGER DEFAULT 1")
+
+(defn- duplicate-column-error?
+  [error column-name]
+  (let [message (-> (or (ex-message error) (some-> error .-message) (str error))
+                    string/lower-case)]
+    (and (string/includes? message "duplicate column")
+         (string/includes? message (string/lower-case column-name)))))
+
+(defn- <ensure-graph-e2ee-column!
+  [db]
+  (letfn [(<run-migration! []
+            (-> (common/<d1-run db graph-e2ee-migration-sql)
+                (p/catch (fn [error]
+                           (if (duplicate-column-error? error "graph_e2ee")
+                             nil
+                             (p/rejected error))))))]
+    (-> (p/let [result (common/<d1-all db
+                                       "select name from pragma_table_info('graphs') where name = 'graph_e2ee'")
+                rows (common/get-sql-rows result)]
+          (when (empty? rows)
+            (<run-migration!)))
+        (p/catch (fn [_]
+                   (<run-migration!))))))
+
 (defn <index-init! [db]
   (p/do!
    (common/<d1-run db
@@ -51,6 +78,7 @@
                         "created_at INTEGER,"
                         "updated_at INTEGER"
                         ");"))
+   (<ensure-graph-e2ee-column! db)
    (common/<d1-run db
                    (str "create table if not exists users ("
                         "id TEXT primary key,"

+ 3 - 2
deps/db-sync/src/logseq/db_sync/node/server.cljs

@@ -15,6 +15,7 @@
             [logseq.db-sync.platform.node :as platform-node]
             [logseq.db-sync.worker.auth :as auth]
             [logseq.db-sync.worker.handler.ws :as ws-handler]
+            [logseq.db-sync.worker.http :as worker-http]
             [logseq.db-sync.worker.presence :as presence]
             [promesa.core :as p]))
 
@@ -97,8 +98,8 @@
                                     (p/catch
                                      (fn [e]
                                        (log/error :db-sync/node-request-failed {:error e})
-                                       ;; Throw until there's a more desirable behavior like 500
-                                       (throw e))))))
+                                       (js/console.error ":db-sync/node-request-failed" e)
+                                       (platform-node/send-response! res (worker-http/error-response "server error" 500)))))))
         WSS (or (.-WebSocketServer ws) (.-Server ws))
         ^js wss (new WSS #js {:noServer true})]
     (.on server "error" (fn [error] (log/error :db-sync/node-server-error {:error error})))

+ 3 - 3
deps/db-sync/start.sh

@@ -5,9 +5,9 @@ set -euo pipefail
 
 # Defaults match the local `yarn watch` app auth config.
 # Override these env vars for production pool values if needed.
-: "${COGNITO_ISSUER:=https://cognito-idp.us-east-2.amazonaws.com/us-east-2_kAqZcxIeM}"
-: "${COGNITO_CLIENT_ID:=1qi1uijg8b6ra70nejvbptis0q}"
-: "${COGNITO_JWKS_URL:=https://cognito-idp.us-east-2.amazonaws.com/us-east-2_kAqZcxIeM/.well-known/jwks.json}"
+: "${COGNITO_ISSUER:=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8}"
+: "${COGNITO_CLIENT_ID:=69cs1lgme7p8kbgld8n5kseii6}"
+: "${COGNITO_JWKS_URL:=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/jwks.json}"
 
 export DB_SYNC_PORT
 export COGNITO_ISSUER

+ 44 - 0
deps/db-sync/test/logseq/db_sync/index_test.cljs

@@ -5,6 +5,9 @@
             [logseq.db-sync.index :as index]
             [promesa.core :as p]))
 
+(def ^:private graph-e2ee-migration-sql
+  "alter table graphs add column graph_e2ee integer default 1")
+
 (deftest index-list-includes-graph-e2ee-flag-test
   (async done
          (let [rows #js [#js {"graph_id" "graph-1"
@@ -54,3 +57,44 @@
                (p/catch (fn [error]
                           (is false (str error))
                           (done)))))))
+
+(deftest index-init-runs-graph-e2ee-migration-test
+  (async done
+         (let [sql-calls (atom [])]
+           (-> (p/with-redefs [common/<d1-all (fn [& _]
+                                                (p/resolved #js {:results #js []}))
+                               common/get-sql-rows (fn [result]
+                                                     (aget result "results"))
+                               common/<d1-run (fn [_db sql & _args]
+                                                (swap! sql-calls conj (string/lower-case sql))
+                                                (p/resolved {:ok true}))]
+                 (index/<index-init! :db))
+               (p/then (fn [_]
+                         (is (some #(string/includes? % graph-e2ee-migration-sql)
+                                   @sql-calls))
+                         (done)))
+               (p/catch (fn [error]
+                          (is false (str error))
+                          (done)))))))
+
+(deftest index-init-ignores-duplicate-graph-e2ee-column-error-test
+  (async done
+         (let [sql-calls (atom [])]
+           (-> (p/with-redefs [common/<d1-all (fn [& _]
+                                                (p/resolved #js {:results #js []}))
+                               common/get-sql-rows (fn [result]
+                                                     (aget result "results"))
+                               common/<d1-run (fn [_db sql & _args]
+                                                (let [sql' (string/lower-case sql)]
+                                                  (swap! sql-calls conj sql')
+                                                  (if (string/includes? sql' graph-e2ee-migration-sql)
+                                                    (p/rejected (ex-info "duplicate column name: graph_e2ee" {}))
+                                                    (p/resolved {:ok true}))))]
+                 (index/<index-init! :db))
+               (p/then (fn [_]
+                         (is (some #(string/includes? % graph-e2ee-migration-sql)
+                                   @sql-calls))
+                         (done)))
+               (p/catch (fn [error]
+                          (is false (str error))
+                          (done)))))))

+ 119 - 0
deps/db-sync/test/logseq/db_sync/node_server_test.cljs

@@ -0,0 +1,119 @@
+(ns logseq.db-sync.node-server-test
+  (:require [cljs.test :refer [async deftest is]]
+            [logseq.db-sync.node.server :as node-server]
+            [logseq.db-sync.worker.auth :as auth]
+            [promesa.core :as p]))
+
+(defn- fetch-with-timeout [url timeout-ms]
+  (let [timeout-sentinel ::timeout]
+    {:sentinel timeout-sentinel
+     :promise (js/Promise.
+               (fn [resolve reject]
+                 (let [controller (js/AbortController.)
+                       timeout-id (js/setTimeout
+                                   (fn []
+                                     (.abort controller))
+                                   timeout-ms)]
+                   (-> (js/fetch url #js {:method "GET"
+                                          :headers #js {"authorization" "Bearer test.token.sig"}
+                                          :signal (.-signal controller)})
+                       (.then (fn [response]
+                                (js/clearTimeout timeout-id)
+                                (resolve response)))
+                       (.catch (fn [error]
+                                 (js/clearTimeout timeout-id)
+                                 (if (= "AbortError" (.-name error))
+                                   (resolve timeout-sentinel)
+                                   (reject error))))))))}))
+
+(deftest node-server-returns-500-when-auth-claims-rejects-test
+  (async done
+         (let [stop-server! (atom nil)
+               test-url (atom nil)]
+           (-> (p/with-redefs [auth/auth-claims
+                               (fn [_request _env]
+                                 (p/rejected (ex-info "jwks" {})))]
+                 (p/let [{:keys [base-url stop!]} (node-server/start! {:port 0
+                                                                       :data-dir (str "tmp/db-sync-node-server-test/" (random-uuid))})
+                         _ (reset! stop-server! stop!)
+                         _ (reset! test-url (str base-url "/graphs"))
+                         {:keys [promise sentinel]} (fetch-with-timeout @test-url 1200)
+                         response promise]
+                   (if (identical? response sentinel)
+                     (is false "request timed out")
+                     (p/let [body (.json response)]
+                       (is (= 500 (.-status response)))
+                       (is (= "server error" (aget body "error")))))))
+               (p/then
+                (fn []
+                  (if-let [stop! @stop-server!]
+                    (-> (stop!)
+                        (p/then (fn [] (done)))
+                        (p/catch (fn [error]
+                                   (is false (str error))
+                                   (done))))
+                    (done))))
+               (p/catch
+                (fn [error]
+                  (if-let [stop! @stop-server!]
+                    (-> (stop!)
+                        (p/then (fn []
+                                  (is false (str error))
+                                  (done)))
+                        (p/catch (fn [stop-error]
+                                   (is false (str error))
+                                   (is false (str stop-error))
+                                   (done))))
+                    (do
+                      (is false (str error))
+                      (done)))))))))
+
+(deftest node-server-logs-request-failed-marker-when-auth-claims-rejects-test
+  (async done
+         (let [stop-server! (atom nil)
+               original-console-error (.-error js/console)
+               logged-errors (atom [])]
+           (aset js/console
+                 "error"
+                 (fn [& args]
+                   (swap! logged-errors conj args)))
+           (-> (p/with-redefs [auth/auth-claims
+                               (fn [_request _env]
+                                 (p/rejected (ex-info "jwks" {})))]
+                 (p/let [{:keys [base-url stop!]} (node-server/start! {:port 0
+                                                                       :data-dir (str "tmp/db-sync-node-server-log-test/" (random-uuid))})
+                         _ (reset! stop-server! stop!)
+                         _ (reset! logged-errors [])
+                         {:keys [promise sentinel]} (fetch-with-timeout (str base-url "/graphs") 1200)
+                         response promise]
+                   (if (identical? response sentinel)
+                     (is false "request timed out")
+                     (do
+                       (is (= 500 (.-status response)))
+                       (is (some #(= ":db-sync/node-request-failed" (first %))
+                                 @logged-errors))))))
+               (p/then
+                (fn []
+                  (aset js/console "error" original-console-error)
+                  (if-let [stop! @stop-server!]
+                    (-> (stop!)
+                        (p/then (fn [] (done)))
+                        (p/catch (fn [error]
+                                   (is false (str error))
+                                   (done))))
+                    (done))))
+               (p/catch
+                (fn [error]
+                  (aset js/console "error" original-console-error)
+                  (if-let [stop! @stop-server!]
+                    (-> (stop!)
+                        (p/then (fn []
+                                  (is false (str error))
+                                  (done)))
+                        (p/catch (fn [stop-error]
+                                   (is false (str error))
+                                   (is false (str stop-error))
+                                   (done))))
+                    (do
+                      (is false (str error))
+                      (done)))))))))

+ 1 - 0
deps/db-sync/test/logseq/db_sync/test_runner.cljs

@@ -4,6 +4,7 @@
             [logseq.db-sync.index-test]
             [logseq.db-sync.node-adapter-test]
             [logseq.db-sync.node-config-test]
+            [logseq.db-sync.node-server-test]
             [logseq.db-sync.platform-test]
             [logseq.db-sync.worker-auth-test]
             [logseq.db-sync.worker-handler-assets-test]