From 5d85c3ce39418fa02a8f81a88bdcb31898f0fe05 Mon Sep 17 00:00:00 2001 From: Hunter Long Date: Wed, 2 Sep 2020 14:00:31 -0700 Subject: [PATCH] v0.90.65 (#805) * notifier panic fix * portainer template * remove default host from discord notifier * fix for updating fields * fix for updating fields * fixed notifier panic * fixed notifier panic * test fix * test fix * missing login banner image * dont delete admin if DEMO_MODE * updatess to service on Dashboard * notifier endpoint fixes, timeframe rounding chart data * modal for UI confirmations --- .github/workflows/development.yml | 1 + .github/workflows/master.yml | 1 + .gitignore | 1 + CHANGELOG.md | 7 +++ cmd/main.go | 8 +-- database/grouping.go | 22 ++++---- dev/portainer.json | 21 ++++++++ frontend/config/webpack.config.prod.js | 2 +- frontend/src/API.js | 4 +- frontend/src/assets/scss/base.scss | 4 +- frontend/src/assets/scss/layout.scss | 28 +++++++++++ .../Dashboard/DashboardMessages.vue | 18 +++++-- .../Dashboard/DashboardServices.vue | 26 +++++++--- .../components/Dashboard/DashboardUsers.vue | 18 +++++-- .../src/components/Dashboard/Failures.vue | 20 +++++--- .../components/Dashboard/GroupedServices.vue | 2 +- .../src/components/Dashboard/Incidents.vue | 24 ++++++--- .../src/components/Dashboard/ServiceInfo.vue | 22 ++++---- .../components/Dashboard/ServiceSparkLine.vue | 10 ++-- .../src/components/Dashboard/ServicesList.vue | 27 +++++++--- .../src/components/Dashboard/ThemeEditor.vue | 22 +++++--- frontend/src/components/Elements/Modal.vue | 50 +++++++++++++++++++ .../components/Service/FailuresBarChart.vue | 3 +- .../src/components/Service/ServiceBlock.vue | 1 - .../src/components/Service/ServiceChart.vue | 13 ++--- frontend/src/forms/Login.vue | 2 +- frontend/src/forms/Notifier.vue | 4 +- frontend/src/mixin.js | 17 +++++-- frontend/src/pages/Dashboard.vue | 7 +++ frontend/src/pages/Help.vue | 2 +- frontend/src/pages/Index.vue | 17 +++---- frontend/src/pages/Login.vue | 2 +- frontend/src/pages/Settings.vue | 22 +++++--- frontend/src/store.js | 14 +++++- go.mod | 1 + go.sum | 3 ++ handlers/api.go | 10 ++-- handlers/notifications.go | 11 ++-- handlers/routes.go | 1 + handlers/services.go | 13 +++++ notifiers/amazon_sns.go | 2 +- notifiers/discord.go | 7 ++- notifiers/email.go | 22 +++----- notifiers/notifiers.go | 7 ++- notifiers/slack.go | 6 --- notifiers/slack_test.go | 2 +- types/checkins/database_hits.go | 2 +- types/configs/configs_form.go | 30 +++++------ types/configs/load.go | 5 +- types/configs/struct.go | 24 ++++++++- types/incidents/database.go | 2 +- types/notifications/database.go | 13 +++++ types/notifications/struct.go | 4 -- types/null/marshal.go | 7 ++- types/services/notifications.go | 7 +++ types/users/hooks.go | 9 ++++ utils/metrics.go | 5 -- utils/utils_test.go | 3 +- version.txt | 2 +- 59 files changed, 449 insertions(+), 191 deletions(-) create mode 100644 dev/portainer.json create mode 100644 frontend/src/components/Elements/Modal.vue delete mode 100644 utils/metrics.go diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index ea9feaad..ed698db3 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -257,6 +257,7 @@ jobs: API_SECRET: demopassword123 DISABLE_LOGS: false ALLOW_REPORTS: true + SAMPLE_DATA: true COVERALLS: ${{ secrets.COVERALLS }} DISCORD_URL: ${{ secrets.DISCORD_URL }} EMAIL_HOST: ${{ secrets.EMAIL_HOST }} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 5ec814fd..7d0c10b5 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -257,6 +257,7 @@ jobs: API_SECRET: demopassword123 DISABLE_LOGS: false ALLOW_REPORTS: true + SAMPLE_DATA: true COVERALLS: ${{ secrets.COVERALLS }} DISCORD_URL: ${{ secrets.DISCORD_URL }} EMAIL_HOST: ${{ secrets.EMAIL_HOST }} diff --git a/.gitignore b/.gitignore index d0b07313..ccdb9205 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ snap prime stage parts +assets_backup certs releases core/rice-box.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 588e4c25..c57f8e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.90.65 (09-01-2020) +- Fixed issue with dashboard not logging in (notifier panic) +- Modified static email templates to github.com/statping/emails +- Modified Regenerate API function to keep API_SECRET env +- Added DEMO_MODE env variable, if true, 'admin' cannot be deleted +- Modified Service sparklines on Dashboard + # 0.90.64 (08-18-2020) - Modified max-width for container to 1012px, larger UI - Added failure sparklines in the Services list view diff --git a/cmd/main.go b/cmd/main.go index 85bec417..d3f7e082 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -148,20 +148,20 @@ func InitApp() error { if _, err := core.Select(); err != nil { return err } + // init Sentry error monitoring (its useful) + utils.SentryInit(core.App.AllowReports.Bool) // init prometheus metrics metrics.InitMetrics() + // connect each notifier, added them into database if needed + notifiers.InitNotifiers() // select all services in database and store services in a mapping of Service pointers if _, err := services.SelectAllServices(true); err != nil { return err } // start routines for each service checking process services.CheckServices() - // connect each notifier, added them into database if needed - notifiers.InitNotifiers() // start routine to delete old records (failures, hits) go database.Maintenance() - // init Sentry error monitoring (its useful) - utils.SentryInit(core.App.AllowReports.Bool) core.App.Setup = true core.App.Started = utils.Now() return nil diff --git a/database/grouping.go b/database/grouping.go index ff953e24..28bce658 100644 --- a/database/grouping.go +++ b/database/grouping.go @@ -70,19 +70,19 @@ func (t *TimeVar) ToValues() ([]*TimeValue, error) { } // GraphData will return all hits or failures -func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) { - g.db = g.db.MultipleSelects( - g.db.SelectByTime(g.Group), +func (b *GroupQuery) GraphData(by By) ([]*TimeValue, error) { + b.db = b.db.MultipleSelects( + b.db.SelectByTime(b.Group), by.String(), ).Group("timeframe").Order("timeframe", true) - caller, err := g.ToTimeValue() + caller, err := b.ToTimeValue() if err != nil { return nil, err } - if g.FillEmpty { - return caller.FillMissing(g.Start, g.End) + if b.FillEmpty { + return caller.FillMissing(b.Start, b.End) } return caller.ToValues() } @@ -90,8 +90,8 @@ func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) { // ToTimeValue will format the SQL rows into a JSON format for the API. // [{"timestamp": "2006-01-02T15:04:05Z", "amount": 468293}] // TODO redo this entire function, use better SQL query to group by time -func (g *GroupQuery) ToTimeValue() (*TimeVar, error) { - rows, err := g.db.Rows() +func (b *GroupQuery) ToTimeValue() (*TimeVar, error) { + rows, err := b.db.Rows() if err != nil { return nil, err } @@ -102,8 +102,8 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) { if err := rows.Scan(&timeframe, &amount); err != nil { log.Error(err, timeframe) } - trueTime, _ := g.db.ParseTime(timeframe) - newTs := types.FixedTime(trueTime, g.Group) + trueTime, _ := b.db.ParseTime(timeframe) + newTs := types.FixedTime(trueTime, b.Group) tv := &TimeValue{ Timeframe: newTs, @@ -111,7 +111,7 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) { } data = append(data, tv) } - return &TimeVar{g, data}, nil + return &TimeVar{b, data}, nil } func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) { diff --git a/dev/portainer.json b/dev/portainer.json new file mode 100644 index 00000000..fe93ba18 --- /dev/null +++ b/dev/portainer.json @@ -0,0 +1,21 @@ +[ + { + "type": 1, + "title": "Statping", + "restart_policy": "unless-stopped", + "description": "Service monitoring with an easy to use status page and mobile app", + "logo": "https://assets.statping.com/icon.png", + "image": "statping/statping:latest", + "platform": "linux", + "categories": ["monitoring"], + "administrator_only": false, + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + { + "container": "/app" + } + ] + } +] diff --git a/frontend/config/webpack.config.prod.js b/frontend/config/webpack.config.prod.js index d877196f..0e10e4a4 100644 --- a/frontend/config/webpack.config.prod.js +++ b/frontend/config/webpack.config.prod.js @@ -74,7 +74,7 @@ const webpackConfig = merge(commonConfig, { threshold: 10240, minRatio: 0.8 }), - new webpack.HashedModuleIdsPlugin(), + // new webpack.HashedModuleIdsPlugin(), new HtmlPlugin({ template: 'public/base.gohtml', filename: 'base.gohtml', diff --git a/frontend/src/API.js b/frontend/src/API.js index 49ef994c..9cbc9ed6 100644 --- a/frontend/src/API.js +++ b/frontend/src/API.js @@ -7,8 +7,8 @@ const tokenKey = "statping_auth"; class Api { constructor() { - this.version = "0.90.64"; - this.commit = "130cc3ede7463ec9af8d62abb84992e2a0ef453c"; + this.version = "0.90.65"; + this.commit = "5bc10fcc8536a08ce7a099a0b4cbceb2dc9fc35b"; } async oauth() { diff --git a/frontend/src/assets/scss/base.scss b/frontend/src/assets/scss/base.scss index 461b72f6..8143cde0 100644 --- a/frontend/src/assets/scss/base.scss +++ b/frontend/src/assets/scss/base.scss @@ -85,13 +85,13 @@ .chartmarker { padding: 0px; width: 200px; - text-align: right; + text-align: left; } .chartmarker SPAN { font-size: 4pt; display: block; - color: #8b8b8b; + color: #b1b1b1; } .apexcharts-tooltip { diff --git a/frontend/src/assets/scss/layout.scss b/frontend/src/assets/scss/layout.scss index 2bd6e5ec..e0e573af 100644 --- a/frontend/src/assets/scss/layout.scss +++ b/frontend/src/assets/scss/layout.scss @@ -14,6 +14,34 @@ A:HOVER { color: lighten($text-color, 12%) !important; } +.modal-backdrop { + top: 0; + left: 0; + position: absolute; + display: block; + z-index: 10000; + width: 100%; + height: 100%; + background-color: #00000073; +} + +.modal { + z-index: 999999 !important; + display: block; +} + +.modal-dialog { + top: 20%; +} + +.modal-header { + padding: 0.5rem 1rem; +} + +.modal-footer { + padding: 0.5rem 1rem; +} + .text-muted { color: lighten($text-color, 30%) !important; } diff --git a/frontend/src/components/Dashboard/DashboardMessages.vue b/frontend/src/components/Dashboard/DashboardMessages.vue index c17bf54a..c5cc3a5e 100644 --- a/frontend/src/components/Dashboard/DashboardMessages.vue +++ b/frontend/src/components/Dashboard/DashboardMessages.vue @@ -81,13 +81,21 @@ serviceName (service) { return service.name || "Global Message" }, + async delete(m) { + await Api.message_delete(m.id) + const messages = await Api.messages() + this.$store.commit('setMessages', messages) + }, async deleteMessage(m) { - let c = confirm(`Are you sure you want to delete message '${m.title}'?`) - if (c) { - await Api.message_delete(m.id) - const messages = await Api.messages() - this.$store.commit('setMessages', messages) + const modal = { + visible: true, + title: "Delete Announcement", + body: `Are you sure you want to delete Announcement ${m.title}?`, + btnColor: "btn-danger", + btnText: "Delete Announcement", + func: () => this.delete(m), } + this.$store.commit("setModal", modal) } } } diff --git a/frontend/src/components/Dashboard/DashboardServices.vue b/frontend/src/components/Dashboard/DashboardServices.vue index d8c40247..42228a0a 100644 --- a/frontend/src/components/Dashboard/DashboardServices.vue +++ b/frontend/src/components/Dashboard/DashboardServices.vue @@ -1,5 +1,6 @@ - - - diff --git a/frontend/src/components/Dashboard/ServicesList.vue b/frontend/src/components/Dashboard/ServicesList.vue index c4273399..e00a10be 100644 --- a/frontend/src/components/Dashboard/ServicesList.vue +++ b/frontend/src/components/Dashboard/ServicesList.vue @@ -72,12 +72,14 @@ + + diff --git a/frontend/src/components/Service/FailuresBarChart.vue b/frontend/src/components/Service/FailuresBarChart.vue index d241d776..5720e60a 100644 --- a/frontend/src/components/Service/FailuresBarChart.vue +++ b/frontend/src/components/Service/FailuresBarChart.vue @@ -157,7 +157,8 @@ export default { }, async loadFailures() { this.loaded = false - const data = await Api.service_failures_data(this.service.id, this.toUnix(this.parseISO(this.start)), this.toUnix(this.parseISO(this.end)), this.group, true) + const startEnd = this.startEndParams(this.parseISO(this.start), this.parseISO(this.end), this.group) + const data = await Api.service_failures_data(this.service.id, startEnd.start, startEnd.end, this.group, true) this.loaded = true this.data = [{data: this.convertChartData(data)}] } diff --git a/frontend/src/components/Service/ServiceBlock.vue b/frontend/src/components/Service/ServiceBlock.vue index 735e6d5d..eaa6e31f 100644 --- a/frontend/src/components/Service/ServiceBlock.vue +++ b/frontend/src/components/Service/ServiceBlock.vue @@ -61,7 +61,6 @@ const Analytics = () => import(/* webpackChunkName: "service" */ './Analytics'); const ServiceChart = () => import(/* webpackChunkName: "service" */ "./ServiceChart"); const ServiceTopStats = () => import(/* webpackChunkName: "service" */ "@/components/Service/ServiceTopStats"); -const Graphing = () => import(/* webpackChunkName: "service" */ '../../graphing'); export default { name: 'ServiceBlock', diff --git a/frontend/src/components/Service/ServiceChart.vue b/frontend/src/components/Service/ServiceChart.vue index fca32d5a..482ff7cf 100644 --- a/frontend/src/components/Service/ServiceChart.vue +++ b/frontend/src/components/Service/ServiceChart.vue @@ -195,17 +195,14 @@ methods: { async chartHits(val) { this.ready = false - const start = val.start_time - const end = this.toUnix(new Date()) - this.data = await Api.service_hits(this.service.id, start, end, val.interval, false) - if (this.data === null && val.interval !== "5m") { - await this.chartHits({start_time: val.start_time, interval: "5m"}) - } - this.ping_data = await Api.service_ping(this.service.id, start, end, val.interval, false) + const end = this.endOf("hour", this.now()) + const start = this.beginningOf("hour", this.fromUnix(val.start_time)) + this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(end), val.interval, false) + this.ping_data = await Api.service_ping(this.service.id, this.toUnix(start), this.toUnix(end), val.interval, false) this.series = [ {name: "Latency", ...this.convertToChartData(this.data)}, - {name: "Ping", ...this.convertToChartData(this.ping_data)}, + {name: "Ping", ...this.convertToChartData(this.ping_data)}, ] this.ready = true } diff --git a/frontend/src/forms/Login.vue b/frontend/src/forms/Login.vue index 87ee8332..1411b64b 100644 --- a/frontend/src/forms/Login.vue +++ b/frontend/src/forms/Login.vue @@ -10,7 +10,7 @@
- +
diff --git a/frontend/src/forms/Notifier.vue b/frontend/src/forms/Notifier.vue index eb06d1d9..73866ac3 100644 --- a/frontend/src/forms/Notifier.vue +++ b/frontend/src/forms/Notifier.vue @@ -149,11 +149,11 @@
- Service '{{$store.getters.serviceById(log.service).name}}' + Service {{log.service}} {{log.success ? "Success Triggered" : "Failure Triggered"}} -
+
{{log.message}}
diff --git a/frontend/src/mixin.js b/frontend/src/mixin.js index e211e907..f66c7d7d 100644 --- a/frontend/src/mixin.js +++ b/frontend/src/mixin.js @@ -1,5 +1,5 @@ import Vue from "vue"; -const { startOfDay, startOfWeek, endOfMonth, startOfToday, startOfTomorrow, startOfYesterday, endOfYesterday, endOfTomorrow, endOfToday, endOfDay, startOfMonth, lastDayOfMonth, subSeconds, getUnixTime, fromUnixTime, differenceInSeconds, formatDistance, addMonths, addSeconds, isWithinInterval } = require('date-fns') +const { startOfDay, startOfHour, startOfWeek, endOfMonth, endOfHour, startOfToday, startOfTomorrow, startOfYesterday, endOfYesterday, endOfTomorrow, endOfToday, endOfDay, startOfMonth, lastDayOfMonth, subSeconds, getUnixTime, fromUnixTime, differenceInSeconds, formatDistance, addMonths, addSeconds, isWithinInterval } = require('date-fns') import formatDistanceToNow from 'date-fns/formatDistanceToNow' import format from 'date-fns/format' import parseISO from 'date-fns/parseISO' @@ -59,6 +59,8 @@ export default Vue.mixin({ }, endOf(method, val) { switch (method) { + case "hour": + return endOfHour(val) case "day": return endOfDay(val) case "today": @@ -70,10 +72,17 @@ export default Vue.mixin({ case "month": return endOfMonth(val) } - return roundToNearestMinutes(val) + return val + }, + startEndParams(start, end, group) { + start = this.beginningOf("hour", start) + end = this.endOf("hour", end) + return {start: this.toUnix(start), end: this.toUnix(end), group: group} }, beginningOf(method, val) { switch (method) { + case "hour": + return startOfHour(val) case "day": return startOfDay(val) case "today": @@ -83,11 +92,11 @@ export default Vue.mixin({ case "yesterday": return startOfYesterday() case "week": - return startOfWeek() + return startOfWeek(val) case "month": return startOfMonth(val) } - return roundToNearestMinutes(val) + return val }, isZero(val) { return getUnixTime(parseISO(val)) <= 0 diff --git a/frontend/src/pages/Dashboard.vue b/frontend/src/pages/Dashboard.vue index 17f20d2f..d1b08cfa 100644 --- a/frontend/src/pages/Dashboard.vue +++ b/frontend/src/pages/Dashboard.vue @@ -1,16 +1,20 @@