mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
Merge remote-tracking branch 'origin/master' into feat/cliable
This commit is contained in:
@@ -534,6 +534,16 @@
|
||||
[value]
|
||||
(contains? #{"true" "1"} value))
|
||||
|
||||
(defn- sqlite-too-big-error?
|
||||
[error]
|
||||
(let [message (-> (or (ex-message error)
|
||||
(some-> error .-message)
|
||||
(str error))
|
||||
string/lower-case)]
|
||||
(or (string/includes? message "sqlite_toobig")
|
||||
(string/includes? message "string or blob too big")
|
||||
(string/includes? message "statement too long"))))
|
||||
|
||||
(defn- handle-sync-snapshot-upload
|
||||
[^js self request url]
|
||||
(let [graph-id (graph-id-from-request request)
|
||||
@@ -556,27 +566,27 @@
|
||||
(if (and (= encoding snapshot-content-encoding)
|
||||
(not (exists? js/DecompressionStream)))
|
||||
(http/error-response "gzip not supported" 500)
|
||||
(p/let [_ (ensure-schema! self)
|
||||
_ (when reset?
|
||||
(storage/set-meta! (.-sql self) snapshot-uploading-meta-key true))
|
||||
_ (when reset?
|
||||
(<set-graph-ready-for-use! self graph-id false))
|
||||
stream (maybe-decompress-stream stream encoding)
|
||||
count (import-snapshot-stream! self stream reset?)
|
||||
_ (when finished?
|
||||
(storage/set-meta! (.-sql self) snapshot-uploading-meta-key false))
|
||||
_ (when finished?
|
||||
(when (seq checksum-param)
|
||||
(storage/set-checksum! (.-sql self) checksum-param)))
|
||||
_ (when finished?
|
||||
(<set-graph-ready-for-use! self graph-id true))
|
||||
_ (when finished?
|
||||
;; Snapshot replacement resets tx history (`t` may drop to 0).
|
||||
;; Broadcast current `t` so connected clients can recover.
|
||||
(ws/broadcast! self nil {:type "changed"
|
||||
:t (t-now self)}))]
|
||||
(http/json-response :sync/snapshot-upload {:ok true
|
||||
:count count})))))))
|
||||
(p/catch
|
||||
(p/let [_ (ensure-schema! self)
|
||||
_ (when reset?
|
||||
(storage/set-meta! (.-sql self) snapshot-uploading-meta-key true))
|
||||
_ (when reset?
|
||||
(<set-graph-ready-for-use! self graph-id false))
|
||||
stream (maybe-decompress-stream stream encoding)
|
||||
count (import-snapshot-stream! self stream reset?)
|
||||
_ (when finished?
|
||||
(storage/set-meta! (.-sql self) snapshot-uploading-meta-key false))
|
||||
_ (when finished?
|
||||
(when (seq checksum-param)
|
||||
(storage/set-checksum! (.-sql self) checksum-param)))
|
||||
_ (when finished?
|
||||
(<set-graph-ready-for-use! self graph-id true))]
|
||||
(http/json-response :sync/snapshot-upload {:ok true
|
||||
:count count}))
|
||||
(fn [error]
|
||||
(if (sqlite-too-big-error? error)
|
||||
(http/error-response "snapshot row too large" 413)
|
||||
(throw error)))))))))
|
||||
|
||||
(defn handle [{:keys [^js self request url route]}]
|
||||
(case (:handler route)
|
||||
|
||||
@@ -661,11 +661,10 @@
|
||||
(is false (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest finished-snapshot-upload-broadcasts-changed-test
|
||||
(deftest snapshot-upload-returns-413-when-sqlite-row-is-too-large-test
|
||||
(async done
|
||||
(let [sql (test-sql/make-sql)
|
||||
conn (d/create-conn db-schema/schema)
|
||||
changed-messages (atom [])
|
||||
self #js {:sql sql
|
||||
:conn conn
|
||||
:schema-ready true
|
||||
@@ -674,19 +673,17 @@
|
||||
#js {:method "POST"
|
||||
:body (js/Uint8Array. 0)})]
|
||||
(-> (p/with-redefs [sync-handler/import-snapshot-stream! (fn [_self _stream _reset?]
|
||||
(p/resolved 0))
|
||||
(p/rejected (js/Error. "string or blob too big: SQLITE_TOOBIG")))
|
||||
sync-handler/<set-graph-ready-for-use! (fn [_self _graph-id _graph-ready-for-use?]
|
||||
(p/resolved true))
|
||||
ws/broadcast! (fn [_self _sender payload]
|
||||
(swap! changed-messages conj payload))]
|
||||
(p/resolved true))]
|
||||
(p/let [resp (sync-handler/handle {:self self
|
||||
:request request
|
||||
:url (js/URL. (.-url request))
|
||||
:route {:handler :sync/snapshot-upload}})]
|
||||
(is (= 200 (.-status resp)))
|
||||
(is (= [{:type "changed"
|
||||
:t (storage/get-t sql)}]
|
||||
@changed-messages))))
|
||||
:route {:handler :sync/snapshot-upload}})
|
||||
text (.text resp)
|
||||
body (js->clj (js/JSON.parse text) :keywordize-keys true)]
|
||||
(is (= 413 (.-status resp)))
|
||||
(is (= {:error "snapshot row too large"} body))))
|
||||
(p/then (fn []
|
||||
(done)))
|
||||
(p/catch (fn [error]
|
||||
|
||||
1
deps/outliner/.carve/config.edn
vendored
1
deps/outliner/.carve/config.edn
vendored
@@ -2,7 +2,6 @@
|
||||
:api-namespaces [logseq.outliner.datascript-report
|
||||
logseq.outliner.pipeline
|
||||
logseq.outliner.cli
|
||||
logseq.outliner.batch-tx
|
||||
logseq.outliner.core
|
||||
logseq.outliner.db-pipeline
|
||||
logseq.outliner.property
|
||||
|
||||
2
deps/publish/deps.edn
vendored
2
deps/publish/deps.edn
vendored
@@ -1,4 +1,4 @@
|
||||
{:paths ["src" "../../resources"]
|
||||
{:paths ["src" "test" "../../resources"]
|
||||
:deps
|
||||
{org.clojure/clojure {:mvn/version "1.12.4"}
|
||||
rum/rum {:git/url "https://github.com/logseq/rum" ;; fork
|
||||
|
||||
2
deps/publish/package.json
vendored
2
deps/publish/package.json
vendored
@@ -7,11 +7,13 @@
|
||||
"dev": "cd ./worker && pnpm exec wrangler dev",
|
||||
"watch": "clojure -M:cljs watch publish-worker",
|
||||
"release": "clojure -M:cljs release publish-worker",
|
||||
"test": "clojure -M:cljs compile publish-test && node worker/dist/worker-test.js",
|
||||
"clean": "rm -rf ./worker/dist/",
|
||||
"bump-publish-version": "node ./scripts/bump-publish-version.js",
|
||||
"deploy": "pnpm bump-publish-version && pnpm clean && pnpm release && cd ./worker && pnpm exec wrangler deploy --env prod"
|
||||
},
|
||||
"dependencies": {
|
||||
"mldoc": "^1.5.9",
|
||||
"shadow-cljs": "^3.4.4"
|
||||
}
|
||||
}
|
||||
|
||||
412
deps/publish/pnpm-lock.yaml
generated
vendored
412
deps/publish/pnpm-lock.yaml
generated
vendored
@@ -8,12 +8,23 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
mldoc:
|
||||
specifier: ^1.5.9
|
||||
version: 1.5.9
|
||||
shadow-cljs:
|
||||
specifier: ^3.4.4
|
||||
version: 3.4.4
|
||||
|
||||
packages:
|
||||
|
||||
ansi-regex@2.1.1:
|
||||
resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ansi-regex@3.0.1:
|
||||
resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
@@ -23,21 +34,168 @@ packages:
|
||||
buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
|
||||
camelcase@5.3.1:
|
||||
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cliui@4.1.0:
|
||||
resolution: {integrity: sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==}
|
||||
|
||||
code-point-at@1.1.0:
|
||||
resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
cross-spawn@6.0.6:
|
||||
resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==}
|
||||
engines: {node: '>=4.8'}
|
||||
|
||||
decamelize@1.2.0:
|
||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||
|
||||
execa@1.0.0:
|
||||
resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
find-up@3.0.0:
|
||||
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
get-caller-file@1.0.3:
|
||||
resolution: {integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==}
|
||||
|
||||
get-stream@4.1.0:
|
||||
resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
invert-kv@2.0.0:
|
||||
resolution: {integrity: sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
is-fullwidth-code-point@1.0.0:
|
||||
resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-fullwidth-code-point@2.0.0:
|
||||
resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
is-stream@1.1.0:
|
||||
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
isexe@3.1.1:
|
||||
resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
lcid@2.0.0:
|
||||
resolution: {integrity: sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
locate-path@3.0.0:
|
||||
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
map-age-cleaner@0.1.3:
|
||||
resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
mem@4.3.0:
|
||||
resolution: {integrity: sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
mimic-fn@2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
mldoc@1.5.9:
|
||||
resolution: {integrity: sha512-87FQ7hseS87tsk+VdpIigpu8LH+GwmbbFgpxgFwvnbH5oOjmIrc47laH4Dyggzqiy8/vMjDHkl7vsId0eXhCDQ==}
|
||||
hasBin: true
|
||||
|
||||
nice-try@1.0.5:
|
||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||
|
||||
npm-run-path@2.0.2:
|
||||
resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
number-is-nan@1.0.1:
|
||||
resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
os-locale@3.1.0:
|
||||
resolution: {integrity: sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
p-defer@1.0.0:
|
||||
resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
p-finally@1.0.0:
|
||||
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
p-is-promise@2.1.0:
|
||||
resolution: {integrity: sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
p-limit@2.3.0:
|
||||
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
p-locate@3.0.0:
|
||||
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
p-try@2.2.0:
|
||||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
path-exists@3.0.0:
|
||||
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
path-key@2.0.1:
|
||||
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
process@0.11.10:
|
||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
pump@3.0.4:
|
||||
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
||||
|
||||
readline-sync@1.4.10:
|
||||
resolution: {integrity: sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
require-main-filename@1.0.1:
|
||||
resolution: {integrity: sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==}
|
||||
|
||||
semver@5.7.2:
|
||||
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
|
||||
hasBin: true
|
||||
|
||||
set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
|
||||
shadow-cljs-jar@1.3.4:
|
||||
resolution: {integrity: sha512-cZB2pzVXBnhpJ6PQdsjO+j/MksR28mv4QD/hP/2y1fsIa9Z9RutYgh3N34FZ8Ktl4puAXaIGlct+gMCJ5BmwmA==}
|
||||
|
||||
@@ -46,6 +204,17 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
shebang-command@1.2.0:
|
||||
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
shebang-regex@1.0.0:
|
||||
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
|
||||
@@ -53,11 +222,45 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
string-width@1.0.2:
|
||||
resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
string-width@2.1.1:
|
||||
resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
strip-ansi@3.0.1:
|
||||
resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
strip-ansi@4.0.0:
|
||||
resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
strip-eof@1.0.0:
|
||||
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
which-module@2.0.1:
|
||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||
|
||||
which@1.3.1:
|
||||
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
||||
hasBin: true
|
||||
|
||||
which@5.0.0:
|
||||
resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
hasBin: true
|
||||
|
||||
wrap-ansi@2.1.0:
|
||||
resolution: {integrity: sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
ws@8.18.3:
|
||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -70,8 +273,21 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
y18n@4.0.3:
|
||||
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
||||
|
||||
yargs-parser@11.1.1:
|
||||
resolution: {integrity: sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==}
|
||||
|
||||
yargs@12.0.5:
|
||||
resolution: {integrity: sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==}
|
||||
|
||||
snapshots:
|
||||
|
||||
ansi-regex@2.1.1: {}
|
||||
|
||||
ansi-regex@3.0.1: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
@@ -81,14 +297,146 @@ snapshots:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
camelcase@5.3.1: {}
|
||||
|
||||
cliui@4.1.0:
|
||||
dependencies:
|
||||
string-width: 2.1.1
|
||||
strip-ansi: 4.0.0
|
||||
wrap-ansi: 2.1.0
|
||||
|
||||
code-point-at@1.1.0: {}
|
||||
|
||||
cross-spawn@6.0.6:
|
||||
dependencies:
|
||||
nice-try: 1.0.5
|
||||
path-key: 2.0.1
|
||||
semver: 5.7.2
|
||||
shebang-command: 1.2.0
|
||||
which: 1.3.1
|
||||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
execa@1.0.0:
|
||||
dependencies:
|
||||
cross-spawn: 6.0.6
|
||||
get-stream: 4.1.0
|
||||
is-stream: 1.1.0
|
||||
npm-run-path: 2.0.2
|
||||
p-finally: 1.0.0
|
||||
signal-exit: 3.0.7
|
||||
strip-eof: 1.0.0
|
||||
|
||||
find-up@3.0.0:
|
||||
dependencies:
|
||||
locate-path: 3.0.0
|
||||
|
||||
get-caller-file@1.0.3: {}
|
||||
|
||||
get-stream@4.1.0:
|
||||
dependencies:
|
||||
pump: 3.0.4
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
invert-kv@2.0.0: {}
|
||||
|
||||
is-fullwidth-code-point@1.0.0:
|
||||
dependencies:
|
||||
number-is-nan: 1.0.1
|
||||
|
||||
is-fullwidth-code-point@2.0.0: {}
|
||||
|
||||
is-stream@1.1.0: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
isexe@3.1.1: {}
|
||||
|
||||
lcid@2.0.0:
|
||||
dependencies:
|
||||
invert-kv: 2.0.0
|
||||
|
||||
locate-path@3.0.0:
|
||||
dependencies:
|
||||
p-locate: 3.0.0
|
||||
path-exists: 3.0.0
|
||||
|
||||
map-age-cleaner@0.1.3:
|
||||
dependencies:
|
||||
p-defer: 1.0.0
|
||||
|
||||
mem@4.3.0:
|
||||
dependencies:
|
||||
map-age-cleaner: 0.1.3
|
||||
mimic-fn: 2.1.0
|
||||
p-is-promise: 2.1.0
|
||||
|
||||
mimic-fn@2.1.0: {}
|
||||
|
||||
mldoc@1.5.9:
|
||||
dependencies:
|
||||
yargs: 12.0.5
|
||||
|
||||
nice-try@1.0.5: {}
|
||||
|
||||
npm-run-path@2.0.2:
|
||||
dependencies:
|
||||
path-key: 2.0.1
|
||||
|
||||
number-is-nan@1.0.1: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
os-locale@3.1.0:
|
||||
dependencies:
|
||||
execa: 1.0.0
|
||||
lcid: 2.0.0
|
||||
mem: 4.3.0
|
||||
|
||||
p-defer@1.0.0: {}
|
||||
|
||||
p-finally@1.0.0: {}
|
||||
|
||||
p-is-promise@2.1.0: {}
|
||||
|
||||
p-limit@2.3.0:
|
||||
dependencies:
|
||||
p-try: 2.2.0
|
||||
|
||||
p-locate@3.0.0:
|
||||
dependencies:
|
||||
p-limit: 2.3.0
|
||||
|
||||
p-try@2.2.0: {}
|
||||
|
||||
path-exists@3.0.0: {}
|
||||
|
||||
path-key@2.0.1: {}
|
||||
|
||||
process@0.11.10: {}
|
||||
|
||||
pump@3.0.4:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.5
|
||||
once: 1.4.0
|
||||
|
||||
readline-sync@1.4.10: {}
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
require-main-filename@1.0.1: {}
|
||||
|
||||
semver@5.7.2: {}
|
||||
|
||||
set-blocking@2.0.0: {}
|
||||
|
||||
shadow-cljs-jar@1.3.4: {}
|
||||
|
||||
shadow-cljs@3.4.4:
|
||||
@@ -104,6 +452,14 @@ snapshots:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
shebang-command@1.2.0:
|
||||
dependencies:
|
||||
shebang-regex: 1.0.0
|
||||
|
||||
shebang-regex@1.0.0: {}
|
||||
|
||||
signal-exit@3.0.7: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
@@ -111,8 +467,64 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
string-width@1.0.2:
|
||||
dependencies:
|
||||
code-point-at: 1.1.0
|
||||
is-fullwidth-code-point: 1.0.0
|
||||
strip-ansi: 3.0.1
|
||||
|
||||
string-width@2.1.1:
|
||||
dependencies:
|
||||
is-fullwidth-code-point: 2.0.0
|
||||
strip-ansi: 4.0.0
|
||||
|
||||
strip-ansi@3.0.1:
|
||||
dependencies:
|
||||
ansi-regex: 2.1.1
|
||||
|
||||
strip-ansi@4.0.0:
|
||||
dependencies:
|
||||
ansi-regex: 3.0.1
|
||||
|
||||
strip-eof@1.0.0: {}
|
||||
|
||||
which-module@2.0.1: {}
|
||||
|
||||
which@1.3.1:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
which@5.0.0:
|
||||
dependencies:
|
||||
isexe: 3.1.1
|
||||
|
||||
wrap-ansi@2.1.0:
|
||||
dependencies:
|
||||
string-width: 1.0.2
|
||||
strip-ansi: 3.0.1
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
ws@8.18.3: {}
|
||||
|
||||
y18n@4.0.3: {}
|
||||
|
||||
yargs-parser@11.1.1:
|
||||
dependencies:
|
||||
camelcase: 5.3.1
|
||||
decamelize: 1.2.0
|
||||
|
||||
yargs@12.0.5:
|
||||
dependencies:
|
||||
cliui: 4.1.0
|
||||
decamelize: 1.2.0
|
||||
find-up: 3.0.0
|
||||
get-caller-file: 1.0.3
|
||||
os-locale: 3.1.0
|
||||
require-directory: 2.1.1
|
||||
require-main-filename: 1.0.1
|
||||
set-blocking: 2.0.0
|
||||
string-width: 2.1.1
|
||||
which-module: 2.0.1
|
||||
y18n: 4.0.3
|
||||
yargs-parser: 11.1.1
|
||||
|
||||
7
deps/publish/shadow-cljs.edn
vendored
7
deps/publish/shadow-cljs.edn
vendored
@@ -9,4 +9,9 @@
|
||||
PublishMetaDO logseq.publish.worker/PublishMetaDO}}}
|
||||
:js-options {:js-provider :import}
|
||||
:closure-defines {shadow.cljs.devtools.client.env/enabled false}
|
||||
:devtools {:enabled false}}}}
|
||||
:devtools {:enabled false}}
|
||||
:publish-test {:target :node-test
|
||||
:output-to "worker/dist/worker-test.js"
|
||||
:devtools {:enabled false}
|
||||
:compiler-options {:static-fns false}
|
||||
:main logseq.publish.test-runner/main}}}
|
||||
|
||||
33
deps/publish/src/logseq/publish/meta_store.cljs
vendored
33
deps/publish/src/logseq/publish/meta_store.cljs
vendored
@@ -281,10 +281,11 @@
|
||||
"page_tags.source_block_content, page_tags.source_block_format, page_tags.updated_at, "
|
||||
"pages.short_id "
|
||||
"FROM page_tags "
|
||||
"LEFT JOIN pages "
|
||||
"INNER JOIN pages "
|
||||
"ON pages.graph_uuid = page_tags.graph_uuid "
|
||||
"AND pages.page_uuid = page_tags.source_page_uuid "
|
||||
"WHERE page_tags.tag_title = ? "
|
||||
"AND pages.password_hash IS NULL "
|
||||
"ORDER BY page_tags.updated_at DESC;")
|
||||
tag-name))
|
||||
page-rows (publish-common/get-sql-rows
|
||||
@@ -293,10 +294,11 @@
|
||||
"pages.short_id, "
|
||||
"MAX(page_tags.updated_at) AS updated_at "
|
||||
"FROM page_tags "
|
||||
"LEFT JOIN pages "
|
||||
"INNER JOIN pages "
|
||||
"ON pages.graph_uuid = page_tags.graph_uuid "
|
||||
"AND pages.page_uuid = page_tags.source_page_uuid "
|
||||
"WHERE page_tags.tag_title = ? "
|
||||
"AND pages.password_hash IS NULL "
|
||||
"GROUP BY page_tags.graph_uuid, page_tags.source_page_uuid, page_tags.source_page_title, pages.short_id "
|
||||
"ORDER BY updated_at DESC;")
|
||||
tag-name))]
|
||||
@@ -316,11 +318,12 @@
|
||||
"pages.short_id, "
|
||||
"MAX(page_refs.updated_at) AS updated_at "
|
||||
"FROM page_refs "
|
||||
"LEFT JOIN pages "
|
||||
"INNER JOIN pages "
|
||||
"ON pages.graph_uuid = page_refs.graph_uuid "
|
||||
"AND pages.page_uuid = page_refs.source_page_uuid "
|
||||
"WHERE (lower(page_refs.target_page_title) = lower(?)) "
|
||||
"OR (page_refs.target_page_name = lower(?)) "
|
||||
"WHERE ((lower(page_refs.target_page_title) = lower(?)) "
|
||||
"OR (page_refs.target_page_name = lower(?))) "
|
||||
"AND pages.password_hash IS NULL "
|
||||
"GROUP BY page_refs.graph_uuid, page_refs.source_page_uuid, page_refs.source_page_title, pages.short_id "
|
||||
"ORDER BY updated_at DESC;")
|
||||
ref-name
|
||||
@@ -345,7 +348,10 @@
|
||||
rows (publish-common/get-sql-rows
|
||||
(publish-common/sql-exec sql
|
||||
(str "SELECT page_uuid, page_title, short_id, graph_uuid, updated_at, owner_username "
|
||||
"FROM pages WHERE owner_username = ? ORDER BY updated_at DESC;")
|
||||
"FROM pages "
|
||||
"WHERE owner_username = ? "
|
||||
"AND password_hash IS NULL "
|
||||
"ORDER BY updated_at DESC;")
|
||||
username))]
|
||||
(publish-common/json-response {:user {:username username}
|
||||
:pages (map (fn [row]
|
||||
@@ -381,11 +387,16 @@
|
||||
(= (nth parts 4 nil) "tagged_nodes")
|
||||
(let [rows (publish-common/get-sql-rows
|
||||
(publish-common/sql-exec sql
|
||||
(str "SELECT graph_uuid, tag_page_uuid, tag_title, source_page_uuid, "
|
||||
"source_page_title, source_block_uuid, source_block_content, "
|
||||
"source_block_format, updated_at "
|
||||
"FROM page_tags WHERE graph_uuid = ? AND tag_page_uuid = ? "
|
||||
"ORDER BY updated_at DESC;")
|
||||
(str "SELECT page_tags.graph_uuid, page_tags.tag_page_uuid, page_tags.tag_title, page_tags.source_page_uuid, "
|
||||
"page_tags.source_page_title, page_tags.source_block_uuid, page_tags.source_block_content, "
|
||||
"page_tags.source_block_format, page_tags.updated_at "
|
||||
"FROM page_tags "
|
||||
"INNER JOIN pages "
|
||||
"ON pages.graph_uuid = page_tags.graph_uuid "
|
||||
"AND pages.page_uuid = page_tags.source_page_uuid "
|
||||
"WHERE page_tags.graph_uuid = ? AND page_tags.tag_page_uuid = ? "
|
||||
"AND pages.password_hash IS NULL "
|
||||
"ORDER BY page_tags.updated_at DESC;")
|
||||
graph-uuid
|
||||
page-uuid))]
|
||||
(publish-common/json-response {:tagged_nodes (map (fn [row]
|
||||
|
||||
44
deps/publish/src/logseq/publish/publish.css
vendored
44
deps/publish/src/logseq/publish/publish.css
vendored
@@ -72,6 +72,10 @@ body {
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.publish-home {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -474,6 +478,7 @@ a:hover {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: flex-start;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
.positioned-properties {
|
||||
@@ -486,7 +491,10 @@ a:hover {
|
||||
.positioned-properties.block-left, .positioned-properties.block-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.block-content > .positioned-properties.block-left {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.positioned-properties.block-right {
|
||||
@@ -520,16 +528,40 @@ a:hover {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 1;
|
||||
font-size: 1rem;
|
||||
font-size: 18px;
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.property-icon svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.positioned-properties .ls-icon-Backlog {
|
||||
color: var(--rx-gray-05, #a8a29e);
|
||||
}
|
||||
|
||||
.positioned-properties .ls-icon-Todo {
|
||||
color: var(--rx-gray-10, #78716c);
|
||||
}
|
||||
|
||||
.positioned-properties .ls-icon-InProgress50 {
|
||||
color: var(--rx-yellow-08, #ca8a04);
|
||||
}
|
||||
|
||||
.positioned-properties .ls-icon-InReview {
|
||||
color: var(--rx-blue-09, #1d4ed8);
|
||||
}
|
||||
|
||||
.positioned-properties .ls-icon-Done {
|
||||
color: var(--rx-green-08, #16a34a);
|
||||
}
|
||||
|
||||
.positioned-properties .ls-icon-Cancelled {
|
||||
color: var(--rx-red-08, #dc2626);
|
||||
}
|
||||
|
||||
.property-value-with-icon {
|
||||
|
||||
2
deps/publish/src/logseq/publish/publish.js
vendored
2
deps/publish/src/logseq/publish/publish.js
vendored
@@ -151,7 +151,7 @@ const getTablerExtIcon = (id) => {
|
||||
const renderTablerExtIcon = (el, id) => {
|
||||
const iconFn = getTablerExtIcon(id);
|
||||
if (!iconFn) return false;
|
||||
const node = iconFn({ size: 14, stroke: 2 });
|
||||
const node = iconFn({ size: 18 });
|
||||
if (!node) return false;
|
||||
el.textContent = "";
|
||||
const domNode = reactNodeToDom(node);
|
||||
|
||||
23
deps/publish/src/logseq/publish/render.cljs
vendored
23
deps/publish/src/logseq/publish/render.cljs
vendored
@@ -11,7 +11,7 @@
|
||||
[logseq.publish.model :as publish-model]))
|
||||
|
||||
;; Timestamp in milliseconds used for cache busting static assets.
|
||||
(defonce version 1767194868810)
|
||||
(defonce version 1777365821532)
|
||||
|
||||
(def ref-regex
|
||||
(js/RegExp. "\\[\\[([0-9a-fA-F-]{36})\\]\\]|\\(\\(([0-9a-fA-F-]{36})\\)\\)" "g"))
|
||||
@@ -100,13 +100,19 @@
|
||||
(defn- icon-span
|
||||
[icon]
|
||||
(when (and (map? icon) (string? (:id icon)) (not (string/blank? (:id icon))))
|
||||
(let [icon-type (:type icon)
|
||||
icon-id (:id icon)
|
||||
class-name (str "property-icon"
|
||||
(when (and (= :tabler-icon icon-type)
|
||||
(not (string/blank? icon-id)))
|
||||
(str " ls-icon-" icon-id)))]
|
||||
[:span
|
||||
(cond->
|
||||
{:class "property-icon"
|
||||
{:class class-name
|
||||
:data-icon-id (:id icon)
|
||||
:data-icon-type (name (:type icon))}
|
||||
(:color icon)
|
||||
(assoc :style (str "color: " (:color icon) ";")))]))
|
||||
(assoc :style (str "color: " (:color icon) ";")))])))
|
||||
|
||||
(defn- with-icon
|
||||
[icon nodes]
|
||||
@@ -255,6 +261,11 @@
|
||||
(or (get property-type-by-ident prop-key)
|
||||
(get-in db-property/built-in-properties [prop-key :schema :type])))
|
||||
|
||||
(defn property-hidden?
|
||||
[prop-key property-hidden-by-ident]
|
||||
(or (true? (get property-hidden-by-ident prop-key))
|
||||
(true? (get-in db-property/built-in-properties [prop-key :schema :hide?]))))
|
||||
|
||||
(defn page-ref->uuid [name name->uuid]
|
||||
(or (get name->uuid name)
|
||||
(get name->uuid (common-util/page-name-sanity-lc name))))
|
||||
@@ -351,7 +362,7 @@
|
||||
props)
|
||||
props (->> props
|
||||
(remove (fn [[k _]]
|
||||
(true? (get (:property-hidden-by-ident ctx) k))))
|
||||
(property-hidden? k (:property-hidden-by-ident ctx))))
|
||||
(map (fn [[k v]]
|
||||
(if (= k :block/tags)
|
||||
[k (filter-tags v entities)]
|
||||
@@ -445,12 +456,12 @@
|
||||
[:div.positioned-property
|
||||
[:span.property-name (property-title k (:property-title-by-ident ctx))]
|
||||
[:span.property-value
|
||||
(into [:span] (positioned-value-nodes v k ctx entities))]])]
|
||||
(into [:span.inline-flex] (positioned-value-nodes v k ctx entities))]])]
|
||||
|
||||
[:div {:class (str "positioned-properties " (name position))}
|
||||
(for [[k v] (sorted-properties props ctx)]
|
||||
[:span.positioned-property
|
||||
(into [:span] (positioned-value-nodes v k ctx entities))])])))
|
||||
(into [:span.inline-flex] (positioned-value-nodes v k ctx entities))])])))
|
||||
|
||||
(def ^:private youtube-regex #"^((?:https?:)?//)?((?:www|m).)?((?:youtube.com|youtu.be|y2u.be|youtube-nocookie.com))(/(?:[\w-]+\?v=|embed/|v/)?)([\w-]+)([\S^\?]+)?$")
|
||||
(def ^:private vimeo-regex #"^((?:https?:)?//)?((?:www).)?((?:player.vimeo.com|vimeo.com))(/(?:video/)?)([\w-]+)(\S+)?$")
|
||||
|
||||
36
deps/publish/src/logseq/publish/routes.cljs
vendored
36
deps/publish/src/logseq/publish/routes.cljs
vendored
@@ -312,20 +312,6 @@
|
||||
(p/let [tags (.json tags-resp)]
|
||||
(publish-common/json-response (js->clj tags :keywordize-keys true) 200))))))))))
|
||||
|
||||
(defn handle-list-pages [env]
|
||||
(let [^js do-ns (aget env "PUBLISH_META_DO")
|
||||
do-id (.idFromName do-ns "index")
|
||||
do-stub (.get do-ns do-id)]
|
||||
(p/let [meta-resp (.fetch do-stub "https://publish/pages" #js {:method "GET"})]
|
||||
(if-not (.-ok meta-resp)
|
||||
(js/Response.
|
||||
(publish-render/render-404-html)
|
||||
#js {:headers (publish-common/merge-headers
|
||||
#js {"content-type" "text/html; charset=utf-8"}
|
||||
(publish-common/cors-headers))})
|
||||
(p/let [meta (.json meta-resp)]
|
||||
(publish-common/json-response (js->clj meta :keywordize-keys true) 200))))))
|
||||
|
||||
(defn handle-list-graph-pages-by-uuid [graph-uuid env]
|
||||
(if-not graph-uuid
|
||||
(publish-common/bad-request "missing graph uuid")
|
||||
@@ -620,6 +606,13 @@
|
||||
(publish-render/render-page-html transit page-uuid refs-json tagged-nodes)
|
||||
#js {:headers headers}))))))))))))))
|
||||
|
||||
(defn- rewrite-request-path
|
||||
[request new-path]
|
||||
(let [request-url (js/URL. (.-url request))
|
||||
new-url (js/URL. (str (.-origin request-url) new-path))]
|
||||
(set! (.-search new-url) (.-search request-url))
|
||||
(js/Request. (str new-url) request)))
|
||||
|
||||
(defn ^:large-vars/cleanup-todo handle-fetch [request env]
|
||||
(let [url (js/URL. (.-url request))
|
||||
path (.-pathname url)
|
||||
@@ -669,9 +662,6 @@
|
||||
(and (= path "/pages") (= method "POST"))
|
||||
(handle-post-pages request env)
|
||||
|
||||
(and (= path "/pages") (= method "GET"))
|
||||
(handle-list-pages env)
|
||||
|
||||
(and (string/starts-with? path "/search/") (= method "GET"))
|
||||
(handle-graph-search request env)
|
||||
|
||||
@@ -726,7 +716,9 @@
|
||||
(js/Response. (.-body object)
|
||||
#js {:headers headers}))))))))
|
||||
|
||||
(and (string/starts-with? path "/p/") (= method "GET"))
|
||||
(and (or (string/starts-with? path "/p/")
|
||||
(string/starts-with? path "/s/"))
|
||||
(= method "GET"))
|
||||
(let [parts (string/split path #"/")
|
||||
short-id (nth parts 2 nil)]
|
||||
(if (string/blank? short-id)
|
||||
@@ -744,11 +736,9 @@
|
||||
(publish-common/not-found)
|
||||
(let [graph-uuid (aget row "graph_uuid")
|
||||
page-uuid (aget row "page_uuid")
|
||||
location (str "/page/" graph-uuid "/" page-uuid)]
|
||||
(js/Response. nil #js {:status 302
|
||||
:headers (publish-common/merge-headers
|
||||
#js {"location" location}
|
||||
(publish-common/cors-headers))})))))))))
|
||||
page-path (str "/page/" graph-uuid "/" page-uuid)
|
||||
page-request (rewrite-request-path request page-path)]
|
||||
(handle-page-html page-request env)))))))))
|
||||
|
||||
(and (string/starts-with? path "/u/") (= method "GET"))
|
||||
(let [parts (string/split path #"/")
|
||||
|
||||
100
deps/publish/test/logseq/publish/common_test.cljs
vendored
Normal file
100
deps/publish/test/logseq/publish/common_test.cljs
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
(ns logseq.publish.common-test
|
||||
(:require [cljs.test :refer [async deftest is testing]]
|
||||
[logseq.publish.common :as publish-common]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(deftest merge-headers-overrides-and-preserves-values
|
||||
(let [headers (publish-common/merge-headers #js {"a" "1" "keep" "ok"}
|
||||
#js {"a" "2" "b" "3"})]
|
||||
(is (= "2" (.get headers "a")))
|
||||
(is (= "3" (.get headers "b")))
|
||||
(is (= "ok" (.get headers "keep")))))
|
||||
|
||||
(deftest parse-meta-header-valid-and-invalid-json
|
||||
(testing "valid json meta header is parsed into keywordized map"
|
||||
(let [request (js/Request. "https://publish.example/pages"
|
||||
#js {:headers #js {"x-publish-meta" "{\"content_hash\":\"h\",\"graph\":\"g\",\"page_uuid\":\"p\"}"}})
|
||||
meta (publish-common/parse-meta-header request)]
|
||||
(is (= "h" (:content_hash meta)))
|
||||
(is (= "g" (:graph meta)))
|
||||
(is (= "p" (:page_uuid meta)))))
|
||||
(testing "invalid json returns nil"
|
||||
(let [request (js/Request. "https://publish.example/pages"
|
||||
#js {:headers #js {"x-publish-meta" "{not-json"}})]
|
||||
(is (nil? (publish-common/parse-meta-header request))))))
|
||||
|
||||
(deftest valid-meta-requires-core-fields
|
||||
(is (some? (publish-common/valid-meta? {:content_hash "h" :graph "g" :page_uuid "p"})))
|
||||
(is (nil? (publish-common/valid-meta? {:content_hash "h" :graph "g"})))
|
||||
(is (nil? (publish-common/valid-meta? nil))))
|
||||
|
||||
(deftest get-sql-rows-handles-supported-shapes
|
||||
(is (= [] (publish-common/get-sql-rows nil)))
|
||||
(is (= [{"a" 1}]
|
||||
(js->clj (publish-common/get-sql-rows #js {:rows #js [#js {"a" 1}]})
|
||||
:keywordize-keys false)))
|
||||
(let [row (js-obj)]
|
||||
(aset row "rows" #js [#js {"b" 2}])
|
||||
(is (= [{"rows" [{"b" 2}]}]
|
||||
(js->clj (publish-common/get-sql-rows #js [row])
|
||||
:keywordize-keys false)))))
|
||||
|
||||
(deftest json-error-response-helpers-set-status
|
||||
(async done
|
||||
(-> (p/let [unauthorized (publish-common/unauthorized)
|
||||
forbidden (publish-common/forbidden)
|
||||
bad-request (publish-common/bad-request "bad")
|
||||
not-found (publish-common/not-found)
|
||||
unauthorized-body (.json unauthorized)
|
||||
forbidden-body (.json forbidden)
|
||||
bad-request-body (.json bad-request)
|
||||
not-found-body (.json not-found)]
|
||||
(is (= 401 (.-status unauthorized)))
|
||||
(is (= 403 (.-status forbidden)))
|
||||
(is (= 400 (.-status bad-request)))
|
||||
(is (= 404 (.-status not-found)))
|
||||
(is (= "unauthorized" (aget unauthorized-body "error")))
|
||||
(is (= "forbidden" (aget forbidden-body "error")))
|
||||
(is (= "bad" (aget bad-request-body "error")))
|
||||
(is (= "not found" (aget not-found-body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest normalize-etag-removes-double-quotes
|
||||
(is (= "abc" (publish-common/normalize-etag "\"abc\"")))
|
||||
(is (= "abc" (publish-common/normalize-etag "abc")))
|
||||
(is (nil? (publish-common/normalize-etag nil))))
|
||||
|
||||
(deftest encode-path-encodes-path-segments
|
||||
(is (= "with%20space/plus%2Bsign" (publish-common/encode-path "with space/plus+sign"))))
|
||||
|
||||
(deftest short-id-for-page-is-deterministic-and-fixed-length
|
||||
(async done
|
||||
(-> (p/let [a (publish-common/short-id-for-page "graph-1" "page-1")
|
||||
b (publish-common/short-id-for-page "graph-1" "page-1")
|
||||
c (publish-common/short-id-for-page "graph-1" "page-2")]
|
||||
(is (= 10 (count a)))
|
||||
(is (= a b))
|
||||
(is (not= a c))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest hash-and-verify-password-roundtrip
|
||||
(async done
|
||||
(-> (p/let [hashed (publish-common/hash-password "secret-value")
|
||||
ok? (publish-common/verify-password "secret-value" hashed)
|
||||
wrong? (publish-common/verify-password "wrong-value" hashed)]
|
||||
(is (string? hashed))
|
||||
(is (true? ok?))
|
||||
(is (false? wrong?))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest verify-password-rejects-invalid-hash-format
|
||||
(is (false? (publish-common/verify-password "secret" "not-a-valid-hash"))))
|
||||
27
deps/publish/test/logseq/publish/render_test.cljs
vendored
Normal file
27
deps/publish/test/logseq/publish/render_test.cljs
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
(ns logseq.publish.render-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[logseq.publish.render :as render]))
|
||||
|
||||
(deftest entity-properties-filters-explicitly-hidden-properties
|
||||
(testing "property is filtered when ctx marks it hidden"
|
||||
(let [entity {:user.property/secret "value"}
|
||||
ctx {:property-hidden-by-ident {:user.property/secret true}}
|
||||
result (render/entity-properties entity ctx {})]
|
||||
(is (nil? (get result :user.property/secret))))))
|
||||
|
||||
(deftest entity-properties-filters-built-in-hidden-properties
|
||||
(testing "built-in properties with :hide? true are filtered without property entity metadata"
|
||||
(let [entity {:logseq.property/created-from-property "some-value"}
|
||||
ctx {:property-hidden-by-ident {}}
|
||||
result (render/entity-properties entity ctx {})]
|
||||
(is (nil? (get result :logseq.property/created-from-property))))))
|
||||
|
||||
(deftest filter-tags-removes-built-in-tag-values
|
||||
(testing "built-in class keyword tags are removed"
|
||||
(let [result (render/filter-tags [:logseq.class/Tag :user.property/custom] {})]
|
||||
(is (= [:user.property/custom] result))))
|
||||
(testing "built-in class entities are removed"
|
||||
(let [entities {1 {:db/ident :logseq.class/Tag}
|
||||
2 {:db/ident :user.property/custom}}
|
||||
result (render/filter-tags [1 2] entities)]
|
||||
(is (= [2] result)))))
|
||||
393
deps/publish/test/logseq/publish/routes_test.cljs
vendored
Normal file
393
deps/publish/test/logseq/publish/routes_test.cljs
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
(ns logseq.publish.routes-test
|
||||
(:require [cljs.test :refer [async deftest is testing]]
|
||||
[logseq.common.authorization :as authorization]
|
||||
[logseq.publish.common :as publish-common]
|
||||
[logseq.publish.routes :as routes]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn- json-response
|
||||
[data]
|
||||
(js/Response.
|
||||
(js/JSON.stringify data)
|
||||
#js {:status 200
|
||||
:headers #js {"content-type" "application/json"}}))
|
||||
|
||||
(defn- short-url-env
|
||||
[]
|
||||
(let [do-stub #js {:fetch (fn [url _opts]
|
||||
(cond
|
||||
(= "https://publish/short/abc123" url)
|
||||
(js/Promise.resolve
|
||||
(json-response
|
||||
#js {:page #js {"graph_uuid" "graph-1"
|
||||
"page_uuid" "page-1"}}))
|
||||
|
||||
(re-matches #"https://publish/pages/[^/]+/[^/]+/password" url)
|
||||
(js/Promise.resolve (json-response #js {}))
|
||||
|
||||
(re-matches #"https://publish/pages/[^/]+/[^/]+" url)
|
||||
(js/Promise.resolve
|
||||
(json-response
|
||||
#js {"content_hash" "etag-1"
|
||||
"r2_key" "publish/graph-1/page-1.transit"}))
|
||||
|
||||
:else
|
||||
(js/Promise.resolve
|
||||
(js/Response. "not found" #js {:status 404}))))}
|
||||
do-ns #js {:idFromName (fn [_name] "index")
|
||||
:get (fn [_id] do-stub)}
|
||||
r2 #js {:get (fn [_key]
|
||||
(js/Promise.resolve
|
||||
#js {:arrayBuffer (fn []
|
||||
(let [payload (.encode (js/TextEncoder.) "{}")]
|
||||
(js/Promise.resolve (.-buffer payload))))}))}]
|
||||
#js {"PUBLISH_META_DO" do-ns
|
||||
"PUBLISH_R2" r2}))
|
||||
|
||||
(defn- empty-env []
|
||||
#js {})
|
||||
|
||||
(defn- json-error-response
|
||||
[status message]
|
||||
(js/Response.
|
||||
(js/JSON.stringify #js {"error" message})
|
||||
#js {:status status
|
||||
:headers #js {"content-type" "application/json"}}))
|
||||
|
||||
(defn- ok-json-response
|
||||
[data]
|
||||
(js/Response.
|
||||
(js/JSON.stringify data)
|
||||
#js {:status 200
|
||||
:headers #js {"content-type" "application/json"}}))
|
||||
|
||||
(defn- method-from-opts
|
||||
[opts]
|
||||
(or (some-> opts (aget "method"))
|
||||
"GET"))
|
||||
|
||||
(defn- permission-env
|
||||
[route-dispatch]
|
||||
(let [do-ns #js {:idFromName (fn [name] name)
|
||||
:get (fn [id]
|
||||
#js {:fetch (fn [url opts]
|
||||
(js/Promise.resolve
|
||||
(route-dispatch id url (method-from-opts opts))))})}]
|
||||
#js {"PUBLISH_META_DO" do-ns}))
|
||||
|
||||
(deftest short-url-does-not-redirect-to-uuid-url
|
||||
(testing "short URL should not redirect to /page/:graph/:page"
|
||||
(async done
|
||||
(let [request (js/Request. "https://publish.example/p/abc123?password=s3cr3t")
|
||||
env (short-url-env)]
|
||||
(-> (p/let [response (routes/handle-fetch request env)
|
||||
body (.text response)]
|
||||
(is (= 200 (.-status response)))
|
||||
(is (nil? (.get (.-headers response) "location")))
|
||||
(is (re-find #"<!doctype html>" body))
|
||||
(is (not (re-find #"Page not found" body)))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))))
|
||||
|
||||
(deftest legacy-s-short-url-is-supported
|
||||
(testing "legacy /s/:short-id should render page html"
|
||||
(async done
|
||||
(let [request (js/Request. "https://publish.example/s/abc123")
|
||||
env (short-url-env)]
|
||||
(-> (p/let [response (routes/handle-fetch request env)
|
||||
body (.text response)]
|
||||
(is (= 200 (.-status response)))
|
||||
(is (nil? (.get (.-headers response) "location")))
|
||||
(is (re-find #"<!doctype html>" body))
|
||||
(is (not (re-find #"Page not found" body)))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))))
|
||||
|
||||
(deftest page-uuid-route-is-still-supported
|
||||
(testing "legacy /page/:graph-uuid/:page-uuid should still render page html"
|
||||
(async done
|
||||
(let [request (js/Request. "https://publish.example/page/3bc00ad3-f421-41e7-8c65-40861c298be5/6954ee2a-506b-4dd9-bd6d-0dc24db9c055")
|
||||
env (short-url-env)]
|
||||
(-> (p/let [response (routes/handle-fetch request env)
|
||||
body (.text response)]
|
||||
(is (= 200 (.-status response)))
|
||||
(is (nil? (.get (.-headers response) "location")))
|
||||
(is (re-find #"<!doctype html>" body))
|
||||
(is (not (re-find #"Page not found" body)))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))))
|
||||
|
||||
(deftest options-route-returns-cors-no-content
|
||||
(async done
|
||||
(let [request (js/Request. "https://publish.example/any"
|
||||
#js {:method "OPTIONS"})]
|
||||
(-> (p/let [response (routes/handle-fetch request (empty-env))]
|
||||
(is (= 204 (.-status response)))
|
||||
(is (= "*" (.get (.-headers response) "access-control-allow-origin")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest home-route-renders-html
|
||||
(async done
|
||||
(let [request (js/Request. "https://publish.example/")]
|
||||
(-> (p/let [response (routes/handle-fetch request (empty-env))
|
||||
body (.text response)]
|
||||
(is (= 200 (.-status response)))
|
||||
(is (re-find #"<!doctype html>" body))
|
||||
(is (re-find #"Logseq Publish" body))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest static-assets-return-content-types
|
||||
(async done
|
||||
(-> (p/let [css-resp (routes/handle-fetch (js/Request. "https://publish.example/static/publish.css") (empty-env))
|
||||
js-resp (routes/handle-fetch (js/Request. "https://publish.example/static/publish.js") (empty-env))
|
||||
ext-resp (routes/handle-fetch (js/Request. "https://publish.example/static/tabler.ext.js") (empty-env))]
|
||||
(is (= "text/css; charset=utf-8" (.get (.-headers css-resp) "content-type")))
|
||||
(is (= "text/javascript; charset=utf-8" (.get (.-headers js-resp) "content-type")))
|
||||
(is (= "text/javascript; charset=utf-8" (.get (.-headers ext-resp) "content-type")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest short-url-missing-id-returns-bad-request
|
||||
(async done
|
||||
(-> (p/let [response (routes/handle-fetch (js/Request. "https://publish.example/p/") (empty-env))
|
||||
body (.json response)]
|
||||
(is (= 400 (.-status response)))
|
||||
(is (= "missing short id" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest short-url-not-found-returns-not-found
|
||||
(async done
|
||||
(let [do-stub #js {:fetch (fn [_url _opts]
|
||||
(js/Promise.resolve (js/Response. "nope" #js {:status 404})))}
|
||||
do-ns #js {:idFromName (fn [_name] "index")
|
||||
:get (fn [_id] do-stub)}
|
||||
env #js {"PUBLISH_META_DO" do-ns}]
|
||||
(-> (p/let [response (routes/handle-fetch (js/Request. "https://publish.example/p/abc") env)
|
||||
body (.json response)]
|
||||
(is (= 404 (.-status response)))
|
||||
(is (= "not found" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest asset-route-validates-missing-or-invalid-id
|
||||
(async done
|
||||
(-> (p/let [missing-id-resp (routes/handle-fetch (js/Request. "https://publish.example/asset//") (empty-env))
|
||||
missing-id-body (.json missing-id-resp)
|
||||
invalid-id-resp (routes/handle-fetch (js/Request. "https://publish.example/asset/g/noext") (empty-env))
|
||||
invalid-id-body (.json invalid-id-resp)]
|
||||
(is (= 400 (.-status missing-id-resp)))
|
||||
(is (= "missing asset id" (aget missing-id-body "error")))
|
||||
(is (= 400 (.-status invalid-id-resp)))
|
||||
(is (= "invalid asset id" (aget invalid-id-body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest user-route-validates-missing-username
|
||||
(async done
|
||||
(-> (p/let [response (routes/handle-fetch (js/Request. "https://publish.example/u/") (empty-env))
|
||||
body (.json response)]
|
||||
(is (= 400 (.-status response)))
|
||||
(is (= "missing username" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest post-pages-without-auth-is-unauthorized
|
||||
(async done
|
||||
(-> (p/let [request (js/Request. "https://publish.example/pages"
|
||||
#js {:method "POST"
|
||||
:headers #js {"content-type" "application/transit+json"}
|
||||
:body "{}"})
|
||||
response (routes/handle-fetch request (empty-env))
|
||||
body (.json response)]
|
||||
(is (= 401 (.-status response)))
|
||||
(is (= "unauthorized" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest delete-page-without-auth-is-unauthorized
|
||||
(async done
|
||||
(-> (p/let [request (js/Request. "https://publish.example/pages/graph-1/page-1"
|
||||
#js {:method "DELETE"})
|
||||
response (routes/handle-fetch request (empty-env))
|
||||
body (.json response)]
|
||||
(is (= 401 (.-status response)))
|
||||
(is (= "unauthorized" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done))))))
|
||||
|
||||
(deftest delete-page-owner-mismatch-is-forbidden
|
||||
(async done
|
||||
(let [env (permission-env
|
||||
(fn [id url method]
|
||||
(cond
|
||||
(and (= id "index")
|
||||
(= method "GET")
|
||||
(= url "https://publish/pages/graph-1/page-1"))
|
||||
(ok-json-response #js {"owner_sub" "owner-a"})
|
||||
|
||||
:else
|
||||
(json-error-response 404 "not found"))))
|
||||
request (js/Request. "https://publish.example/pages/graph-1/page-1"
|
||||
#js {:method "DELETE"
|
||||
:headers #js {"authorization" "Bearer token"}})]
|
||||
(-> (p/let [response (p/with-redefs [authorization/verify-jwt (fn [_ _] #js {"sub" "owner-b"})]
|
||||
(routes/handle-fetch request env))
|
||||
body (.json response)]
|
||||
(is (= 403 (.-status response)))
|
||||
(is (= "forbidden" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest delete-page-owner-match-succeeds
|
||||
(async done
|
||||
(let [env (permission-env
|
||||
(fn [id url method]
|
||||
(cond
|
||||
(and (= id "index")
|
||||
(= method "GET")
|
||||
(= url "https://publish/pages/graph-1/page-1"))
|
||||
(ok-json-response #js {"owner_sub" "owner-a"})
|
||||
|
||||
(and (= id "index")
|
||||
(= method "DELETE")
|
||||
(= url "https://publish/pages/graph-1/page-1"))
|
||||
(ok-json-response #js {"ok" true})
|
||||
|
||||
(and (= id "graph-1:page-1")
|
||||
(= method "DELETE")
|
||||
(= url "https://publish/pages/graph-1/page-1"))
|
||||
(ok-json-response #js {"ok" true})
|
||||
|
||||
:else
|
||||
(json-error-response 404 "not found"))))
|
||||
request (js/Request. "https://publish.example/pages/graph-1/page-1"
|
||||
#js {:method "DELETE"
|
||||
:headers #js {"authorization" "Bearer token"}})]
|
||||
(-> (p/let [response (p/with-redefs [authorization/verify-jwt (fn [_ _] #js {"sub" "owner-a"})]
|
||||
(routes/handle-fetch request env))
|
||||
body (.json response)]
|
||||
(is (= 200 (.-status response)))
|
||||
(is (true? (aget body "ok")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest delete-graph-owner-mismatch-is-forbidden
|
||||
(async done
|
||||
(let [env (permission-env
|
||||
(fn [id url method]
|
||||
(cond
|
||||
(and (= id "index")
|
||||
(= method "GET")
|
||||
(= url "https://publish/pages/graph-1"))
|
||||
(ok-json-response #js {"pages" #js [#js {"owner_sub" "owner-a" "page_uuid" "page-1"}]})
|
||||
|
||||
:else
|
||||
(json-error-response 404 "not found"))))
|
||||
request (js/Request. "https://publish.example/pages/graph-1"
|
||||
#js {:method "DELETE"
|
||||
:headers #js {"authorization" "Bearer token"}})]
|
||||
(-> (p/let [response (p/with-redefs [authorization/verify-jwt (fn [_ _] #js {"sub" "owner-b"})]
|
||||
(routes/handle-fetch request env))
|
||||
body (.json response)]
|
||||
(is (= 403 (.-status response)))
|
||||
(is (= "forbidden" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest delete-graph-owner-match-succeeds
|
||||
(async done
|
||||
(let [env (permission-env
|
||||
(fn [id url method]
|
||||
(cond
|
||||
(and (= id "index")
|
||||
(= method "GET")
|
||||
(= url "https://publish/pages/graph-1"))
|
||||
(ok-json-response #js {"pages" #js [#js {"owner_sub" "owner-a" "page_uuid" "page-1"}
|
||||
#js {"owner_sub" "owner-a" "page_uuid" "page-2"}]})
|
||||
|
||||
(and (= id "index")
|
||||
(= method "DELETE")
|
||||
(= url "https://publish/pages/graph-1"))
|
||||
(ok-json-response #js {"ok" true})
|
||||
|
||||
(and (= method "DELETE")
|
||||
(or (and (= id "graph-1:page-1") (= url "https://publish/pages/graph-1/page-1"))
|
||||
(and (= id "graph-1:page-2") (= url "https://publish/pages/graph-1/page-2"))))
|
||||
(ok-json-response #js {"ok" true})
|
||||
|
||||
:else
|
||||
(json-error-response 404 "not found"))))
|
||||
request (js/Request. "https://publish.example/pages/graph-1"
|
||||
#js {:method "DELETE"
|
||||
:headers #js {"authorization" "Bearer token"}})]
|
||||
(-> (p/let [response (p/with-redefs [authorization/verify-jwt (fn [_ _] #js {"sub" "owner-a"})]
|
||||
(routes/handle-fetch request env))
|
||||
body (.json response)]
|
||||
(is (= 200 (.-status response)))
|
||||
(is (true? (aget body "ok")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
|
||||
(deftest get-page-requires-correct-password-when-protected
|
||||
(async done
|
||||
(let [env (permission-env
|
||||
(fn [id url method]
|
||||
(cond
|
||||
(and (= id "graph-1:page-1")
|
||||
(= method "GET")
|
||||
(= url "https://publish/pages/graph-1/page-1"))
|
||||
(ok-json-response #js {"content_hash" "h-1"
|
||||
"r2_key" "publish/graph-1/page-1.transit"})
|
||||
|
||||
(and (= id "index")
|
||||
(= method "GET")
|
||||
(= url "https://publish/pages/graph-1/page-1/password"))
|
||||
(ok-json-response #js {"password_hash" "pbkdf2$sha256$90000$x$y"})
|
||||
|
||||
:else
|
||||
(json-error-response 404 "not found"))))
|
||||
request (js/Request. "https://publish.example/pages/graph-1/page-1")]
|
||||
(-> (p/let [response (p/with-redefs [publish-common/verify-password (fn [_ _] (p/resolved false))]
|
||||
(routes/handle-fetch request env))
|
||||
body (.json response)]
|
||||
(is (= 401 (.-status response)))
|
||||
(is (= "password required" (aget body "error")))
|
||||
(done))
|
||||
(p/catch (fn [error]
|
||||
(is nil (str error))
|
||||
(done)))))))
|
||||
23
deps/publish/test/logseq/publish/test_runner.cljs
vendored
Normal file
23
deps/publish/test/logseq/publish/test_runner.cljs
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
(ns logseq.publish.test-runner
|
||||
(:require [cljs.test :as ct]
|
||||
[logseq.publish.common-test]
|
||||
[logseq.publish.render-test]
|
||||
[logseq.publish.routes-test]
|
||||
[shadow.test :as st]
|
||||
[shadow.test.env :as env]))
|
||||
|
||||
(derive ::node ::ct/default)
|
||||
|
||||
(defmethod ct/report [::node :end-run-tests] [m]
|
||||
(if (ct/successful? m)
|
||||
(js/process.exit 0)
|
||||
(js/process.exit 1)))
|
||||
|
||||
(defn ^:dev/after-load reset-test-data! []
|
||||
(-> (env/get-test-data)
|
||||
(env/reset-test-data!)))
|
||||
|
||||
(defn main [& _args]
|
||||
(reset-test-data!)
|
||||
(let [test-env (ct/empty-env ::node)]
|
||||
(st/run-all-tests test-env nil)))
|
||||
2
deps/publish/worker/README.md
vendored
2
deps/publish/worker/README.md
vendored
@@ -32,8 +32,6 @@ metadata in a Durable Object backed by SQLite.
|
||||
- Deletes a published page
|
||||
- `DELETE /pages/:graph-uuid`
|
||||
- Deletes all pages for a graph
|
||||
- `GET /pages`
|
||||
- Lists metadata entries (from the index DO)
|
||||
- `GET /tag/:tag-name`
|
||||
- List all pages tagged with `:tag-name`
|
||||
- `GET /ref/:page-name`
|
||||
|
||||
Reference in New Issue
Block a user