(ns auto-ap.handler (:require [amazonica.core :refer [defcredential]] [auto-ap.client-routes :as client-routes] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.logging :as alog] [auto-ap.routes.auth :as auth] [auto-ap.routes.exports :as exports] [auto-ap.routes.ezcater :as ezcater] [auto-ap.routes.graphql :as graphql] [auto-ap.routes.health :as health] [auto-ap.routes.invoices :as invoices] [auto-ap.routes.queries :as queries] [auto-ap.routes.yodlee2 :as yodlee2] [auto-ap.ssr.core :as ssr] [bidi.bidi :as bidi] [bidi.ring :refer [->ResourcesMaybe make-handler]] [buddy.auth.backends.session :refer [session-backend]] [buddy.auth.backends.token :refer [jws-backend]] [buddy.auth.middleware :refer [wrap-authentication wrap-authorization]] [clojure.string :as str] [com.brunobonacci.mulog :as mu] [config.core :refer [env]] [ring.middleware.edn :refer [wrap-edn-params]] [ring.middleware.multipart-params :as mp] [ring.middleware.params :refer [wrap-params]] [ring.middleware.reload :refer [wrap-reload]] [ring.middleware.session :refer [wrap-session]] [ring.middleware.session.cookie :refer [cookie-store]] [ring.util.response :as response] [unilog.context :as lc] [clj-time.coerce :as coerce] [clj-time.core :as time])) (when (:aws-access-key-id env) (defcredential (:aws-access-key-id env) (:aws-secret-access-key env) (:aws-region env))) (defn deep-merge [v & vs] (letfn [(rec-merge [v1 v2] (if (and (map? v1) (map? v2)) (merge-with deep-merge v1 v2) v2))] (when (some identity vs) (reduce #(rec-merge %1 %2) v vs)))) (def all-routes ["/" (-> (into [] (deep-merge ssr-routes/routes (second client-routes/routes) graphql/routes ezcater/routes health/routes queries/routes2 yodlee2/routes auth/routes invoices/routes exports/routes2)) (conj ["" (->ResourcesMaybe {:prefix "public/"})]) (conj [true :not-found])) ;; always go for not found as last resort, have to switch to vec in order for that to work ]) (defn not-found [_] {:status 404 :headers {} :body ""}) (defn render-index [_] (response/resource-response "index.html" {:root "public"})) (def match->handler-lookup (-> {:not-found not-found} (merge ssr/key->handler) (merge graphql/match->handler) (merge ezcater/match->handler) (merge health/match->handler) (merge queries/match->handler) (merge yodlee2/match->handler) (merge auth/match->handler) (merge invoices/match->handler) (merge exports/match->handler) (merge (into {} (map (fn [k] [k render-index]) client-routes/all-matches))))) (def match->handler (fn [route] (or (get match->handler-lookup route) route))) (def route-handler (make-handler all-routes match->handler)) (defn wrap-guess-route [handler] (fn [{:keys [uri request-method] :as request} ] (let [matched-route (:handler (bidi.bidi/match-route all-routes uri :request-method request-method))] (handler (assoc request :matched-route matched-route))))) (defn test-match-route [method uri] (bidi.bidi/match-route all-routes uri :request-method method)) (def auth-backend (jws-backend {:secret (:jwt-secret env) :options {:alg :hs512}})) (defn wrap-logging [handler] (fn [request] (mu/with-context {:uri (:uri request) :query (:uri request) :request-method (:request-method request) :user (:identity request) :user-role (:user/role (:identity request)) :user-name (:user/name (:identity request))} (mu/trace ::http-request-trace [] (lc/with-context {:uri (:uri request) :source "request" :user-role (:user/role (:identity request)) :user-name (:user/name (:identity request))} (when-not (str/includes? (:uri request) "health-check") (alog/info ::http-request-starting)) (try (let [response (handler request)] (alog/info ::http-request-done :status-code (:status response)) response) (catch Exception e (alog/error ::request-error :exception e) (throw e)))))))) (defn wrap-idle-session-timeout [handler ] (fn [request] (let [session (:session request {}) end-time (coerce/to-date-time (::idle-timeout session))] (if (and end-time (time/before? end-time (time/now))) {:session nil :status 302 :headers {"Location" "/login"}} (when-let [response (handler request)] (let [session (:session response session)] (if (nil? session) response (let [end-time (time/plus (time/now) (time/days 2))] (assoc response :session (assoc session ::idle-timeout (coerce/to-date end-time))))))))))) #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} (def app (-> route-handler (wrap-guess-route) (wrap-authorization auth-backend ) (wrap-authentication auth-backend (session-backend {:authfn (fn [auth] (dissoc auth :exp))})) (wrap-idle-session-timeout) (wrap-session {:store (cookie-store {:key (byte-array [42, 52, -31, 105, -126, -33, -118, -69, -82, -59, -15, -69, -38, 103, -102, -1])} )}) (wrap-reload) (wrap-params) (mp/wrap-multipart-params) (wrap-edn-params) (wrap-logging)))