diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..1bc703e5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +examples/ +examples/** +target/** +!target/auto-ap.jar diff --git a/project.clj b/project.clj index e10e60c2..e8593379 100644 --- a/project.clj +++ b/project.clj @@ -26,6 +26,8 @@ [buddy/buddy-sign "2.1.0"] [clj-time "0.14.2"] [io.forward/clojure-mail "1.0.7"] + [ring/ring-json "0.4.0" + :exclusions [cheshire]] [amazonica "0.3.121" :exclusions [com.amazonaws/aws-java-sdk com.amazonaws/amazon-kinesis-client]] @@ -67,7 +69,8 @@ :uberjar {:prep-tasks [["cljsbuild" "once" "min"] "compile"]} :provided {:dependencies [[org.clojure/clojurescript "1.9.908"] [reagent "0.7.0"] - [re-frame "0.10.2"]]}} + [re-frame "0.10.2"] + [com.andrewmcveigh/cljs-time "0.5.2"]]}} :cljsbuild {:builds diff --git a/src/clj/auto_ap/background/mail.clj b/src/clj/auto_ap/background/mail.clj index 1fa45a70..1a0e39ac 100644 --- a/src/clj/auto_ap/background/mail.clj +++ b/src/clj/auto_ap/background/mail.clj @@ -23,29 +23,32 @@ (def queue-url "https://sqs.us-east-1.amazonaws.com/679918342773/integreat-mail-prod") (defn process-sqs [] - (println "Fetching messages from sqs...") - (let [companies (companies/get-all)] - (doseq [message (:messages (sqs/receive-message {:queue-url queue-url - :wait-time-seconds 5 - :max-number-of-messages 10 - #_#_:attribute-names ["All"]}))] - (let [message-body (json/read-str (:body message) - :key-fn keyword)] - (doseq [r (:Records message-body)] - (println "Processing record " r) - (let [props (Session/getDefaultInstance (Properties.)) - message-stream (-> (s3/get-object {:key (-> r :s3 :object :key) - :bucket-name (-> r :s3 :bucket :name)}) - :input-stream) - mail (message/read-message (MimeMessage. props message-stream))] - (doseq [pdf-stream (->> (-> mail :body) - (filter :content-type) - (filter #(re-find #"application/pdf" (:content-type %)) ))] - (let [filename (str "/tmp/" (UUID/randomUUID) ".pdf")] - (io/copy (:body pdf-stream) (io/file filename)) - (invoices/import (parse/parse-file filename filename) companies) - (io/delete-file filename)))))) - (sqs/delete-message (assoc message :queue-url queue-url ))))) + (try + (println "Fetching messages from sqs...") + (let [companies (companies/get-all)] + (doseq [message (:messages (sqs/receive-message {:queue-url queue-url + :wait-time-seconds 5 + :max-number-of-messages 10 + #_#_:attribute-names ["All"]}))] + (let [message-body (json/read-str (:body message) + :key-fn keyword)] + (doseq [r (:Records message-body)] + (println "Processing record " r) + (let [props (Session/getDefaultInstance (Properties.)) + message-stream (-> (s3/get-object {:key (-> r :s3 :object :key) + :bucket-name (-> r :s3 :bucket :name)}) + :input-stream) + mail (message/read-message (MimeMessage. props message-stream))] + (doseq [pdf-stream (->> (-> mail :body) + (filter :content-type) + (filter #(re-find #"application/pdf" (:content-type %)) ))] + (let [filename (str "/tmp/" (UUID/randomUUID) ".pdf")] + (io/copy (:body pdf-stream) (io/file filename)) + (invoices/import (parse/parse-file filename filename) companies) + (io/delete-file filename)))))) + (sqs/delete-message (assoc message :queue-url queue-url )))) + (catch Exception e + (println e)))) (defn always-process-sqs [] (while (not (Thread/interrupted)) diff --git a/src/clj/auto_ap/db/invoices.clj b/src/clj/auto_ap/db/invoices.clj index dfa2632e..ddb4793b 100644 --- a/src/clj/auto_ap/db/invoices.clj +++ b/src/clj/auto_ap/db/invoices.clj @@ -34,6 +34,6 @@ (do (println row) (assoc row - :company (:name (parse/best-match companies customer-identifier)) + :company-id (:id (parse/best-match companies customer-identifier)) :imported false :potential-duplicate false))))) diff --git a/src/clj/auto_ap/db/reminders.clj b/src/clj/auto_ap/db/reminders.clj index 2094d56e..aa3b8556 100644 --- a/src/clj/auto_ap/db/reminders.clj +++ b/src/clj/auto_ap/db/reminders.clj @@ -27,7 +27,7 @@ (str/join "," (take (count vendors) (repeat "?"))) ") " "AND " - "scheduled > NOW()" + "sent is NULL" ) ] vendors)))) diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index 13039f79..8ce3b04d 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -22,7 +22,7 @@ [auto-ap.routes.auth :as auth] [amazonica.core :refer [defcredential]])) -(defcredential "AKIAIRKDGLBX7J7VJZ6Q" "OtRw2t/xktJBDjP8Jnx1Yf6G+uzBfIkrQEc6nmgo" "us-east-1") +(defcredential "AKIAINHACMVQJ6NYD26A" "FwdL4TbIC/5H/4mwhQy4iSI/eSewyPgfS1EEt6tL" "us-east-1") (defroutes static-routes (GET "/" [] (response/resource-response "index.html" {:root "public"})) diff --git a/src/clj/auto_ap/routes/reminders.clj b/src/clj/auto_ap/routes/reminders.clj index d1c51a11..a7a6b2c2 100644 --- a/src/clj/auto_ap/routes/reminders.clj +++ b/src/clj/auto_ap/routes/reminders.clj @@ -10,16 +10,18 @@ [clj-time.periodic :as p] [clj-time.local :as l] [clj-time.jdbc] + [clj-http.client :as http] + [clojure.data.json :as json] + [ring.middleware.json :refer [wrap-json-body]] ) (:import [org.joda.time DateTime])) (defn next-sunday [] - (let [sunday (->> (p/periodic-seq (time/today) (time/days 1)) + (let [sunday (->> (p/periodic-seq (time/plus (time/today) (time/days 1)) (time/days 1)) (filter pred/sunday?) - first) - ] - - (.toDateTime (time/local-date-time (time/year sunday) (time/month sunday) (time/day sunday))))) + first)] + (time/from-time-zone (time/date-time (time/year sunday) (time/month sunday) (time/day sunday)) + (time/time-zone-for-id "America/Los_Angeles")))) (defn schedule-reminders [] (let [vendors (vendors/find-with-reminders) @@ -41,30 +43,38 @@ )) (defn send-emails [reminders] - (doseq [{:keys [name email id]} reminders] + (doseq [{:keys [vendor-name email id]} reminders] (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.")}}) + :body {:html (str "

Hello " vendor-name ",

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

- Integreat.

") + :text (str "Hello " vendor-name ",\r\nThis is a reminder to send this week's invoices to us. You can just reply to this email.\r\n - Integreat.")}}) (reminders/finish id))) (defn replace-joda [x] (into {} (map (fn [[k v]] [k (if (instance? DateTime v) - (c/to-string v) + (c/to-date v) v)]) - x)) - ) - + x))) (defroutes routes (context "/reminders" [] - (POST "/send" {:keys [query-params]} - (schedule-reminders) - (-> (reminders/get-ready) - (send-emails)) + (POST "/send" {:keys [query-params headers body] :as x} + (let [notification-type (get headers "x-amz-sns-message-type")] + (println "Received notification " notification-type) + (if (= "SubscriptionConfirmation" notification-type) + (do + (println "Responding to confirmation" ) + (let [json (json/read-str (slurp body))] + (println json) + (http/get (get json "SubscribeURL")))) + (do + (println "Scheduling") + (schedule-reminders) + (-> (reminders/get-ready) + (send-emails))))) {:status 200 :body "{}" diff --git a/src/cljs/auto_ap/effects.cljs b/src/cljs/auto_ap/effects.cljs index 6c7c186c..4b78087b 100644 --- a/src/cljs/auto_ap/effects.cljs +++ b/src/cljs/auto_ap/effects.cljs @@ -2,6 +2,8 @@ (:require-macros [cljs.core.async.macros :refer [go]]) (:require [re-frame.core :as re-frame] [cljs-http.client :as http] + [cljs-time.coerce :as c] + [cljs-time.core :as time] [cljs.core.async :refer [date-times [x] + (cond (map? x) + (into {} (map (fn [[k v]] + [k (if (instance? js/Date v) + (time/to-default-time-zone (c/from-date v)) + v)]) + x)) + (list? x) + (map dates->date-times x))) + (re-frame/reg-fx :http (fn [{:keys [method uri on-success body headers token]}] @@ -33,5 +45,6 @@ :url uri}) (date-times) (conj on-success) (re-frame/dispatch)))))) diff --git a/src/cljs/auto_ap/views/pages/admin/reminders.cljs b/src/cljs/auto_ap/views/pages/admin/reminders.cljs index 2e6ed089..470252cf 100644 --- a/src/cljs/auto_ap/views/pages/admin/reminders.cljs +++ b/src/cljs/auto_ap/views/pages/admin/reminders.cljs @@ -4,10 +4,13 @@ [reagent.core :as reagent] [auto-ap.subs :as subs] [auto-ap.events.admin.reminders :as events] - [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event]] + [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event date-time->str date->str]] [cljs.reader :as edn] + [auto-ap.routes :as routes] [bidi.bidi :as bidi])) + + (defn reminders-table [] (let [reminders (or @(re-frame/subscribe [::subs/reminders]) [])] [:table {:class "table", :style {:width "100%"}} @@ -20,9 +23,9 @@ ^{:key id} [:tr [:td vendor-name] - [:td (str scheduled)] + [:td (date->str scheduled)] [:td (when sent - [:span [:span.icon [:i.fa.fa-check]] "Sent " (str sent)]) ]])]])) + [:span [:span.icon [:i.fa.fa-check]] "Sent " (date-time->str sent)]) ]])]])) (defn admin-reminders-page [] diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index 5d4d10c1..80d9d875 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -1,5 +1,6 @@ (ns auto-ap.views.utils - (:require [re-frame.core :as re-frame])) + (:require [re-frame.core :as re-frame] + [cljs-time.format :as format])) (defn active-when= [active-page candidate] (when (= active-page candidate) " active")) @@ -18,3 +19,12 @@ (fn [e] (.preventDefault e) (re-frame/dispatch event))) + +(def pretty-long (format/formatter "MM/dd/yyyy HH:mm:ss")) +(def pretty (format/formatter "MM/dd/yyyy")) + +(defn date->str [d] + (format/unparse pretty d)) + +(defn date-time->str [d] + (format/unparse pretty-long d)) diff --git a/terraform/main.tf b/terraform/main.tf index 7ddfa377..ab497c9a 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,4 +1,12 @@ -provider "aws" {} +provider "aws" { + access_key = "${var.aws_access_key_id}" + secret_key = "${var.aws_secret_access_key}" + region = "us-east-1" +} + +variable "aws_secret_access_key" {} +variable "aws_access_key_id" {} +variable "stage" {} data "aws_caller_identity" "current" {} @@ -8,7 +16,7 @@ resource "aws_ses_receipt_rule_set" "main" { resource "aws_ses_receipt_rule" "store" { depends_on = ["aws_ses_receipt_rule_set.main"] - name = "store" + name = "store-${var.stage}" rule_set_name = "default-rule-set" recipients = ["invoices@mail.integreat.aws.brycecovertoperations.com"] enabled = true @@ -21,7 +29,7 @@ resource "aws_ses_receipt_rule" "store" { } resource "aws_s3_bucket" "invoices" { - bucket = "integreat-mail-prod" + bucket = "integreat-mail-${var.stage}" acl = "private" policy = <