From 6e76b1cf29f7fe7bfc42665706a262ded7689686 Mon Sep 17 00:00:00 2001 From: hunterlong Date: Wed, 5 Aug 2020 21:17:14 -0700 Subject: [PATCH] tests, email rendered from MJML, db changes --- CHANGELOG.md | 2 + Makefile | 8 +- {source/tmpl => frontend/public}/robots.txt | 0 frontend/src/pages/Help.vue | 2 +- go.mod | 8 +- go.sum | 23 +- handlers/services.go | 3 +- notifiers/email.go | 14 +- notifiers/email_rendered.go | 54 ++ notifiers/email_template.go | 628 -------------------- notifiers/generate/main.go | 269 +++++++++ notifiers/notifiers.go | 2 + source/tmpl/banner.png | Bin 49875 -> 0 bytes types/checkins/generate.go | 3 - types/hits/interface.go | 1 - types/incidents/struct.go | 2 +- types/services/database.go | 30 +- types/services/incidents.go | 1 + types/services/services_test.go | 26 +- types/users/auth.go | 4 +- types/users/users_test.go | 2 +- utils/utils.go | 6 +- 22 files changed, 407 insertions(+), 681 deletions(-) rename {source/tmpl => frontend/public}/robots.txt (100%) create mode 100755 notifiers/email_rendered.go delete mode 100644 notifiers/email_template.go create mode 100644 notifiers/generate/main.go delete mode 100644 source/tmpl/banner.png delete mode 100644 types/checkins/generate.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f63c67..2ec28b1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Modified index page to use /assets directory for assets, (main.css, style.css) - Modified index page to use CDN asset paths - Fixed New Checkin form +- Modified email notifier template to be rendered from MJML +- Modified database relationships with services using gorm # 0.90.61 (07-22-2020) - Modified sass layouts, organized and split up sections diff --git a/Makefile b/Makefile index 15779743..525bc7a3 100644 --- a/Makefile +++ b/Makefile @@ -145,7 +145,9 @@ frontend-build: @cp -r frontend/dist source/ @cp -r frontend/src/assets/scss source/dist/ @cp -r frontend/public/main.scss source/dist/scss/ - @cp -r source/tmpl/*.* source/dist/ + @cp frontend/public/favicon.ico source/dist/ + @cp frontend/public/robots.txt source/dist/ + @cp frontend/public/banner.png source/dist/ @cp -r frontend/public/favicon source/dist/ @echo "Frontend build complete at ./source/dist" @@ -157,7 +159,7 @@ yarn: cd frontend && yarn # compile assets using SASS and Rice. compiles scss -> css, and run rice embed-go -compile: frontend-build +compile: generate frontend-build rm -f source/rice-box.go cd source && rice embed-go @@ -175,7 +177,7 @@ install-darwin: mv $(BINARY_NAME) /usr/local/bin/$(BINARY_NAME) generate: - cd source && go generate + go generate ./... build-all: clean compile build-folders build-linux build-linux-arm build-darwin build-win compress-folders diff --git a/source/tmpl/robots.txt b/frontend/public/robots.txt similarity index 100% rename from source/tmpl/robots.txt rename to frontend/public/robots.txt diff --git a/frontend/src/pages/Help.vue b/frontend/src/pages/Help.vue index 7f5c3a58..0686de35 100644 --- a/frontend/src/pages/Help.vue +++ b/frontend/src/pages/Help.vue @@ -6,4 +6,4 @@ export default { name: 'Help', } - + \ No newline at end of file diff --git a/go.mod b/go.mod index 161e452d..58fdb92e 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9 github.com/jinzhu/gorm v1.9.12 github.com/mattn/go-sqlite3 v2.0.3+incompatible - github.com/pavius/impi v0.0.3 // indirect github.com/pelletier/go-toml v1.7.0 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.1.0 @@ -30,14 +29,15 @@ require ( github.com/spf13/viper v1.6.3 github.com/stretchr/testify v1.5.1 github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1 + github.com/tdewolff/minify/v2 v2.8.0 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d - golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d // indirect + golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect + golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c // indirect google.golang.org/grpc v1.28.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/mail.v2 v2.3.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v2 v2.2.8 - mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect - mvdan.cc/unindent v0.0.0-20170829200357-ff6a34147cea // indirect ) diff --git a/go.sum b/go.sum index 5688f53a..bf6608e2 100755 --- a/go.sum +++ b/go.sum @@ -114,6 +114,7 @@ github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8 github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -397,6 +398,7 @@ github.com/liquidweb/liquidweb-go v1.6.1/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVL github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -470,8 +472,6 @@ github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pavius/impi v0.0.3 h1:DND6MzU+BLABhOZXbELR3FU8b+zDgcq4dOCNLhiTYuI= -github.com/pavius/impi v0.0.3/go.mod h1:x/hU0bfdWIhuOT1SKwiJg++yvkk6EuOtJk8WtDZqgr8= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= @@ -576,6 +576,13 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1 h1:HGVkRrwDCbmSP6h1CoBDj6l/mhnvsP5JbYaQ4ss0R6o= github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1/go.mod h1:I3xbaE9ud9/TEXzehwkHx86SyJwqeSNsX2X5oV61jIg= +github.com/tdewolff/minify v1.1.0 h1:nxHQi1ML+g3ZbZHffiZ6eC7vMqNvSRfX3KB5Y5y/kfw= +github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo= +github.com/tdewolff/minify/v2 v2.8.0 h1:t3tOPWkTpKhsgxm3IM9Sy8hE2eIt30Oaa+2havJGGIE= +github.com/tdewolff/minify/v2 v2.8.0/go.mod h1:6zN8VLhMfFxNrwHROcboYNo2+huPNu4SV8DPh3PUQ8E= +github.com/tdewolff/parse/v2 v2.4.4 h1:uMdbQRtYbKR/msP9CbI7li9wK6pionYiH6s7ipltyGY= +github.com/tdewolff/parse/v2 v2.4.4/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7 h1:CpHxIaZzVy26GqJn8ptRyto8fuoYOd1v0fXm9bG3wQ8= github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -609,7 +616,6 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -677,8 +683,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -730,6 +734,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -771,6 +776,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -822,8 +829,6 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4 h1:kDtqNkeBrZb8B+atrj50B5XLHpzXXqcCdZPP/ApQ5NY= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d h1:szSOL78iTCl0LF1AMjhSWJj8tIM0KixlUUnBtYXsmd8= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -941,10 +946,6 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unindent v0.0.0-20170829200357-ff6a34147cea h1:M9wqFYtIbcU4ya22owATTPClIVAKkxqoYt2HAn02/JI= -mvdan.cc/unindent v0.0.0-20170829200357-ff6a34147cea/go.mod h1:uK66ibKYZGgWb8OWRdHaAVsidCOAFXHXLpYh0P+WFgA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/handlers/services.go b/handlers/services.go index 38d55e74..31138758 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -269,8 +269,7 @@ func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) { sendErrorJson(err, w, r) return } - err = service.DeleteFailures() - if err != nil { + if err := service.AllFailures().DeleteAll(); err != nil { sendErrorJson(err, w, r) return } diff --git a/notifiers/email.go b/notifiers/email.go index 000ccd46..81c23bf2 100644 --- a/notifiers/email.go +++ b/notifiers/email.go @@ -92,7 +92,7 @@ type emailOutgoing struct { // OnFailure will trigger failing service func (e *emailer) OnFailure(s services.Service, f failures.Failure) (string, error) { subject := fmt.Sprintf("Service %s is Offline", s.Name) - tmpl := renderEmail(s, f) + tmpl := renderEmail(s, f, emailFailure) email := &emailOutgoing{ To: e.Var2.String, Subject: subject, @@ -105,7 +105,7 @@ func (e *emailer) OnFailure(s services.Service, f failures.Failure) (string, err // OnSuccess will trigger successful service func (e *emailer) OnSuccess(s services.Service) (string, error) { subject := fmt.Sprintf("Service %s is Back Online", s.Name) - tmpl := renderEmail(s, failures.Failure{}) + tmpl := renderEmail(s, failures.Failure{}, emailSuccess) email := &emailOutgoing{ To: e.Var2.String, Subject: subject, @@ -115,13 +115,13 @@ func (e *emailer) OnSuccess(s services.Service) (string, error) { return tmpl, e.dialSend(email) } -func renderEmail(s services.Service, f failures.Failure) string { +func renderEmail(s services.Service, f failures.Failure, emailData string) string { wr := bytes.NewBuffer(nil) tmpl := template.New("email") - tmpl, err := tmpl.Parse(emailBase) + tmpl, err := tmpl.Parse(emailData) if err != nil { log.Errorln(err) - return emailBase + return emailData } data := replacer{ @@ -133,7 +133,7 @@ func renderEmail(s services.Service, f failures.Failure) string { if err = tmpl.ExecuteTemplate(wr, "email", data); err != nil { log.Errorln(err) - return emailBase + return emailData } return wr.String() @@ -146,7 +146,7 @@ func (e *emailer) OnTest() (string, error) { email := &emailOutgoing{ To: e.Var2.String, Subject: subject, - Template: renderEmail(service, failures.Example()), + Template: renderEmail(service, failures.Example(), emailFailure), From: e.Var1.String, } return subject, e.dialSend(email) diff --git a/notifiers/email_rendered.go b/notifiers/email_rendered.go new file mode 100755 index 00000000..204529b5 --- /dev/null +++ b/notifiers/email_rendered.go @@ -0,0 +1,54 @@ +// DO NOT EDIT ** This file was generated with go generate on 2020-08-06 04:13:25.078006 +0000 UTC ** DO NOT EDIT // +package notifiers + +const emailSuccess = `Statping Service Notification
Sphero
{{.Service.Name}} is currently offline, you might want to check it.
Offline for {{.Service.Downtime.Human}}
View Dashboard
Service Domain
{{.Service.Domain}}
Current Issue
{{.Failure.Issue}}
 
