Tienson Qin 3 týždňov pred
rodič
commit
704312b407

+ 1 - 0
deps.edn

@@ -38,6 +38,7 @@
   logseq/common                         {:local/root "deps/common"}
   logseq/graph-parser                   {:local/root "deps/graph-parser"}
   logseq/outliner                       {:local/root "deps/outliner"}
+  logseq/publish                        {:local/root "deps/publish"}
   logseq/publishing                     {:local/root "deps/publishing"}
   logseq/cli                            {:local/root "deps/cli"}
   logseq/shui                           {:local/root "deps/shui"}

+ 18 - 0
deps/publish/README.md

@@ -0,0 +1,18 @@
+## Description
+
+Shared library for page publishing (snapshot payloads, SSR helpers, shared schemas, and storage contracts).
+
+The Cloudflare Durable Object implementation is expected to use SQLite with the
+Logseq datascript fork layered on top.
+
+## API
+
+Namespaces live under `logseq.publish`.
+
+## Usage
+
+This module is intended to be consumed by the Logseq app and the publishing worker.
+
+## Dev
+
+Keep this module aligned with the main repo's linting and testing conventions.

+ 6 - 0
deps/publish/deps.edn

@@ -0,0 +1,6 @@
+{:deps
+ {}
+ :aliases
+ {:clj-kondo
+  {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2024.09.27"}}
+   :main-opts  ["-m" "clj-kondo.main"]}}}

+ 8 - 0
deps/publish/package.json

@@ -0,0 +1,8 @@
+{
+  "name": "@logseq/publish",
+  "version": "1.0.0",
+  "private": true,
+  "devDependencies": {
+    "@logseq/nbb-logseq": "github:logseq/nbb-logseq#feat-db-v31"
+  }
+}

+ 24 - 0
deps/publish/src/logseq/publish.cljc

@@ -0,0 +1,24 @@
+(ns logseq.publish
+  "Public entrypoint for page publishing shared logic."
+  (:require [logseq.publish.snapshot :as snapshot]
+            [logseq.publish.ssr :as ssr]
+            [logseq.publish.storage :as storage]))
+
+(defn normalize-snapshot
+  "Public wrapper around snapshot normalization."
+  [snapshot-map]
+  (snapshot/normalize-snapshot snapshot-map))
+
+(defn snapshot-valid?
+  "Checks required keys in the snapshot."
+  [snapshot-map]
+  (snapshot/snapshot-valid? snapshot-map))
+
+(defn render-page-html
+  "Render HTML for a published page."
+  [snapshot-map opts]
+  (ssr/render-page-html snapshot-map opts))
+
+(def PublishStore storage/PublishStore)
+
+;; Placeholder namespace for page publishing shared logic.

+ 29 - 0
deps/publish/src/logseq/publish/snapshot.cljc

