diff options
author | Miguel Ángel Moreno <mail@migalmoreno.com> | 2024-12-19 02:21:55 +0100 |
---|---|---|
committer | Miguel Ángel Moreno <mail@migalmoreno.com> | 2024-12-19 02:21:55 +0100 |
commit | 62d5d995345fa4ae8814f76cfa4f3eb930a7d9a1 (patch) | |
tree | 354779ed86601126f6342e271cf2142643905d4a | |
parent | 13e285955508e601d6a648c25e29632340b4d9b6 (diff) |
refactor: make routing shared between frontend and backend
-rw-r--r-- | deps.edn | 4 | ||||
-rw-r--r-- | src/backend/tubo/http.clj | 4 | ||||
-rw-r--r-- | src/backend/tubo/router.clj | 84 | ||||
-rw-r--r-- | src/backend/tubo/routes.clj | 64 | ||||
-rw-r--r-- | src/frontend/tubo/core.cljs | 4 | ||||
-rw-r--r-- | src/frontend/tubo/router.cljs | 93 | ||||
-rw-r--r-- | src/frontend/tubo/routes.cljs | 76 | ||||
-rw-r--r-- | src/shared/tubo/routes.cljc | 30 |
8 files changed, 214 insertions, 145 deletions
@@ -5,12 +5,14 @@ metosin/reitit-ring {:mvn/version "0.5.18"} metosin/reitit-middleware {:mvn/version "0.5.18"} metosin/reitit-malli {:mvn/version "0.5.18"} + metosin/reitit-swagger {:mvn/version "0.5.18"} + metosin/reitit-swagger-ui {:mvn/version "0.5.18"} ring/ring {:mvn/version "1.9.5"} ring/ring-json {:mvn/version "0.5.1"} org.clojure/java.data {:mvn/version "1.0.95"} hiccup/hiccup {:mvn/version "1.0.5"} ring-cors/ring-cors {:mvn/version "0.1.13"}} - :paths ["src/backend" "resources" "classes"] + :paths ["src/backend" "src/shared" "resources" "classes"] :mvn/repos {"jitpack" {:url "https://jitpack.io"}} :aliases {:build {:deps {io.github.clojure/tools.build {:mvn/version "0.9.4"}} diff --git a/src/backend/tubo/http.clj b/src/backend/tubo/http.clj index 200b52e..95913d6 100644 --- a/src/backend/tubo/http.clj +++ b/src/backend/tubo/http.clj @@ -1,7 +1,7 @@ (ns tubo.http (:require [org.httpkit.server :refer [run-server]] - [tubo.routes :as routes]) + [tubo.router :as router]) (:import tubo.DownloaderImpl org.schabi.newpipe.extractor.NewPipe @@ -13,7 +13,7 @@ ([] (start-server! 3000)) ([port] (NewPipe/init (DownloaderImpl/init) (Localization. "en" "US")) - (reset! server (run-server #'routes/app {:port port})) + (reset! server (run-server #'router/app {:port port})) (println "Server running in port" port))) (defn stop-server! [] (when @server (@server :timeout 100) (reset! server nil))) diff --git a/src/backend/tubo/router.clj b/src/backend/tubo/router.clj new file mode 100644 index 0000000..8a031b5 --- /dev/null +++ b/src/backend/tubo/router.clj @@ -0,0 +1,84 @@ +(ns tubo.router + (:require + [reitit.core :as r] + [reitit.ring :as ring] + [reitit.ring.coercion :as rrc] + [reitit.coercion.malli] + [reitit.swagger :as swagger] + [reitit.swagger-ui :as swagger-ui] + [ring.middleware.reload :refer [wrap-reload]] + [ring.middleware.params :refer [wrap-params]] + [ring.middleware.json :refer [wrap-json-response]] + [tubo.handler :as handler] + [tubo.routes :as routes])) + +(defn expand-routes + [data opts] + (if (keyword? data) + (case data + :api/services {:get {:summary "returns all supported services" + :handler handler/services}} + :api/search {:get {:summary + "returns search results for a given service" + :coercion reitit.coercion.malli/coercion + :parameters {:path {:service-id int?} + :query {:q string?}} + :handler handler/search}} + :api/default-kiosk {:get + {:summary + "returns default kiosk entries for a given service" + :coercion reitit.coercion.malli/coercion + :parameters {:path {:service-id int?}} + :handler handler/kiosk}} + :api/all-kiosks {:get {:summary + "returns all kiosks supported by a given service" + :coercion reitit.coercion.malli/coercion + :parameters {:path {:service-id int?}} + :handler handler/kiosks}} + :api/kiosk {:get + {:summary + "returns kiosk entries for a given service and a kiosk ID" + :coercion reitit.coercion.malli/coercion + :parameters {:path {:service-id int? :kiosk-id string?}} + :handler handler/kiosk}} + :api/stream {:get {:summary "returns stream data for a given URL" + :handler handler/stream}} + :api/channel {:get {:summary "returns channel data for a given URL" + :handler handler/channel}} + :api/channel-tab {:get + {:summary + "returns channel tab data for a given URL and a tab ID" + :handler handler/channel-tabs}} + :api/playlist {:get {:summary "returns playlist data for a given URL" + :handler handler/playlist}} + :api/comments {:get {:summary "returns comments data for a given URL" + :handler handler/comments}} + :api/swagger-spec {:no-doc true + :get {:swagger {:info {:title "Tubo API"} + :basePath "/"} + :handler (swagger/create-swagger-handler)}} + :api/swagger-ui {:no-doc true + :get (swagger-ui/create-swagger-ui-handler)} + {:no-doc true + :handler handler/index}) + (r/expand data opts))) + +(def router + (ring/router + routes/routes + {:expand expand-routes + :data {:middleware [rrc/coerce-request-middleware + rrc/coerce-response-middleware + rrc/coerce-exceptions-middleware]}})) + +(def app + (ring/ring-handler + router + (ring/routes + (ring/create-resource-handler {:path "/"}) + (ring/redirect-trailing-slash-handler {:method :add}) + (ring/create-default-handler + {:not-found (constantly {:status 404 :body "Not found"})})) + {:middleware [wrap-params + [wrap-json-response {:pretty true}] + wrap-reload]})) diff --git a/src/backend/tubo/routes.clj b/src/backend/tubo/routes.clj deleted file mode 100644 index e8dd536..0000000 --- a/src/backend/tubo/routes.clj +++ /dev/null @@ -1,64 +0,0 @@ -(ns tubo.routes - (:require - [reitit.ring :as ring] - [reitit.ring.coercion :as rrc] - [reitit.coercion.malli] - [ring.middleware.reload :refer [wrap-reload]] - [ring.middleware.params :refer [wrap-params]] - [ring.middleware.json :refer [wrap-json-response]] - [tubo.handler :as handler])) - -(def router - (ring/router - [["/" handler/index] - ["/search" handler/index] - ["/stream" handler/index] - ["/channel" handler/index] - ["/playlist" handler/index] - ["/kiosk" handler/index] - ["/settings" handler/index] - ["/bookmark" handler/index] - ["/bookmarks" handler/index] - ["/api/v1" - ["/services" - ["" {:get handler/services}] - ["/:service-id/search" - {:get {:coercion reitit.coercion.malli/coercion - :parameters {:path {:service-id int?} - :query {:q string?}} - :handler handler/search}}] - ["/:service-id" - ["/default-kiosk" - {:get {:coercion reitit.coercion.malli/coercion - :parameters {:path {:service-id int?}} - :handler handler/kiosk}}] - ["/kiosks" - ["" - {:get {:coercion reitit.coercion.malli/coercion - :parameters {:path {:service-id int?}} - :handler handler/kiosks}}] - ["/:kiosk-id" - {:get {:coercion reitit.coercion.malli/coercion - :parameters {:path {:service-id int? :kiosk-id string?}} - :handler handler/kiosk}}]]]] - ["/streams/:url" {:get handler/stream}] - ["/channels" - ["/:url" - ["" {:get handler/channel}] - ["/tabs/:tab-id" {:get handler/channel-tabs}]]] - ["/playlists/:url" {:get handler/playlist}] - ["/comments/:url" {:get handler/comments}]]] - {:data {:middleware [rrc/coerce-request-middleware - rrc/coerce-response-middleware - rrc/coerce-exceptions-middleware]}})) - -(def app - (ring/ring-handler - router - (ring/routes - (ring/create-resource-handler {:path "/"}) - (ring/create-default-handler - {:not-found (constantly {:status 404 :body "Not found"})})) - {:middleware [wrap-params - [wrap-json-response {:pretty true}] - wrap-reload]})) diff --git a/src/frontend/tubo/core.cljs b/src/frontend/tubo/core.cljs index c9f7e7c..4f8762f 100644 --- a/src/frontend/tubo/core.cljs +++ b/src/frontend/tubo/core.cljs @@ -4,7 +4,7 @@ [reagent.core :as r] [re-frame.core :as rf] [tubo.events] - [tubo.routes :as routes] + [tubo.router :as router] [tubo.subs] [tubo.views :as views])) @@ -13,7 +13,7 @@ (defn ^:dev/after-load mount-root [] (rf/clear-subscription-cache!) - (routes/start-routes!) + (router/start-router!) (.render root (r/as-element [(fn [] views/app)]))) (defn ^:export init [] (rf/dispatch-sync [:initialize-db]) (mount-root)) diff --git a/src/frontend/tubo/router.cljs b/src/frontend/tubo/router.cljs new file mode 100644 index 0000000..3e51171 --- /dev/null +++ b/src/frontend/tubo/router.cljs @@ -0,0 +1,93 @@ +(ns tubo.router + (:require + [reitit.core :as r] + [reitit.frontend :as ref] + [reitit.frontend.easy :as rfe] + [re-frame.core :as rf] + [tubo.bookmarks.views :as bookmarks] + [tubo.channel.views :as channel] + [tubo.kiosks.views :as kiosk] + [tubo.playlist.views :as playlist] + [tubo.routes :as routes] + [tubo.search.views :as search] + [tubo.settings.views :as settings] + [tubo.stream.views :as stream])) + +(defn expand-routes + [data opts] + (if (keyword? data) + (case data + :web/homepage {:view kiosk/kiosk + :name :homepage + :controllers [{:start #(rf/dispatch [:fetch-homepage])}]} + :web/search {:view search/search + :name :search-page + :controllers [{:parameters {:query [:q :serviceId]} + :start (fn [{{:keys [serviceId q]} + :query}] + (rf/dispatch + [:search/fetch-page + serviceId + q])) + :stop #(rf/dispatch + [:search/show-form false])}]} + :web/stream {:view stream/stream + :name :stream-page + :controllers [{:parameters {:query [:url]} + :start (fn [{{:keys [url]} :query}] + (rf/dispatch + [:stream/fetch-page + url]))}]} + :web/channel {:view channel/channel + :name :channel-page + :controllers [{:parameters {:query [:url]} + :start (fn [{{:keys [url]} :query}] + (rf/dispatch + [:channel/fetch-page + url]))}]} + :web/playlist {:view playlist/playlist + :name :playlist-page + :controllers [{:parameters {:query [:url]} + :start (fn [{{:keys [url]} :query}] + (rf/dispatch + [:playlist/fetch-page + url]))}]} + :web/kiosk {:view kiosk/kiosk + :name :kiosk-page + :controllers [{:parameters {:query [:kioskId :serviceId]} + :start (fn [{{:keys [serviceId + kioskId]} + :query}] + (rf/dispatch + [:kiosks/fetch-page + serviceId + kioskId]))}]} + :web/settings {:view settings/settings + :name :settings-page + :controllers [{:start #(rf/dispatch + [:settings/fetch-page])}]} + :web/bookmark {:view bookmarks/bookmark + :name :bookmark-page + :controllers [{:parameters {:query [:id]} + :start (fn [{{:keys [id]} :query}] + (rf/dispatch + [:bookmark/fetch-page + id]))}]} + :web/bookmarks {:view bookmarks/bookmarks + :name :bookmarks-page + :controllers [{:start #(rf/dispatch + [:bookmarks/fetch-page])}]} + nil) + (r/expand data opts))) + +(def router + (ref/router routes/routes {:expand expand-routes})) + +(defn on-navigate + [new-match] + (when new-match + (rf/dispatch [:navigation/navigated new-match]))) + +(defn start-router! + [] + (rfe/start! router on-navigate {:use-fragment false})) diff --git a/src/frontend/tubo/routes.cljs b/src/frontend/tubo/routes.cljs deleted file mode 100644 index 8e72d09..0000000 --- a/src/frontend/tubo/routes.cljs +++ /dev/null @@ -1,76 +0,0 @@ -(ns tubo.routes - (:require - [reitit.frontend :as ref] - [reitit.frontend.easy :as rfe] - [re-frame.core :as rf] - [tubo.channel.views :as channel] - [tubo.kiosks.views :as kiosk] - [tubo.playlist.views :as playlist] - [tubo.bookmarks.views :as bookmarks] - [tubo.search.views :as search] - [tubo.settings.views :as settings] - [tubo.stream.views :as stream])) - -(def router - (ref/router - [["/" - {:view kiosk/kiosk - :name :homepage - :controllers [{:start #(rf/dispatch [:fetch-homepage])}]}] - ["/search" - {:view search/search - :name :search-page - :controllers [{:parameters {:query [:q :serviceId]} - :start (fn [{{:keys [serviceId q]} :query}] - (rf/dispatch [:search/fetch-page serviceId - q])) - :stop #(rf/dispatch [:search/show-form false])}]}] - ["/stream" - {:view stream/stream - :name :stream-page - :controllers [{:parameters {:query [:url]} - :start (fn [{{:keys [url]} :query}] - (rf/dispatch [:stream/fetch-page url]))}]}] - ["/channel" - {:view channel/channel - :name :channel-page - :controllers [{:parameters {:query [:url]} - :start (fn [{{:keys [url]} :query}] - (rf/dispatch [:channel/fetch-page url])) - :stop #(rf/dispatch [:channel/reset])}]}] - ["/playlist" - {:view playlist/playlist - :name :playlist-page - :controllers [{:parameters {:query [:url]} - :start (fn [{{:keys [url]} :query}] - (rf/dispatch [:playlist/fetch-page url]))}]}] - ["/kiosk" - {:view kiosk/kiosk - :name :kiosk-page - :controllers [{:parameters {:query [:kioskId :serviceId]} - :start (fn [{{:keys [serviceId kioskId]} :query}] - (rf/dispatch [:kiosks/fetch-page serviceId - kioskId]))}]}] - ["/settings" - {:view settings/settings - :name :settings-page - :controllers [{:start #(rf/dispatch [:settings/fetch-page])}]}] - ["/bookmark" - {:view bookmarks/bookmark - :name :bookmark-page - :controllers [{:parameters {:query [:id]} - :start (fn [{{:keys [id]} :query}] - (rf/dispatch [:bookmark/fetch-page id]))}]}] - ["/bookmarks" - {:view bookmarks/bookmarks - :name :bookmarks-page - :controllers [{:start #(rf/dispatch [:bookmarks/fetch-page])}]}]])) - -(defn on-navigate - [new-match] - (when new-match - (rf/dispatch [:navigation/navigated new-match]))) - -(defn start-routes! - [] - (rfe/start! router on-navigate {:use-fragment false})) diff --git a/src/shared/tubo/routes.cljc b/src/shared/tubo/routes.cljc new file mode 100644 index 0000000..f67c547 --- /dev/null +++ b/src/shared/tubo/routes.cljc @@ -0,0 +1,30 @@ +(ns tubo.routes) + +(def routes + [["/" :web/homepage] + ["/search" :web/search] + ["/stream" :web/stream] + ["/channel" :web/channel] + ["/playlist" :web/playlist] + ["/kiosk" :web/kiosk] + ["/settings" :web/settings] + ["/bookmark" :web/bookmark] + ["/bookmarks" :web/bookmarks] + ["/swagger.json" :api/swagger-spec] + ["/api-docs/*" :api/swagger-ui] + ["/api/v1" + ["/services" + ["" :api/services] + ["/:service-id/search" :api/search] + ["/:service-id" + ["/default-kiosk" :api/default-kiosk] + ["/kiosks" + ["" :api/all-kiosks] + ["/:kiosk-id" :api/kiosk]]]] + ["/streams/:url" :api/stream] + ["/channels" + ["/:url" + ["" :api/channel] + ["/tabs/:tab-id" :api/channel-tab]]] + ["/playlists/:url" :api/playlist] + ["/comments/:url" :api/comments]]]) |