pull/645/head
Tienson Qin 2020-02-28 14:06:44 +08:00
parent 2187e04439
commit 1a89bcfd18
13 changed files with 163 additions and 74 deletions

View File

@ -24,7 +24,7 @@
[aero "1.1.6"]
[com.stuartsierra/component "0.4.0"]
[com.taoensso/nippy "2.14.0"]
]
[hiccup "1.0.5"]]
;; :main backend.core
:profiles {:repl {:dependencies [[io.pedestal/pedestal.service-tools "0.5.7"]]
:source-paths ["src/backend" "dev"]}

1
backend/resources/public Symbolic link
View File

@ -0,0 +1 @@
/home/tienson/codes/projects/gitnotes/frontend/public

View File

@ -12,7 +12,7 @@
(let [{:keys [app-key app-secret redirect-uri]} (get-in config/config [:oauth :github])
instance (social/make-social :github app-key app-secret redirect-uri
:state (str (util/uuid))
:scope "user:email")
:scope "user:email,repo")
access-token (social/getAccessToken instance (:code data))
info (social/getUserInfo instance access-token)
oauth-type "github"
@ -21,9 +21,11 @@
(toucan.db/transaction
(if-let [token (token/get oauth-type oauth-id)]
;; user already exists
(let [token (assoc token :token access-token)]
(some-> (u/get (:user_id token))
(assoc :token token)))
(do
(prn {:token token})
(let [token (assoc token :token access-token)]
(some-> (u/get (:user_id token))
(assoc :token token))))
(when-let [user (u/insert {:name (:login info)
:email (:email info)})]
(let [token (token/create {:user_id (:id user)

View File

@ -16,7 +16,7 @@
(let [dev? config/dev?
xsrf-token (str (util/uuid))
domain (if-not dev?
".chengdongchengxi.com"
".gitnotes.com"
"")
secure (if-not dev?
true
@ -40,7 +40,7 @@
(def delete-token
(let [domain (if-not config/dev?
".chengdongchengxi.com"
".gitnotes.com"
"")]
{"x" {:value ""
:path "/"

View File

@ -11,6 +11,10 @@
(db/select-one Token {:oauth_type oauth-type
:oauth_id oauth-id}))
(defn get-user-tokens
[user-id]
(db/select Token {:user_id user-id}))
(defn exists?
[oauth-type oauth-id]
(db/exists? Token {:oauth_type oauth-type

View File

@ -18,7 +18,7 @@
(defn get
[id]
(db/select-one User id))
(db/select-one User :id id))
(defn insert
[{:keys [name email] :as args}]
@ -39,7 +39,7 @@
[:ok (db/update! User id {:email email})]))
(defn generate-tokens
[db user-id]
[user-id]
(cookie/token-cookie
{:access-token (jwt/sign {:id user-id})
:refresh-token (refresh-token/create user-id)}))

View File

@ -0,0 +1,29 @@
(ns backend.interceptors
(:require [io.pedestal.interceptor :refer [interceptor]]
[backend.cookie :as cookie]
[backend.jwt :as jwt]
[backend.db.user :as u]
[backend.util :as util]))
(def cookie-interceptor
{:name ::cookie-authenticate
:enter
(fn [{:keys [request] :as context}]
(let [tokens (cookie/get-token request)]
(if tokens
(let [{:keys [access-token refresh-token]} tokens]
(if access-token
(try
(let [user (jwt/unsign access-token)
uid (some-> (:id user) util/->uuid)
user (u/get uid)]
(if (:id user)
(-> context
(assoc-in [:request :app-context :uid] uid)
(assoc-in [:request :app-context :user] user))
context))
(catch Exception e
nil))
;; TODO: wrong cookie, early halt
))
context)))})

View File

@ -5,7 +5,10 @@
[backend.util :as util]
[backend.auth :as auth]
[backend.db.user :as u]
[ring.util.response :as resp]))
[backend.db.token :as token]
[ring.util.response :as resp]
[backend.views.home :as home]
[backend.interceptors :as interceptors]))
(def routes
[["/swagger.json"
@ -14,6 +17,13 @@
:description "with pedestal & reitit-http"}}
:handler (swagger/create-swagger-handler)}}]
[
"/"
{:get {:no-doc true
:handler (fn [_req]
{:status 200
:body (home/home)})}}]
["/login"
{:swagger {:tags ["Login"]}}
@ -27,9 +37,8 @@
"?referer="
(get-in req [:headers "referer"] ""))
:state (str (util/uuid))
:scope "user:email")
:scope "user:email,repo")
url (social/getAuthorizationUrl social)]
(prn "url: " url)
(resp/redirect url))
)}}]]
["/auth"
@ -42,13 +51,22 @@
(if (and (:code params)
(:state params))
(if-let [user (auth/github params)]
(resp/header
(assoc :cookies
(u/generate-tokens user)
:status 302)
"Location" config/website-uri)
(-> (resp/redirect config/website-uri)
(assoc :cookies (u/generate-tokens (:id user))))
{:status 500
:body "Internal Error"})
{:status 401
:body "Invalid request"}))}}]]
["/api/v1" {:interceptors [interceptors/cookie-interceptor]}
["/me"
{:get {:summary "Get current user's information"
:handler
(fn [{:keys [app-context] :as req}]
(if-let [user (:user app-context)]
(let [tokens (token/get-user-tokens (:id user))]
{:status 200
:body {:user user
:tokens tokens}})
{:status 200
:body {:user nil}}))}}]]
])

