about summary refs log tree commit diff
path: root/users/grfn/bbbg/src/bbbg/web.clj
blob: 4501043e2f5f52c4f7bfbbe0d67aa81046443c06 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
(ns bbbg.web
  (:require
   [bbbg.discord.auth :as discord.auth :refer [wrap-discord-auth]]
   [bbbg.handlers.attendee-checks :as attendee-checks]
   [bbbg.handlers.attendees :as attendees]
   [bbbg.handlers.events :as events]
   [bbbg.handlers.home :as home]
   [bbbg.handlers.signup-form :as signup-form]
   [bbbg.styles :refer [stylesheet]]
   [bbbg.util.core :as u]
   [bbbg.views.flash :refer [wrap-page-flash]]
   clj-time.coerce
   [clojure.spec.alpha :as s]
   [com.stuartsierra.component :as component]
   [compojure.core :refer [GET routes]]
   [config.core :refer [env]]
   [org.httpkit.server :as http-kit]
   [ring.middleware.flash :refer [wrap-flash]]
   [ring.middleware.keyword-params :refer [wrap-keyword-params]]
   [ring.middleware.params :refer [wrap-params]]
   [ring.middleware.session :refer [wrap-session]]
   [ring.middleware.session.cookie :refer [cookie-store]]
   [ring.util.response :refer [content-type resource-response response]])
  (:import
   java.util.Base64))

(s/def ::port pos-int?)

(s/def ::cookie-secret
  (s/and bytes? #(= 16 (count %))))

(s/def ::config
  (s/merge
   (s/keys :req [::port]
           :opt [::cookie-secret])
   ::discord.auth/config))

(s/fdef make-server
  :args (s/cat :config ::config))


(defn- string->cookie-secret [raw]
  (s/assert
   ::cookie-secret
   (when raw
     (.decode (Base64/getDecoder)
              (.getBytes raw "UTF-8")))))

(defn env->config []
  (s/assert
   ::config
   (u/remove-nils
    (merge
     {::port (:port env 8888)
      ::cookie-secret (some-> env :cookie-secret string->cookie-secret)}
     (discord.auth/env->config)))))

(defn dev-config []
  (s/assert
   ::config
   (merge
    {::port 8888
     ::cookie-secret (into-array Byte/TYPE (repeat 16 0))}
    (discord.auth/dev-config))))

;;;

(defn app-routes [env]
  (routes
   (GET "/main.css" []
     (-> (response stylesheet)
         (content-type "text/css")))
   (GET "/main.js" []
     (-> (resource-response "main.js")
         (content-type "text/javascript")))

   (attendees/attendees-routes env)
   (attendee-checks/attendee-checks-routes env)
   (signup-form/signup-form-routes env)
   (events/events-routes env)
   (home/home-routes env)))

(defn middleware [app env]
  (-> app
      (wrap-discord-auth env)
      wrap-keyword-params
      wrap-params
      wrap-page-flash
      wrap-flash
      (wrap-session {:store (cookie-store
                             {:key (:cookie-secret env)
                              :readers {'clj-time/date-time
                                        clj-time.coerce/from-string}})
                     :cookie-attrs {:same-site :lax}})))

(defn handler [env]
  (-> (app-routes env)
      (middleware env)))

(defrecord WebServer [port cookie-secret db]
  component/Lifecycle
  (start [this]
    (assoc this
           ::shutdown-fn
           (http-kit/run-server
            (fn [r] ((handler this) r))
            {:port port})))
  (stop [this]
    (if-let [shutdown-fn (::shutdown-fn this)]
      (do (shutdown-fn :timeout 100)
          (dissoc this ::shutdown-fn))
      this)))

(defn make-server [{::keys [port cookie-secret]
                    :as env}]
  (component/using
   (map->WebServer
    (merge
     {:port port
      :cookie-secret cookie-secret}
     env))
   [:db]))