You are receiving this email because one of your services has changed on your Statping instance. You can modify this email on the Email Notifier page in Settings.
© Statping
Statping.com         +Github         +Privacy         +Unsubscribe
` + +const emailFailure = `Statping Service Notification
Sphero
{{.Service.Name}} is currently offline, you might want to check it.
Offline for {{.Service.Downtime.Human}}
View Dashboard
Service Domain
{{.Service.Domain}}
Current Issue
{{.Failure.Issue}}
 
You are receiving this email because one of your services has changed on your Statping instance. You can modify this email on the Email Notifier page in Settings.
© Statping
Statping.com         +Github         +Privacy         +Unsubscribe
` diff --git a/notifiers/email_template.go b/notifiers/email_template.go deleted file mode 100644 index af21e415..00000000 --- a/notifiers/email_template.go +++ /dev/null @@ -1,628 +0,0 @@ -package notifiers - -const emailBase = ` -{{$banner := "https://assets.statping.com/greenbackground.png"}} -{{$color := "#4caf50"}} -{{if not .Service.Online}} -{{$banner = "https://assets.statping.com/offlinebanner.png"}} -{{$color = "#c30c0c"}} -{{end}} - - - - - - Statping Service Notification - - - - - - - - - - - - - - - - - -
- - -
-
- - - - - - -
- -
- - - - -
- - - - - - -
- - Statping - -
-
-
- -
-
-
- -
- - - - - - -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -{{if .Service.Online}} - {{.Service.Name}} is back online. -{{else}} - {{.Service.Name}} is currently offline, you might want to check it. -{{end}} - -
-
- -
- - - - - - -
- -
- - - - - - - -
-
-{{if .Service.Online}} -Online for {{.Service.Uptime.Human}} -{{else}} -Offline for {{.Service.Downtime.Human}} -{{end}} -
-
- - - - -
- - View Dashboard -
-
-
- -
-
- -
- -
- - - - - - -
- -
- - - - - - - -
-
Service Domain
-
-
{{.Service.Domain}}
-
-
- -
-
- -
- -
- - - - - - -
- -{{if .Failure}} -
- - - - - - - -
-
Current Issue
-
-
{{.Failure.Issue}}
-
-
-{{end}} - -
-
- -
- -
 
