sx-pub Phase 2: publish to IPFS, browse collections, resolve by path + CID
New endpoints: - POST /pub/publish — pin SX content to IPFS, store path→CID in DB - GET /pub/browse/<collection> — list published documents - GET /pub/doc/<collection>/<slug> — resolve path to CID, fetch from IPFS - GET /pub/cid/<cid> — direct CID fetch (immutable, cache forever) New helpers: pub-publish, pub-collection-items, pub-resolve-document, pub-fetch-cid Tested: published stdlib.sx (6.9KB) → QmQQyR3Ltqi5sFiwZh5dutPbAM4QsEBnw419RyNnTj4fFM Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
;; ==========================================================================
|
||||
;; sx-pub Phase 1 API endpoints — actor, webfinger, collections, status
|
||||
;; sx-pub API endpoints — actor, webfinger, collections, publishing, browsing
|
||||
;;
|
||||
;; All responses are text/sx. No JSON.
|
||||
;; ==========================================================================
|
||||
@@ -96,3 +96,122 @@
|
||||
"\n :ipfs \"" (get status "ipfs") "\""
|
||||
"\n :actor \"" (get status "actor") "\""
|
||||
"\n :domain \"" (or (get status "domain") "unknown") "\")"))))
|
||||
|
||||
|
||||
;; ==========================================================================
|
||||
;; Phase 2: Publishing + Browsing
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Publish
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler pub-publish
|
||||
:path "/pub/publish"
|
||||
:method :post
|
||||
:csrf false
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((collection (helper "request-form" "collection" ""))
|
||||
(slug (helper "request-form" "slug" ""))
|
||||
(content (helper "request-form" "content" ""))
|
||||
(title (helper "request-form" "title" ""))
|
||||
(summary (helper "request-form" "summary" "")))
|
||||
(if (or (= collection "") (= slug "") (= content ""))
|
||||
(do
|
||||
(set-response-status 400)
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
"(Error :message \"Missing collection, slug, or content\")")
|
||||
(let ((result (helper "pub-publish" collection slug content title summary)))
|
||||
(if (get result "error")
|
||||
(do
|
||||
(set-response-status 500)
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(str "(Error :message \"" (get result "error") "\")"))
|
||||
(do
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(str
|
||||
"(Published"
|
||||
"\n :path \"" (get result "path") "\""
|
||||
"\n :cid \"" (get result "cid") "\""
|
||||
"\n :hash \"" (get result "hash") "\""
|
||||
"\n :size " (get result "size")
|
||||
"\n :collection \"" (get result "collection") "\""
|
||||
"\n :slug \"" (get result "slug") "\""
|
||||
"\n :title \"" (get result "title") "\")")))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Browse collection
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler pub-browse-collection
|
||||
:path "/pub/browse/<collection_slug>"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key collection_slug)
|
||||
(let ((data (helper "pub-collection-items" collection_slug)))
|
||||
(if (get data "error")
|
||||
(do
|
||||
(set-response-status 404)
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(str "(Error :message \"" (get data "error") "\")"))
|
||||
(do
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(let ((items (map (fn (d)
|
||||
(str "\n (SxDocument"
|
||||
" :slug \"" (get d "slug") "\""
|
||||
" :title \"" (get d "title") "\""
|
||||
" :summary \"" (get d "summary") "\""
|
||||
" :cid \"" (get d "cid") "\""
|
||||
" :size " (get d "size") ")"))
|
||||
(get data "items"))))
|
||||
(str
|
||||
"(SxCollection"
|
||||
"\n :slug \"" (get data "collection") "\""
|
||||
"\n :name \"" (get data "name") "\""
|
||||
"\n :description \"" (get data "description") "\""
|
||||
(join "" items) ")"))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Resolve document by path
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler pub-document
|
||||
:path "/pub/doc/<collection_slug>/<slug>"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key collection_slug slug)
|
||||
(let ((data (helper "pub-resolve-document" collection_slug slug)))
|
||||
(if (get data "error")
|
||||
(do
|
||||
(set-response-status 404)
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(str "(Error :message \"" (get data "error") "\")"))
|
||||
(do
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(set-response-header "X-IPFS-CID" (get data "cid"))
|
||||
(get data "content")))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Direct CID fetch
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler pub-cid
|
||||
:path "/pub/cid/<cid>"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key cid)
|
||||
(let ((data (helper "pub-fetch-cid" cid)))
|
||||
(if (get data "error")
|
||||
(do
|
||||
(set-response-status 404)
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(str "(Error :message \"" (get data "error") "\")"))
|
||||
(do
|
||||
(set-response-header "Content-Type" "text/sx; charset=utf-8")
|
||||
(set-response-header "Cache-Control" "public, max-age=31536000, immutable")
|
||||
(get data "content")))))
|
||||
|
||||
Reference in New Issue
Block a user