diff --git a/config/dev.edn b/config/dev.edn index 0b4c2647..5c5eb7f1 100644 --- a/config/dev.edn +++ b/config/dev.edn @@ -1,2 +1,3 @@ {:db {:server "localhost"} - :scheme "http"} + :scheme "http" + :jwt-secret "auto ap invoices are awesome"} diff --git a/config/prod.edn b/config/prod.edn index cc9a8b07..bc59d8b9 100644 --- a/config/prod.edn +++ b/config/prod.edn @@ -1,2 +1,3 @@ {:db {:server "database"} - :scheme "https"} + :scheme "https" + :jwt-secret "auto ap invoices are awesome"} diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index adcabc3b..3b9f5648 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -3,9 +3,8 @@ [compojure.route :as route] [clojure.java.io :as io] [clojure.string :as str] - [auto-ap.db.invoices :as invoices] + [auto-ap.routes.invoices :as invoices] [auto-ap.db.users :as users] - [auto-ap.parse :as parse] [ring.middleware.multipart-params :as mp] [ring.util.response :as response] [ring.middleware.defaults :refer [wrap-defaults site-defaults]] @@ -13,23 +12,16 @@ [ring.middleware.reload :refer [wrap-reload]] [ring.middleware.edn :refer [wrap-edn-params]] [clojure.java.jdbc :as j] - [clj-http.client :as http] - [clj-time.core :as time] [config.core :refer [env]] [buddy.auth :refer [authenticated?]] - [buddy.sign.jwt :as jwt] [buddy.auth.backends.token :refer [jws-backend]] [buddy.auth.middleware :refer [wrap-authorization wrap-authentication]] - [auto-ap.db.companies :as companies] - [auto-ap.db.vendors :as vendors] - [amazonica.core :refer [defcredential]] - [amazonica.aws.simpleemail :as ses])) + [auto-ap.routes.companies :as companies] + [auto-ap.routes.vendors :as vendors] + [auto-ap.routes.reminders :as reminders] + [auto-ap.routes.auth :as auth] + [amazonica.core :refer [defcredential]])) - -(def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com") -(def google-client-secret "OC-WemHurPXYpuIw5cT-B90g") - -(def jwt-secret "auto ap invoices are awesome") (defcredential "AKIAIRKDGLBX7J7VJZ6Q" "OtRw2t/xktJBDjP8Jnx1Yf6G+uzBfIkrQEc6nmgo" "us-east-1") (defn wrap-secure [handler] @@ -44,151 +36,18 @@ (route/resources "/") (routes (ANY "*" [] (response/resource-response "index.html" {:root "public"})))) -(defroutes invoice-routes - (context "/invoices" [] - (GET "/" [] - {:status 200 - :body (pr-str (invoices/get-all)) - :headers {"Content-Type" "application/edn"}}) - (GET "/unpaid" {:keys [query-params] :as r} - {:status 200 - :body (pr-str (invoices/get-unpaid (query-params "company"))) - :headers {"Content-Type" "application/edn"}}) - - (GET "/pending" {:keys [query-params]} - {:status 200 - :body (pr-str (invoices/get-pending (query-params "company"))) - :headers {"Content-Type" "application/edn"}}) - (POST "/" {:keys [edn-params]} - (invoices/insert-multi! (:rows edn-params)) - {:status 200 - :body (pr-str (invoices/get-all)) - :headers {"Content-Type" "application/edn"}}) - (POST "/approve" {:keys [query-params]} - (invoices/approve) - {:status 200 - :body (pr-str (invoices/get-pending (query-params "company"))) - :headers {"Content-Type" "application/edn"}}) - (POST "/reject" {:keys [query-params]} - (invoices/reject) - {:status 200 - :body (pr-str (invoices/get-pending (query-params "company"))) - :headers {"Content-Type" "application/edn"}}) - (POST "/upload" - {{ files "file"} :params :as params} - (let [{:keys [filename tempfile]} files - existing-invoices (invoices/get-all) - companies (companies/get-all)] - (println companies) - (invoices/insert-multi! - (for [{:keys [total date invoice-number customer-identifier vendor] :as row} - (parse/parse-file (.getPath tempfile) filename)] - (assoc row - :company-id (:id (parse/best-match companies customer-identifier)) - - :imported false - :potential-duplicate (boolean (seq (filter #(and (= vendor (:vendor %)) - (= invoice-number (:invoice-number %))) - existing-invoices))) - ))) - {:status 200 - :body (pr-str (invoices/get-pending ((:query-params params ) "company"))) - :headers {"Content-Type" "application/edn"}})) - - ;; Removing the export view for now... - #_(wrap-json-response (GET "/export" {:keys [query-params]} - (println query-params) - (doto (invoices/get-unpaid (query-params "company")) - println))))) - -(defroutes company-routes - (context "/companies" [] - (GET "/" [] - {:status 200 - :body (pr-str (companies/get-all)) - :headers {"Content-Type" "application/edn"}}) - (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} - {:status 200 - :body (pr-str (companies/upsert id edn-params)) - :headers {"Content-Type" "application/edn"}}))) - -(defroutes vendor-routes - (context "/vendors" [] - (GET "/" [] - {:status 200 - :body (pr-str (vendors/get-all)) - :headers {"Content-Type" "application/edn"}}) - (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} - {:status 200 - :body (pr-str (vendors/upsert id edn-params)) - :headers {"Content-Type" "application/edn"}}) - (POST "/" {:keys [edn-params] :as r} - {:status 200 - :body (pr-str (vendors/insert edn-params)) - :headers {"Content-Type" "application/edn"}}))) - -(defroutes reminder-routes - (context "/reminders" [] - (POST "/send" {:keys [query-params]} - (doseq [{:keys [name email invoice-reminder-schedule]} (vendors/get-all)] - (when (= "Weekly" invoice-reminder-schedule) - (println "Sending email to" email) - (ses/send-email :destination {:to-addresses [email]} - :source "invoices@mail.integreat.aws.brycecovertoperations.com" - :message {:subject "Reminder to send invoices" - :body {:html (str "

Hello " name ",

This is a reminder to send this week's invoices to us. You can just reply to this email.

- Integreat.

") - :text (str "Hello " name ",\r\nThis is a reminder to send this week's invoices to us. You can just reply to this email.\r\n - Integreat.")}}))) - {:status 200 - :body "{}" - :headers {"Content-Type" "application/edn"}}))) - -(defroutes auth-routes - (GET "/oauth" {{:strs [code]} :query-params :keys [scheme] :as r {:strs [host]} :headers} - (try - (let [auth (-> "https://accounts.google.com/o/oauth2/token" - (http/post - {:form-params {"client_id" google-client-id - "client_secret" google-client-secret - "code" code - "redirect_uri" (str (:scheme env) "://" host "/api/oauth") - "grant_type" "authorization_code"} - :as :json}) - :body) - _ (println auth) - token (:access_token auth) - profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo" - {:headers {"Authorization" (str "Bearer " token)} :as :json}) - :body - (doto println)) - user (users/find-or-insert! {:provider "google" - :provider_id (:id profile)})] - - (if (and token user) - {:status 301 - :headers {"Location" (str "/?jwt=" (jwt/sign {:user "test" - :exp (time/plus (time/now) (time/days 7)) - :companies (:companies user) - :name (:name profile)} - jwt-secret - {:alg :hs512}))}} - {:status 401 - :body "Couldn't authenticate"})) - (catch Exception e - - {:status 401 - :body (str "Couldn't authenticate " (.toString e))})))) (defroutes api-routes (context "/api" [] - (wrap-routes invoice-routes wrap-secure) - (wrap-routes company-routes wrap-secure) - (wrap-routes vendor-routes wrap-secure) - (wrap-routes reminder-routes wrap-secure) - auth-routes)) + (wrap-routes invoices/routes wrap-secure) + (wrap-routes companies/routes wrap-secure) + (wrap-routes vendors/routes wrap-secure) + (wrap-routes reminders/routes wrap-secure) + auth/routes)) -(def auth-backend (jws-backend {:secret jwt-secret :options {:alg :hs512}})) +(def auth-backend (jws-backend {:secret (:jwt-secret env) :options {:alg :hs512}})) (def app-routes (routes @@ -202,5 +61,4 @@ (wrap-reload) (wrap-params) (mp/wrap-multipart-params) - (wrap-edn-params) - )) + (wrap-edn-params))) diff --git a/src/clj/auto_ap/routes/auth.clj b/src/clj/auto_ap/routes/auth.clj new file mode 100644 index 00000000..6fb9876d --- /dev/null +++ b/src/clj/auto_ap/routes/auth.clj @@ -0,0 +1,47 @@ +(ns auto-ap.routes.auth + (:require + [compojure.core :refer [defroutes GET ]] + [auto-ap.db.users :as users] + [buddy.sign.jwt :as jwt] + [clj-http.client :as http] + [config.core :refer [env]] + [clj-time.core :as time])) + +(def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com") +(def google-client-secret "OC-WemHurPXYpuIw5cT-B90g") + +(defroutes routes + (GET "/oauth" {{:strs [code]} :query-params :keys [scheme] :as r {:strs [host]} :headers} + (try + (let [auth (-> "https://accounts.google.com/o/oauth2/token" + (http/post + {:form-params {"client_id" google-client-id + "client_secret" google-client-secret + "code" code + "redirect_uri" (str (:scheme env) "://" host "/api/oauth") + "grant_type" "authorization_code"} + :as :json}) + :body) + _ (println auth) + token (:access_token auth) + profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo" + {:headers {"Authorization" (str "Bearer " token)} :as :json}) + :body + (doto println)) + user (users/find-or-insert! {:provider "google" + :provider_id (:id profile)})] + + (if (and token user) + {:status 301 + :headers {"Location" (str "/?jwt=" (jwt/sign {:user "test" + :exp (time/plus (time/now) (time/days 7)) + :companies (:companies user) + :name (:name profile)} + (:jwt-secret env) + {:alg :hs512}))}} + {:status 401 + :body "Couldn't authenticate"})) + (catch Exception e + + {:status 401 + :body (str "Couldn't authenticate " (.toString e))})))) diff --git a/src/clj/auto_ap/routes/companies.clj b/src/clj/auto_ap/routes/companies.clj new file mode 100644 index 00000000..acee237c --- /dev/null +++ b/src/clj/auto_ap/routes/companies.clj @@ -0,0 +1,14 @@ +(ns auto-ap.routes.companies + (:require [compojure.core :refer [context GET PUT defroutes]] + [auto-ap.db.companies :as companies])) + +(defroutes routes + (context "/companies" [] + (GET "/" [] + {:status 200 + :body (pr-str (companies/get-all)) + :headers {"Content-Type" "application/edn"}}) + (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} + {:status 200 + :body (pr-str (companies/upsert id edn-params)) + :headers {"Content-Type" "application/edn"}}))) diff --git a/src/clj/auto_ap/routes/invoices.clj b/src/clj/auto_ap/routes/invoices.clj new file mode 100644 index 00000000..3450de99 --- /dev/null +++ b/src/clj/auto_ap/routes/invoices.clj @@ -0,0 +1,62 @@ +(ns auto-ap.routes.invoices + (:require [compojure.core :refer [context GET PUT POST defroutes]] + [auto-ap.db.invoices :as invoices] + [auto-ap.db.companies :as companies] + [auto-ap.parse :as parse])) + +(defroutes routes + (context "/invoices" [] + (GET "/" [] + {:status 200 + :body (pr-str (invoices/get-all)) + :headers {"Content-Type" "application/edn"}}) + + (GET "/unpaid" {:keys [query-params] :as r} + {:status 200 + :body (pr-str (invoices/get-unpaid (query-params "company"))) + :headers {"Content-Type" "application/edn"}}) + + (GET "/pending" {:keys [query-params]} + {:status 200 + :body (pr-str (invoices/get-pending (query-params "company"))) + :headers {"Content-Type" "application/edn"}}) + (POST "/" {:keys [edn-params]} + (invoices/insert-multi! (:rows edn-params)) + {:status 200 + :body (pr-str (invoices/get-all)) + :headers {"Content-Type" "application/edn"}}) + (POST "/approve" {:keys [query-params]} + (invoices/approve) + {:status 200 + :body (pr-str (invoices/get-pending (query-params "company"))) + :headers {"Content-Type" "application/edn"}}) + (POST "/reject" {:keys [query-params]} + (invoices/reject) + {:status 200 + :body (pr-str (invoices/get-pending (query-params "company"))) + :headers {"Content-Type" "application/edn"}}) + (POST "/upload" + {{ files "file"} :params :as params} + (let [{:keys [filename tempfile]} files + existing-invoices (invoices/get-all) + companies (companies/get-all)] + (invoices/insert-multi! + (for [{:keys [total date invoice-number customer-identifier vendor] :as row} + (parse/parse-file (.getPath tempfile) filename)] + (assoc row + :company-id (:id (parse/best-match companies customer-identifier)) + + :imported false + :potential-duplicate (boolean (seq (filter #(and (= vendor (:vendor %)) + (= invoice-number (:invoice-number %))) + existing-invoices))) + ))) + {:status 200 + :body (pr-str (invoices/get-pending ((:query-params params ) "company"))) + :headers {"Content-Type" "application/edn"}})) + + ;; Removing the export view for now... + #_(wrap-json-response (GET "/export" {:keys [query-params]} + (println query-params) + (doto (invoices/get-unpaid (query-params "company")) + println))))) diff --git a/src/clj/auto_ap/routes/reminders.clj b/src/clj/auto_ap/routes/reminders.clj new file mode 100644 index 00000000..9c572b80 --- /dev/null +++ b/src/clj/auto_ap/routes/reminders.clj @@ -0,0 +1,20 @@ +(ns auto-ap.routes.reminders + (:require [compojure.core :refer [context GET POST defroutes]] + [auto-ap.db.vendors :as vendors] + [amazonica.aws.simpleemail :as ses])) + + +(defroutes routes + (context "/reminders" [] + (POST "/send" {:keys [query-params]} + (doseq [{:keys [name email invoice-reminder-schedule]} (vendors/get-all)] + (when (= "Weekly" invoice-reminder-schedule) + (println "Sending email to" email) + (ses/send-email :destination {:to-addresses [email]} + :source "invoices@mail.integreat.aws.brycecovertoperations.com" + :message {:subject "Reminder to send invoices" + :body {:html (str "

Hello " name ",

This is a reminder to send this week's invoices to us. You can just reply to this email.

- Integreat.

") + :text (str "Hello " name ",\r\nThis is a reminder to send this week's invoices to us. You can just reply to this email.\r\n - Integreat.")}}))) + {:status 200 + :body "{}" + :headers {"Content-Type" "application/edn"}}))) diff --git a/src/clj/auto_ap/routes/vendors.clj b/src/clj/auto_ap/routes/vendors.clj new file mode 100644 index 00000000..26e6754a --- /dev/null +++ b/src/clj/auto_ap/routes/vendors.clj @@ -0,0 +1,18 @@ +(ns auto-ap.routes.vendors + (:require [compojure.core :refer [context GET PUT POST defroutes]] + [auto-ap.db.vendors :as vendors])) + +(defroutes routes + (context "/vendors" [] + (GET "/" [] + {:status 200 + :body (pr-str (vendors/get-all)) + :headers {"Content-Type" "application/edn"}}) + (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} + {:status 200 + :body (pr-str (vendors/upsert id edn-params)) + :headers {"Content-Type" "application/edn"}}) + (POST "/" {:keys [edn-params] :as r} + {:status 200 + :body (pr-str (vendors/insert edn-params)) + :headers {"Content-Type" "application/edn"}})))