mirror of
https://github.com/metosin/reitit.git
synced 2025-12-16 16:01:11 +00:00
Add frontend auth example
This commit is contained in:
parent
71e83818b1
commit
51d24007e7
8 changed files with 240 additions and 0 deletions
13
examples/frontend-auth/README.md
Normal file
13
examples/frontend-auth/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# reitit-frontend + controllers example
|
||||
|
||||
## Usage
|
||||
|
||||
```clj
|
||||
> lein figwheel
|
||||
```
|
||||
|
||||
Go with browser to http://localhost:3449
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2018 Metosin Oy
|
||||
1
examples/frontend-auth/checkouts/reitit-core
Symbolic link
1
examples/frontend-auth/checkouts/reitit-core
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../modules/reitit-core
|
||||
1
examples/frontend-auth/checkouts/reitit-frontend
Symbolic link
1
examples/frontend-auth/checkouts/reitit-frontend
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../modules/reitit-frontend
|
||||
1
examples/frontend-auth/checkouts/reitit-schema
Symbolic link
1
examples/frontend-auth/checkouts/reitit-schema
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../modules/reitit-schema
|
||||
54
examples/frontend-auth/project.clj
Normal file
54
examples/frontend-auth/project.clj
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
(defproject frontend "0.1.0-SNAPSHOT"
|
||||
:description "FIXME: write description"
|
||||
:url "http://example.com/FIXME"
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||
[ring-server "0.5.0"]
|
||||
[reagent "0.8.1"]
|
||||
[ring "1.7.1"]
|
||||
[compojure "1.6.1"]
|
||||
[hiccup "1.0.5"]
|
||||
[org.clojure/clojurescript "1.10.439"]
|
||||
[metosin/reitit "0.2.13"]
|
||||
[metosin/reitit-schema "0.2.13"]
|
||||
[metosin/reitit-frontend "0.2.13"]
|
||||
;; Just for pretty printting the match
|
||||
[fipp "0.6.14"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-figwheel "0.5.18"]]
|
||||
|
||||
:source-paths []
|
||||
:resource-paths ["resources" "target/cljsbuild"]
|
||||
|
||||
:profiles {:dev {:dependencies [[binaryage/devtools "0.9.10"]]}}
|
||||
|
||||
:cljsbuild
|
||||
{:builds
|
||||
[{:id "app"
|
||||
:figwheel true
|
||||
:source-paths ["src"]
|
||||
:watch-paths ["src" "checkouts/reitit-frontend/src"]
|
||||
:compiler {:main "frontend.core"
|
||||
:asset-path "/js/out"
|
||||
:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js/out"
|
||||
:source-map true
|
||||
:optimizations :none
|
||||
:pretty-print true
|
||||
:preloads [devtools.preload]}}
|
||||
{:id "min"
|
||||
:source-paths ["src"]
|
||||
:compiler {:output-to "target/cljsbuild/public/js/app.js"
|
||||
:output-dir "target/cljsbuild/public/js"
|
||||
:source-map "target/cljsbuild/public/js/app.js.map"
|
||||
:optimizations :advanced
|
||||
:pretty-print false}}]}
|
||||
|
||||
:figwheel {:http-server-root "public"
|
||||
:server-port 3449
|
||||
:nrepl-port 7002
|
||||
;; Server index.html for all routes for HTML5 routing
|
||||
:ring-handler backend.server/handler})
|
||||
10
examples/frontend-auth/resources/public/index.html
Normal file
10
examples/frontend-auth/resources/public/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Reitit frontend example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
11
examples/frontend-auth/src/backend/server.clj
Normal file
11
examples/frontend-auth/src/backend/server.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
(ns backend.server
|
||||
(:require [clojure.java.io :as io]
|
||||
[ring.util.response :as resp]
|
||||
[ring.middleware.content-type :as content-type]))
|
||||
|
||||
(def handler
|
||||
(-> (fn [request]
|
||||
(or (resp/resource-response (:uri request) {:root "public"})
|
||||
(-> (resp/resource-response "index.html" {:root "public"})
|
||||
(resp/content-type "text/html"))))
|
||||
content-type/wrap-content-type))
|
||||
149
examples/frontend-auth/src/frontend/core.cljs
Normal file
149
examples/frontend-auth/src/frontend/core.cljs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
(ns frontend.core
|
||||
(:require [reagent.core :as r]
|
||||
[reitit.frontend :as rf]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[reitit.frontend.controllers :as rfc]
|
||||
[reitit.coercion :as rc]
|
||||
[reitit.coercion.schema :as rsc]
|
||||
[schema.core :as s]
|
||||
[fipp.edn :as fedn]))
|
||||
|
||||
(defonce state (r/atom {:user nil}))
|
||||
|
||||
(defn home-page []
|
||||
[:div
|
||||
[:h2 "Welcome to frontend"]
|
||||
[:p "Look at console log for controller calls."]])
|
||||
|
||||
(defn item-page [match]
|
||||
(let [{:keys [path query]} (:parameters match)
|
||||
{:keys [id]} path]
|
||||
[:div
|
||||
[:ul
|
||||
[:li [:a {:href (rfe/href ::item {:id 1})} "Item 1"]]
|
||||
[:li [:a {:href (rfe/href ::item {:id 2} {:foo "bar"})} "Item 2"]]]
|
||||
(if id
|
||||
[:h2 "Selected item " id])
|
||||
(if (:foo query)
|
||||
[:p "Optional foo query param: " (:foo query)])]))
|
||||
|
||||
(defn login-done [user]
|
||||
(swap! state assoc :user user))
|
||||
|
||||
(defn login [user]
|
||||
;; In real app one would send API call here to create session or retrieve token or something
|
||||
;; and the callback would update app-state
|
||||
(js/setTimeout #(login-done user) 250))
|
||||
|
||||
(defn logout []
|
||||
(swap! state assoc :user nil))
|
||||
|
||||
(defn login-view []
|
||||
(let [form (r/atom {})]
|
||||
(fn []
|
||||
[:div
|
||||
[:form
|
||||
{:on-submit (fn [e]
|
||||
(.preventDefault e)
|
||||
(if (and (:username @form)
|
||||
(:password @form))
|
||||
(login @form)))}
|
||||
|
||||
[:label "Username"]
|
||||
[:input
|
||||
{:default-value ""
|
||||
:on-change #(swap! form assoc :username %)}]
|
||||
|
||||
[:label "Password"]
|
||||
[:input
|
||||
{:default-value ""
|
||||
:on-change #(swap! form assoc :password %)}]
|
||||
|
||||
[:button
|
||||
{:type "submit"}
|
||||
"Login"]]])))
|
||||
|
||||
(defn about-page []
|
||||
[:div
|
||||
[:p "This view is public."]])
|
||||
|
||||
(defn main-view []
|
||||
(let [{:keys [user match]} @state
|
||||
route-data (:data match)]
|
||||
[:div
|
||||
[:ul
|
||||
[:li [:a {:href (rfe/href ::frontpage)} "Frontpage"]]
|
||||
[:li [:a {:href (rfe/href ::about)} "About (public)"]]
|
||||
[:li [:a {:href (rfe/href ::item-list)} "Item list"]]
|
||||
(if user
|
||||
[:li [:a {:on-click (fn [e]
|
||||
(.preventDefault e)
|
||||
(logout))
|
||||
:href "#"}
|
||||
"Logout"]])]
|
||||
;; If user is authenticated
|
||||
;; or if this route has been defined as public, else login view
|
||||
(if (or user
|
||||
(:public? route-data))
|
||||
(if match
|
||||
(let [view (:view route-data)]
|
||||
[view match]))
|
||||
[login-view])
|
||||
[:pre (with-out-str (fedn/pprint @state))]]))
|
||||
|
||||
(defn log-fn [& params]
|
||||
(fn [_]
|
||||
(apply js/console.log params)))
|
||||
|
||||
(def routes
|
||||
(rf/router
|
||||
["/"
|
||||
[""
|
||||
{:name ::frontpage
|
||||
:view home-page
|
||||
:controllers [{:start (log-fn "start" "frontpage controller")
|
||||
:stop (log-fn "stop" "frontpage controller")}]}]
|
||||
|
||||
["about"
|
||||
{:name ::about
|
||||
:view about-page
|
||||
:public? true}]
|
||||
|
||||
["items"
|
||||
;; Shared data for sub-routes
|
||||
{:view item-page
|
||||
:controllers [{:start (log-fn "start" "items controller")
|
||||
:stop (log-fn "stop" "items controller")}]}
|
||||
|
||||
[""
|
||||
{:name ::item-list
|
||||
:controllers [{:start (log-fn "start" "item-list controller")
|
||||
:stop (log-fn "stop" "item-list controller")}]}]
|
||||
["/:id"
|
||||
{:name ::item
|
||||
:parameters {:path {:id s/Int}
|
||||
:query {(s/optional-key :foo) s/Keyword}}
|
||||
:controllers [{:params (fn [match]
|
||||
(:path (:parameters match)))
|
||||
:start (fn [params]
|
||||
(js/console.log "start" "item controller" (:id params)))
|
||||
:stop (fn [params]
|
||||
(js/console.log "stop" "item controller" (:id params)))}]}]]]
|
||||
{:data {:controllers [{:start (log-fn "start" "root-controller")
|
||||
:stop (log-fn "stop" "root controller")}]
|
||||
:coercion rsc/coercion
|
||||
:public? false}}))
|
||||
|
||||
(defn init! []
|
||||
(rfe/start!
|
||||
routes
|
||||
(fn [new-match]
|
||||
(swap! state (fn [state]
|
||||
(if new-match
|
||||
(if (:user state)
|
||||
(assoc state :match (assoc new-match :controllers (rfc/apply-controllers (:controllers (:match state)) new-match)))
|
||||
(assoc state :match new-match))))))
|
||||
{:use-fragment true})
|
||||
(r/render [main-view] (.getElementById js/document "app")))
|
||||
|
||||
(init!)
|
||||
Loading…
Reference in a new issue