View File

@ -21,46 +21,54 @@
[com.stuartsierra.component :as component]
[backend.components.http :as component-http]
[backend.components.hikari :as hikari]
[backend.routes :as routes]))
[backend.routes :as routes]
[backend.config :as config]
[io.pedestal.http.ring-middlewares :as ring-middlewares]))
(def router
(pedestal/routing-interceptor
(http/router
routes/routes
(http/router
routes/routes
{;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs
;;:validate spec/validate ;; enable spec validation for route data
;;:reitit.spec/wrap spell/closed ;; strict top-level validation
:exception pretty/exception
:data {:coercion reitit.coercion.spec/coercion
:muuntaja m/instance
:interceptors [;; swagger feature
swagger/swagger-feature
;; query-params & form-params
(parameters/parameters-interceptor)
;; content-negotiation
(muuntaja/format-negotiate-interceptor)
;; encoding response body
(muuntaja/format-response-interceptor)
;; exception handling
(exception/exception-interceptor)
;; decoding request body
(muuntaja/format-request-interceptor)
;; coercing response bodys
(coercion/coerce-response-interceptor)
;; coercing request parameters
(coercion/coerce-request-interceptor)
;; multipart
(multipart/multipart-interceptor)]}})
{;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs
;;:validate spec/validate ;; enable spec validation for route data
;;:reitit.spec/wrap spell/closed ;; strict top-level validation
;; :exception pretty/exception
:data {:coercion reitit.coercion.spec/coercion
:muuntaja m/instance
:interceptors [;; swagger feature
swagger/swagger-feature
;; query-params & form-params
(parameters/parameters-interceptor)
;; content-negotiation
(muuntaja/format-negotiate-interceptor)
;; encoding response body
(muuntaja/format-response-interceptor)
;; exception handling
;; (exception/exception-interceptor)
;; decoding request body
(muuntaja/format-request-interceptor)
;; coercing response bodys
(coercion/coerce-response-interceptor)
;; coercing request parameters
(coercion/coerce-request-interceptor)
;; multipart
(multipart/multipart-interceptor)]}})
;; optional default ring handler (if no routes have matched)
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-resource-handler)
(ring/create-default-handler))))
;; optional default ring handler (if no routes have matched)
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/swagger"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-resource-handler)
(ring/create-default-handler))))
(defn merge-interceptors-map
[system-map interceptors]
(update system-map :io.pedestal.http/interceptors
(fn [old]
(vec (concat interceptors old)))))
(defn new-system
[{:keys [env port hikari-spec] :as config}]
@ -71,14 +79,20 @@
;; no pedestal routes
::server/routes []
;; allow serving the swagger-ui styles & scripts from self
::server/secure-headers {:content-security-policy-settings
{:default-src "'self'"
:style-src "'self' 'unsafe-inline'"
:script-src "'self' 'unsafe-inline'"}}}
;; ::server/secure-headers {:content-security-policy-settings
;; {:default-src "'self'"
;; :style-src "'self' 'unsafe-inline'"
;; :script-src "'self' 'unsafe-inline'"}}
::server/secure-headers {:content-security-policy-settings {:object-src "'none'"}}
::server/resource-path "/public"}
(server/default-interceptors)
;; use the reitit router
(pedestal/replace-last-interceptor router)
(server/dev-interceptors))]
(pedestal/replace-last-interceptor router))
service-map (merge-interceptors-map
service-map
[ring-middlewares/cookies
server/html-body])
service-map (if config/dev? (server/dev-interceptors service-map) service-map)]
(component/system-map :service-map service-map
:hikari (hikari/new-hikari-cp hikari-spec)
:http

View File

@ -0,0 +1,27 @@
(ns backend.views.home
(:require [hiccup.page :as html]))
(defn home
[]
(html/html5
[:head
[:meta {:charset "utf-8"}]
[:meta
{:content
"minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no",
:name "viewport"}]
[:link {:type "text/css", :href "css/style.css", :rel "stylesheet"}]
[:link
{:href
"https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap",
:rel "stylesheet"}]
[:link {:href "css/highlight.css", :rel "stylesheet"}]
[:title "Gitnotes"]]
[:body
[:div#root]
[:script {:src "https://unpkg.com/@isomorphic-git/lightning-fs@3.4.1/dist/lightning-fs.min.js"}]
[:script {:src "https://unpkg.com/isomorphic-git@0.78.5/dist/bundle.umd.min.js"}]
[:script
"window.fs = new LightningFS('gitnotes');git.plugins.set('fs', window.fs);window.pfs = window.fs.promises;"]
[:script {:src "/js/main.js"}]
[:script {:src "/js/highlight.pack.js"}]]))

View File

@ -70,4 +70,11 @@
[:div "Cloning..."]
:else
(settings/settings-form github-username github-token github-repo)))))
(mui/button {:variant "contained"
:color "primary"
:start-icon (mui/github-icon)
:href "/login/github"}
"Login with Github")
;; (settings/settings-form github-username github-token github-repo)
))))