- -
- -
-
- - - - - - -
- -
- - - - -
- - - - - - -
- - - -
-
-
- -
-
-
- -
- -
- - - - - - -
- -
- - - - - - - -
-
You are receiving this email because one of your services has changed on your Statping instance. You can modify this email on the Email Notifier page in Settings.
-
-
© Statping
-
-
- -
-
- -
- -
- - - - - - -
- -
- -
- - - - - - -
- - - - -
-
Statping.com         Github         - Privacy         Unsubscribe
-
-
-
- -
- -
-
- -
-
- -
-
- -
- - - -` diff --git a/notifiers/generate/main.go b/notifiers/generate/main.go new file mode 100644 index 00000000..34e73884 --- /dev/null +++ b/notifiers/generate/main.go @@ -0,0 +1,269 @@ +// +build ignore + +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/statping/statping/utils" + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/html" + "os" + "time" +) + +var ( + mjmlApplication string + mjmlPrivate string +) + +func main() { + utils.InitEnvs() + + mjmlApplication = os.Getenv("MJML_APP") + mjmlPrivate = os.Getenv("MJML_PRIVATE") + + fmt.Println(mjmlApplication) + + success := convertMJML(emailSuccessMJML) + fail := convertMJML(emailFailureMJML) + + htmlOut := `// DO NOT EDIT ** This file was generated with go generate on ` + utils.Now().String() + ` ** DO NOT EDIT // +package notifiers + +const emailSuccess = ` + minimize(success) + ` + +const emailFailure = ` + minimize(fail) + ` + +` + + fmt.Println(htmlOut) + + utils.SaveFile("email_rendered.go", []byte(htmlOut)) +} + +type mjmlInput struct { + Mjml string `json:"mjml"` +} + +func minimize(val string) string { + m := minify.New() + m.Add("text/html", &html.Minifier{ + KeepDefaultAttrVals: true, + }) + s, err := m.String("text/html", val) + if err != nil { + panic(err) + } + + return fmt.Sprintf("`%s`", s) +} + +func convertMJML(mjml string) string { + input, _ := json.Marshal(mjmlInput{mjml}) + auth := fmt.Sprintf("%s:%s", mjmlApplication, mjmlPrivate) + resp, _, err := utils.HttpRequest("https://"+auth+"@api.mjml.io/v1/render", "POST", "application/json", nil, bytes.NewBuffer(input), 15*time.Minute, false, nil) + if err != nil { + panic(err) + } + var respData mjmlApi + if err := json.Unmarshal(resp, &respData); err != nil { + panic(err) + } + return respData.Html +} + +type mjmlApi struct { + Html string `json:"html"` + Mjml string `json:"mjml"` + Version string `json:"mjml_version"` +} + +const emailFailureMJML = ` + + Statping Service Notification + + + + + + + + + + + + + + + {{.Service.Name}} is currently offline, you might want to check it. + + + + + + Offline for {{.Service.Downtime.Human}} + + View Dashboard + + + + + + + + + + + + + + + Service Domain + {{.Service.Domain}} + + + + + + Current Issue + {{.Failure.Issue}} + + + + + + + + + + + + + + + + You are receiving this email because one of your services has changed on your Statping instance. You can modify this email on the Email Notifier page in Settings. + + + © Statping + + + + + + + + + Statping.com         + + Github         + + Privacy         + + Unsubscribe + + + + + + + + + +` + +const emailSuccessMJML = ` + + Statping Service Notification + + + + + + + + + + + + + + + {{.Service.Name}} is currently offline, you might want to check it. + + + + + + Offline for {{.Service.Downtime.Human}} + + View Dashboard + + + + + + + + + + + + + + + Service Domain + {{.Service.Domain}} + + + + + + Current Issue + {{.Failure.Issue}} + + + + + + + + + + + + + + + + You are receiving this email because one of your services has changed on your Statping instance. You can modify this email on the Email Notifier page in Settings. + + + © Statping + + + + + + + + + Statping.com         + + Github         + + Privacy         + + Unsubscribe + + + + + + + + + +` diff --git a/notifiers/notifiers.go b/notifiers/notifiers.go index f7e59dee..b1ecd986 100644 --- a/notifiers/notifiers.go +++ b/notifiers/notifiers.go @@ -10,6 +10,8 @@ import ( "time" ) +//go:generate go run generate/main.go + var log = utils.Log.WithField("type", "notifier") type replacer struct { diff --git a/source/tmpl/banner.png b/source/tmpl/banner.png deleted file mode 100644 index 0378161bc0e287a522af246ddd355162d96d513b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49875 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 diff --git a/types/checkins/generate.go b/types/checkins/generate.go deleted file mode 100644 index 5e3ffbb0..00000000 --- a/types/checkins/generate.go +++ /dev/null @@ -1,3 +0,0 @@ -package checkins - -//go:generate counterfeiter . Checkin diff --git a/types/hits/interface.go b/types/hits/interface.go index ab6db7a7..3604ca6b 100644 --- a/types/hits/interface.go +++ b/types/hits/interface.go @@ -61,7 +61,6 @@ func (h Hitters) DeleteAll() error { func (h Hitters) Sum() int64 { var r IntResult - h.db.Select("CAST(SUM(latency) as INT) as amount").Scan(&r) return r.Amount } diff --git a/types/incidents/struct.go b/types/incidents/struct.go index 281396b9..5ddf3d2a 100644 --- a/types/incidents/struct.go +++ b/types/incidents/struct.go @@ -10,7 +10,7 @@ type Incident struct { ServiceId int64 `gorm:"index;column:service" json:"service"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at" json:"updated_at"` - Updates []*IncidentUpdate `gorm:"foreignkey:incident;association_foreignkey:id;association_autoupdate:false;association_autocreate:false" json:"updates,omitempty"` + Updates []*IncidentUpdate `gorm:"foreignkey:incident;association_foreignkey:id" json:"updates,omitempty"` } // IncidentUpdate contains updates based on a Incident diff --git a/types/services/database.go b/types/services/database.go index 8937222c..672ffdcb 100644 --- a/types/services/database.go +++ b/types/services/database.go @@ -16,7 +16,7 @@ var ( ) func (s *Service) AfterFind() { - db.Model(s).Related(&s.Incidents).Related(&s.Messages).Related(&s.Checkins) + db.Model(s).Related(&s.Incidents).Related(&s.Messages).Related(&s.Checkins).Related(&s.Incidents) metrics.Query("service", "find") } @@ -87,8 +87,8 @@ func (s *Service) Create() error { func (s *Service) Update() error { q := db.Update(s) - allServices[s.Id] = s s.Close() + allServices[s.Id] = s s.SleepDuration = s.Duration() go ServiceCheckQueue(allServices[s.Id], true) return q.Error() @@ -96,29 +96,38 @@ func (s *Service) Update() error { func (s *Service) Delete() error { s.Close() - if err := s.DeleteFailures(); err != nil { + if err := s.AllFailures().DeleteAll(); err != nil { return err } - if err := s.DeleteHits(); err != nil { + if err := s.AllHits().DeleteAll(); err != nil { return err } if err := s.DeleteCheckins(); err != nil { return err } + db.Model(s).Association("Checkins").Clear() if err := s.DeleteIncidents(); err != nil { return err } + db.Model(s).Association("Incidents").Clear() + if err := s.DeleteMessages(); err != nil { + return err + } + db.Model(s).Association("Messages").Clear() + delete(allServices, s.Id) q := db.Model(&Service{}).Delete(s) return q.Error() } -func (s *Service) DeleteFailures() error { - return s.AllFailures().DeleteAll() -} - -func (s *Service) DeleteHits() error { - return s.AllHits().DeleteAll() +func (s *Service) DeleteMessages() error { + for _, m := range s.Messages { + if err := m.Delete(); err != nil { + return err + } + } + db.Model(s).Association("messages").Clear() + return nil } func (s *Service) DeleteCheckins() error { @@ -127,5 +136,6 @@ func (s *Service) DeleteCheckins() error { return err } } + db.Model(s).Association("checkins").Clear() return nil } diff --git a/types/services/incidents.go b/types/services/incidents.go index d59a50df..681075f0 100644 --- a/types/services/incidents.go +++ b/types/services/incidents.go @@ -6,5 +6,6 @@ func (s *Service) DeleteIncidents() error { return err } } + db.Model(s).Association("Updates").Clear() return nil } diff --git a/types/services/services_test.go b/types/services/services_test.go index 0c22d4d7..d08ca2a9 100644 --- a/types/services/services_test.go +++ b/types/services/services_test.go @@ -10,6 +10,7 @@ import ( "github.com/statping/statping/types/failures" "github.com/statping/statping/types/hits" "github.com/statping/statping/types/incidents" + "github.com/statping/statping/types/messages" "github.com/statping/statping/types/notifications" "github.com/statping/statping/types/null" "github.com/statping/statping/utils" @@ -99,6 +100,20 @@ var incidentUpdate1 = &incidents.IncidentUpdate{ CreatedAt: utils.Now().Add(-5 * time.Second), } +var message1 = &messages.Message{ + Title: "Example Message", + Description: "Used for testing", + StartOn: utils.Now().Add(15 * time.Minute), + EndOn: utils.Now().Add(30 * time.Minute), + ServiceId: 1, + NotifyUsers: null.NewNullBool(false), + NotifyMethod: "", + NotifyBefore: null.NewNullInt64(0), + NotifyBeforeScale: "", + CreatedAt: utils.Now(), + UpdatedAt: utils.Now(), +} + type exampleGRPC struct { pb.UnimplementedRouteGuideServer } @@ -183,11 +198,12 @@ func startupDb(t *testing.T) { require.Nil(t, err) db, err := database.OpenTester() require.Nil(t, err) - db.AutoMigrate(&Service{}, ¬ifications.Notification{}, &hits.Hit{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &failures.Failure{}, &incidents.Incident{}, &incidents.IncidentUpdate{}) + db.AutoMigrate(&Service{}, ¬ifications.Notification{}, &messages.Message{}, &hits.Hit{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &failures.Failure{}, &incidents.Incident{}, &incidents.IncidentUpdate{}) checkins.SetDB(db) failures.SetDB(db) incidents.SetDB(db) notifications.SetDB(db) + messages.SetDB(db) hits.SetDB(db) SetDB(db) @@ -201,6 +217,7 @@ func startupDb(t *testing.T) { db.Create(&incident1) db.Create(&incidentUpdate1) db.Create(¬ification.Notification) + db.Create(&message1) } func TestServices(t *testing.T) { @@ -521,6 +538,10 @@ func TestServices(t *testing.T) { err = item.Delete() require.Nil(t, err) + // after deleted service, make sure checkins, failures, hits, and incidents are also delete + assert.Len(t, item.AllFailures().List(), 0) + assert.Len(t, item.AllHits().List(), 0) + checkin := item.Checkins assert.Len(t, checkin, 0) for _, c := range checkin { @@ -529,9 +550,6 @@ func TestServices(t *testing.T) { assert.False(t, c.IsRunning()) } - assert.Len(t, item.AllFailures().List(), 0) - assert.Len(t, item.AllHits().List(), 0) - inc := item.Incidents assert.Len(t, inc, 0) for _, i := range inc { diff --git a/types/users/auth.go b/types/users/auth.go index bf91097f..b6ce7c6d 100644 --- a/types/users/auth.go +++ b/types/users/auth.go @@ -8,13 +8,13 @@ import ( // AuthUser will return the User and a boolean if authentication was correct. // accepts username, and password as a string -func AuthUser(username, password string) (*User, bool) { +func AuthUser(username, passwordHash string) (*User, bool) { user, err := FindByUsername(username) if err != nil { log.Warnln(fmt.Errorf("user %v not found", username)) return nil, false } - if utils.CheckHash(password, user.Password) { + if utils.CheckHash(passwordHash, user.Password) { user.UpdatedAt = time.Now().UTC() user.Update() return user, true diff --git a/types/users/users_test.go b/types/users/users_test.go index fd186e99..6c8cc773 100644 --- a/types/users/users_test.go +++ b/types/users/users_test.go @@ -36,7 +36,7 @@ func TestFind(t *testing.T) { } func TestAuthUser(t *testing.T) { - u, err := AuthUser("example_user", "password12345") + u, err := AuthUser("example_user", utils.HashPassword("password12345")) require.Nil(t, err) assert.Equal(t, "example_user", u.Username) diff --git a/utils/utils.go b/utils/utils.go index f0214c1f..4ca9517c 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -162,7 +162,7 @@ func DurationReadable(d time.Duration) string { // // body - The body or form data to send with HTTP request // // timeout - Specific duration to timeout on. time.Duration(30 * time.Seconds) // // You can use a HTTP Proxy if you HTTP_PROXY environment variable -func HttpRequest(endpoint, method string, content interface{}, headers []string, body io.Reader, timeout time.Duration, verifySSL bool, customTLS *tls.Config) ([]byte, *http.Response, error) { +func HttpRequest(endpoint, method string, contentType interface{}, headers []string, body io.Reader, timeout time.Duration, verifySSL bool, customTLS *tls.Config) ([]byte, *http.Response, error) { var err error var req *http.Request if method == "" { @@ -175,8 +175,8 @@ func HttpRequest(endpoint, method string, content interface{}, headers []string, // set default headers so end user can overwrite them if needed req.Header.Set("User-Agent", "Statping") req.Header.Set("Statping-Version", Version) - if content != nil { - req.Header.Set("Content-Type", content.(string)) + if contentType != nil { + req.Header.Set("Content-Type", contentType.(string)) } verifyHost := req.URL.Hostname()