From 570a087f53cbc572c40a1fe079b2ac56a5da1d9d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Sat, 4 Apr 2026 17:05:12 +0200 Subject: [PATCH] refactor: move TodoMVC example into example/ directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - example/todomvc/pocketbook/todomvc.cljs — app source - example/todomvc/todomvc.html — page - example/todomvc/js/ — compiled output (gitignored) Server static file serving is now filesystem-based via --static-dir flag instead of classpath resources. No static dir by default (API only); bb server passes --static-dir example/todomvc. Removed resources/public/ — library has no bundled static assets. --- .gitignore | 1 + bb.edn | 4 +- build.edn | 4 +- deps.edn | 8 +-- .../todomvc}/pocketbook/todomvc.cljs | 0 .../public => example/todomvc}/todomvc.html | 0 src/pocketbook/server.clj | 51 +++++++++++-------- test/pocketbook/server_test.clj | 5 +- 8 files changed, 42 insertions(+), 31 deletions(-) rename {src => example/todomvc}/pocketbook/todomvc.cljs (100%) rename {resources/public => example/todomvc}/todomvc.html (100%) diff --git a/.gitignore b/.gitignore index cb2a52c..022dc5a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ out/ resources/public/js/ +example/todomvc/js/ *.db *.db-shm *.db-wal diff --git a/bb.edn b/bb.edn index 72f088c..24dab2f 100644 --- a/bb.edn +++ b/bb.edn @@ -1,6 +1,6 @@ {:tasks - {server {:doc "Start the Pocketbook sync server" - :task (shell "clj" "-M:server")} + {server {:doc "Start the Pocketbook sync server (serves example app)" + :task (shell "clj" "-M:server" "--static-dir" "example/todomvc")} cljs {:doc "Compile ClojureScript (one-shot)" :task (shell "clj" "-M:cljs")} diff --git a/build.edn b/build.edn index ab3ccb9..d435160 100644 --- a/build.edn +++ b/build.edn @@ -1,5 +1,5 @@ {:main pocketbook.todomvc - :output-to "resources/public/js/main.js" - :output-dir "resources/public/js/out" + :output-to "example/todomvc/js/main.js" + :output-dir "example/todomvc/js/out" :asset-path "js/out" :optimizations :none} diff --git a/deps.edn b/deps.edn index 91f087c..78c499f 100644 --- a/deps.edn +++ b/deps.edn @@ -1,4 +1,4 @@ -{:paths ["src" "resources"] +{:paths ["src"] :deps {org.clojure/clojure {:mvn/version "1.12.0"} http-kit/http-kit {:mvn/version "2.8.0"} com.cognitect/transit-clj {:mvn/version "1.0.333"} @@ -15,12 +15,14 @@ :server {:main-opts ["-m" "pocketbook.server"]} ;; ClojureScript client build - :cljs {:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.132"} + :cljs {:extra-paths ["example/todomvc"] + :extra-deps {org.clojure/clojurescript {:mvn/version "1.11.132"} com.cognitect/transit-cljs {:mvn/version "0.8.280"} org.clojure/core.async {:mvn/version "1.7.701"}} :main-opts ["-m" "cljs.main" "-co" "build.edn" "-c"]} - :cljs-dev {:extra-deps {org.clojure/clojurescript {:mvn/version "1.11.132"} + :cljs-dev {:extra-paths ["example/todomvc"] + :extra-deps {org.clojure/clojurescript {:mvn/version "1.11.132"} com.cognitect/transit-cljs {:mvn/version "0.8.280"} org.clojure/core.async {:mvn/version "1.7.701"}} :main-opts ["-m" "cljs.main" "-co" "build.edn" "-w" "src" "-c"]}}} diff --git a/src/pocketbook/todomvc.cljs b/example/todomvc/pocketbook/todomvc.cljs similarity index 100% rename from src/pocketbook/todomvc.cljs rename to example/todomvc/pocketbook/todomvc.cljs diff --git a/resources/public/todomvc.html b/example/todomvc/todomvc.html similarity index 100% rename from resources/public/todomvc.html rename to example/todomvc/todomvc.html diff --git a/src/pocketbook/server.clj b/src/pocketbook/server.clj index eaab522..3f710a9 100644 --- a/src/pocketbook/server.clj +++ b/src/pocketbook/server.clj @@ -20,10 +20,11 @@ ;; --------------------------------------------------------------------------- (def default-config - {:port 8090 - :db-path "pocketbook.db" - :users nil ;; nil = no auth, or {"alice" {:token "abc" :groups #{"todo"}}} - :cors true}) + {:port 8090 + :db-path "pocketbook.db" + :static-dir nil ;; nil = no static serving, or path like "example/todomvc" + :users nil ;; nil = no auth, or {"alice" {:token "abc" :groups #{"todo"}}} + :cors true}) ;; --------------------------------------------------------------------------- ;; Auth @@ -135,15 +136,19 @@ (subs path (inc i))))) (defn- serve-static - "Attempt to serve a static file from resources/public. Returns response or nil." - [uri] - (let [path (str "public" (if (= "/" uri) "/todomvc.html" uri)) - resource (io/resource path)] - (when resource - {:status 200 - :headers {"Content-Type" (get content-types (ext path) "application/octet-stream") - "Cache-Control" "no-cache"} - :body (io/input-stream resource)}))) + "Attempt to serve a static file from a directory. Returns response or nil." + [static-dir uri] + (when static-dir + (let [rel (if (= "/" uri) "/todomvc.html" uri) + file (io/file static-dir (subs rel 1))] + (when (and (.isFile file) (.canRead file) + ;; Prevent path traversal + (.startsWith (.toPath file) (.toPath (io/file static-dir)))) + {:status 200 + :headers {"Content-Type" (get content-types (ext (.getName file)) + "application/octet-stream") + "Cache-Control" "no-cache"} + :body (io/input-stream file)})))) ;; --------------------------------------------------------------------------- ;; Ring handler @@ -171,7 +176,7 @@ ;; Static files (including / → todomvc.html) :else - (or (serve-static (:uri req)) + (or (serve-static (:static-dir config) (:uri req)) {:status 404 :headers {"Content-Type" "text/plain"} :body "Not found"}))] @@ -195,7 +200,9 @@ (println (str "🔶 Pocketbook server running on http://localhost:" (:port config))) (println (str " Database: " (:db-path config))) (println (str " Auth: " (if (:users config) "enabled" "disabled"))) - (println (str " TodoMVC: http://localhost:" (:port config) "/todomvc.html")) + (when (:static-dir config) + (println (str " Static: " (:static-dir config))) + (println (str " App: http://localhost:" (:port config) "/"))) {:stop server :ds ds :config config}))) @@ -210,11 +217,13 @@ ;; --------------------------------------------------------------------------- (defn -main [& args] - (let [port (some-> (first args) parse-long) - db-path (second args) - config (cond-> {} - port (assoc :port port) - db-path (assoc :db-path db-path))] + (let [opts (apply hash-map (map #(if (str/starts-with? % "--") + (keyword (subs % 2)) + %) + args)) + config (cond-> {} + (:port opts) (assoc :port (parse-long (:port opts))) + (:db-path opts) (assoc :db-path (:db-path opts)) + (:static-dir opts) (assoc :static-dir (:static-dir opts)))] (start! config) - ;; Keep the server running @(promise))) diff --git a/test/pocketbook/server_test.clj b/test/pocketbook/server_test.clj index 40e9523..8c84c3e 100644 --- a/test/pocketbook/server_test.clj +++ b/test/pocketbook/server_test.clj @@ -61,14 +61,13 @@ ;; Tests ;; --------------------------------------------------------------------------- -(deftest serves-static-root +(deftest root-returns-404-without-static-dir (let [req (-> (HttpRequest/newBuilder) (.uri (URI. (url "/"))) (.GET) (.build)) resp (.send client req (HttpResponse$BodyHandlers/ofString))] - (is (= 200 (.statusCode resp))) - (is (.contains (.body resp) "TodoMVC")))) + (is (= 404 (.statusCode resp))))) (deftest push-and-pull (testing "Push new documents"