@@ -0,0 +1,29 @@
+(ns logseq.publish.snapshot
+  "Utilities for shaping page publishing snapshot payloads.")
+
+(def required-keys
+  #{:page :blocks :linked-refs :config})
+
+(defn normalize-snapshot
+  "Ensures the snapshot contains the minimum required keys.
+
+  Expected shape:
+  {:page       <page entity map>
+   :blocks     <block tree or flat list>
+   :linked-refs <linked references payload>
+   :config     <publishing config map>
+   :assets     <optional asset map>}
+  "
+  [{:keys [page blocks linked-refs config] :as snapshot}]
+  (merge
+   {:page page
+    :blocks (or blocks [])
+    :linked-refs (or linked-refs [])
+    :config (or config {})
+    :assets (:assets snapshot)}
+   (select-keys snapshot required-keys)))
+
+(defn snapshot-valid?
+  "Checks if required keys are present in the snapshot map."
+  [snapshot]
+  (every? #(contains? snapshot %) required-keys))

+ 15 - 0
deps/publish/src/logseq/publish/ssr.cljc

@@ -0,0 +1,15 @@
+(ns logseq.publish.ssr
+  "SSR helpers for published pages.")
+
+(defn render-page-html
+  "Renders HTML for a published page.
+
+  Options:
+  - :render-page-fn should return HTML string for the given snapshot.
+  - :wrap-html-fn should wrap the rendered body with document-level markup.
+  "
+  [snapshot {:keys [render-page-fn wrap-html-fn]}]
+  (let [body (when render-page-fn (render-page-fn snapshot))]
+    (if wrap-html-fn
+      (wrap-html-fn body)
+      body)))

+ 9 - 0
deps/publish/src/logseq/publish/storage.cljc

@@ -0,0 +1,9 @@
+(ns logseq.publish.storage
+  "Contracts for durable storage backends.")
+
+(defprotocol PublishStore
+  "Storage for published page snapshots. Implementations should use SQLite as
+  the durable store and run the Logseq datascript fork on top of it."
+  (put-snapshot! [this page-id snapshot])
+  (get-snapshot [this page-id])
+  (delete-snapshot! [this page-id]))

+ 2 - 0
scripts/nbb.edn

@@ -7,5 +7,7 @@
   ;; for config.edn
   logseq/common
   {:local/root "../deps/common"}
+  logseq/publish
+  {:local/root "../deps/publish"}
   logseq/publishing
   {:local/root "../deps/publishing"}}}

+ 1 - 1
scripts/src/logseq/tasks/dev/lint.clj

@@ -24,7 +24,7 @@
 (defn kondo-git-changes
   "Run clj-kondo across dirs and only for files that git diff detects as unstaged changes"
   []
-  (let [kondo-dirs ["src" "deps/common" "deps/db" "deps/graph-parser" "deps/outliner" "deps/publishing" "deps/cli"]
+  (let [kondo-dirs ["src" "deps/common" "deps/db" "deps/graph-parser" "deps/outliner" "deps/publish" "deps/publishing" "deps/cli"]
         dir-regex (re-pattern (str "^(" (string/join "|" kondo-dirs) ")"))
         dir-to-files (->> (shell {:out :string} "git diff --name-only")
                           :out

+ 5 - 0
src/main/frontend/components/page_menu.cljs

@@ -9,6 +9,7 @@
             [frontend.handler.db-based.page :as db-page-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
+            [frontend.handler.publish :as publish-handler]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -97,6 +98,10 @@
                                                                                  :export-type :page}))
                                    {:class "w-auto md:max-w-4xl max-h-[80vh] overflow-y-auto"})}})
 
+          (when (and page (not config/publishing?))
+            {:title   "Publish page"
+             :options {:on-click #(publish-handler/publish-page! page)}})
+
           (when (util/electron?)
             {:title   (t (if public? :page/make-private :page/make-public))
              :options {:on-click

+ 55 - 0
src/main/frontend/handler/publish.cljs

@@ -0,0 +1,55 @@
+(ns frontend.handler.publish
+  "Prepare publish payloads for pages."
+  (:require [datascript.core :as d]
+            [frontend.db :as db]
+            [frontend.handler.notification :as notification]
+            [frontend.state :as state]
+            [logseq.db :as ldb]
+            [logseq.db.common.entity-util :as entity-util]
+            [logseq.db.frontend.schema :as db-schema]))
+
+(defn- datom->vec
+  [datom]
+  [(:e datom) (:a datom) (:v datom) (:tx datom) (:added datom)])
+
+(defn- collect-page-eids
+  [db page-entity]
+  (let [page-id (:db/id page-entity)
+        blocks (ldb/get-page-blocks db page-id)
+        block-eids (map :db/id blocks)
+        ref-eids (->> blocks (mapcat :block/refs) (keep :db/id))
+        tag-eids (->> blocks (mapcat :block/tags) (keep :db/id))
+        page-eids (->> blocks (map :block/page) (keep :db/id))]
+    {:blocks blocks
+     :eids (->> (concat [page-id] block-eids ref-eids tag-eids page-eids)
+                (remove nil?)
+                distinct)}))
+
+(defn build-page-publish-datoms
+  "Builds a datom snapshot for a single page.
+
+  References/backlinks are intentionally ignored at this stage.
+  "
+  [db page-entity]
+  (let [{:keys [blocks eids]} (collect-page-eids db page-entity)
+        datoms (mapcat (fn [eid]
+                         (map datom->vec (d/datoms db :eavt eid)))
+                       eids)]
+    {:page (entity-util/entity->map page-entity)
+     :page-id (:db/id page-entity)
+     :block-count (count blocks)
+     :schema-version (db-schema/schema-version->string db-schema/version)
+     :datoms (vec datoms)}))
+
+(defn publish-page!
+  "Prepares the publish payload for a page. The upload step is stubbed for now."
+  [page]
+  (let [repo (state/get-current-repo)]
+    (if-let [db* (and repo (db/get-db repo))]
+      (if (and page (:db/id page))
+        (let [payload (build-page-publish-datoms db* page)]
+          (notification/show! "Publish payload prepared." :success)
+          (js/console.log "Publish payload" (clj->js payload))
+          payload)
+        (notification/show! "Publish failed: invalid page." :error))
+      (notification/show! "Publish failed: missing database." :error))))