Tienson Qin 5 tahun lalu
induk
melakukan
1a89bcfd18

+ 1 - 1
backend/project.clj

@@ -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 - 0
backend/resources/public

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

+ 6 - 4
backend/src/backend/auth.clj

@@ -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)

+ 2 - 2
backend/src/backend/cookie.clj

@@ -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 "/"

+ 4 - 0
backend/src/backend/db/token.clj

@@ -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

+ 2 - 2
backend/src/backend/db/user.clj

@@ -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)}))

+ 29 - 0
backend/src/backend/interceptors.clj

@@ -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)))})

+ 26 - 8
backend/src/backend/routes.clj

@@ -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}}))}}]]
    ])

+ 55 - 41
backend/src/backend/system.clj

@@ -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

+ 27 - 0
backend/src/backend/views/home.clj

@@ -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/[email protected]/dist/lightning-fs.min.js"}]
+    [:script {:src "https://unpkg.com/[email protected]/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"}]]))

+ 8 - 1
frontend/src/frontend/components/home.cljs

@@ -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)
+       ))))

+ 0 - 15
frontend/src/frontend/components/settings.cljs

@@ -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 []

+ 2 - 0
frontend/src/frontend/mui.cljs

@@ -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))