View File

@ -12,14 +12,6 @@
(mui/grid
{:container true
:direction "column"}
(mui/text-field {:id "standard-basic"
:style {:margin-bottom 12}
:label "Github username"
:auto-focus true
:on-change (fn [event]
(let [v (util/evalue event)]
(swap! state/state assoc :github-username v)))
:value github-username})
(mui/text-field {:id "standard-basic"
:style {:margin-bottom 12}
:label "Github repo"
@ -28,13 +20,6 @@
(swap! state/state assoc :github-repo v)))
:value github-repo
})
(mui/text-field {:id "standard-basic"
:style {:margin-bottom 12}
:label "Github basic token"
:on-change (fn [event]
(let [v (util/evalue event)]
(swap! state/state assoc :github-token v)))
:value github-token})
(mui/button {:variant "contained"
:color "primary"
:on-click (fn []

View File

@ -49,6 +49,7 @@
["@material-ui/core/DialogActions" :default DialogActions]
["@material-ui/icons/Favorite" :default FavoriteIcon]
["@material-ui/icons/Add" :default AddIcon]
["@material-ui/icons/GitHub" :default GithubIcon]
["@material-ui/icons/Share" :default ShareIcon]
["@material-ui/icons/MoreVert" :default MoreVertIcon]
))
@ -83,6 +84,7 @@
(defonce collapse (r/adapt-class Collapse))
(defonce avatar (r/adapt-class Avatar))
(defonce favorite-icon (r/adapt-class FavoriteIcon))
(defonce github-icon (r/adapt-class GithubIcon))
(defonce add-icon (r/adapt-class AddIcon))
(defonce fab (r/adapt-class Fab))
(defonce share-icon (r/adapt-class ShareIcon))