From 6d90db74ab1975579483b9c2c5b83f0ae54cb83f Mon Sep 17 00:00:00 2001 From: Owen Melbourne Date: Mon, 19 Aug 2019 12:03:16 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A8=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Checkers/Certificate.php | 37 ++++++++- app/Checkers/Dns.php | 36 ++++----- app/Notifications/CertificateHasExpired.php | 81 +++++++++++++++++++ app/Notifications/CertificateIsExpiring.php | 81 +++++++++++++++++++ app/Notifications/CertificateIsInvalid.php | 81 +++++++++++++++++++ app/Notifications/CertificateIsWeak.php | 81 +++++++++++++++++++ app/Notifications/CertificateWillExpire.php | 81 +++++++++++++++++++ app/Notifications/DnsHasChanged.php | 1 + public/js/maelstrom.js | 81 ++++++++++++++++--- resources/js/components/CertificateReport.js | 11 ++- resources/js/components/DnsReport.js | 7 +- resources/js/components/RobotsReport.js | 7 +- resources/js/components/UptimeReport.js | 8 +- resources/js/helpers.js | 2 + resources/views/mail/ssl-expired.blade.php | 14 ++++ resources/views/mail/ssl-expiring.blade.php | 14 ++++ resources/views/mail/ssl-invalid.blade.php | 12 +++ resources/views/mail/ssl-weak.blade.php | 14 ++++ .../views/mail/ssl-will-expire.blade.php | 14 ++++ 19 files changed, 623 insertions(+), 40 deletions(-) create mode 100644 app/Notifications/CertificateHasExpired.php create mode 100644 app/Notifications/CertificateIsExpiring.php create mode 100644 app/Notifications/CertificateIsInvalid.php create mode 100644 app/Notifications/CertificateIsWeak.php create mode 100644 app/Notifications/CertificateWillExpire.php create mode 100644 resources/views/mail/ssl-expired.blade.php create mode 100644 resources/views/mail/ssl-expiring.blade.php create mode 100644 resources/views/mail/ssl-invalid.blade.php create mode 100644 resources/views/mail/ssl-weak.blade.php create mode 100644 resources/views/mail/ssl-will-expire.blade.php diff --git a/app/Checkers/Certificate.php b/app/Checkers/Certificate.php index a9715c8..ee7a1d6 100644 --- a/app/Checkers/Certificate.php +++ b/app/Checkers/Certificate.php @@ -5,12 +5,19 @@ use App\Website; use App\CertificateScan; use VisualAppeal\SslLabs; +use App\Notifications\CertificateIsWeak; use Spatie\SslCertificate\SslCertificate; +use App\Notifications\CertificateIsInvalid; +use App\Notifications\CertificateWillExpire; +use App\Notifications\CertificateHasExpired; +use App\Notifications\CertificateIsExpiring; class Certificate { private $website; + private $scan; + public function __construct(Website $website) { $this->website = $website; @@ -52,17 +59,43 @@ private function fetch() if ($endpoint->statusMessage === 'Ready') { $scan->grade = $endpoint->grade; $this->website->certificates()->save($scan); + $this->scan = $scan; break; } } if (app()->runningInConsole()) { - dump($scan->exists ? 'Cert Updated' : 'Pending...'); + if (!$scan->exists) { + dump('Scan still in progress...'); + dump($result); + } } } - private function notify() + private function notify($notification = null) { + $this->scan = $this->website->certificates()->latest()->first(); + + if (!$this->scan) { + return null; + } + + if (!$this->scan->was_valid) { + $notification = new CertificateIsInvalid($this->website, $this->scan); + } elseif ($this->scan->did_expire) { + $notification = new CertificateHasExpired($this->website, $this->scan); + } elseif (now()->diffInHours($this->scan->valid_to) <= 24) { + $notification = new CertificateIsExpiring($this->website, $this->scan); + } elseif (now()->diffInDays($this->scan->valid_to) <= 7) { + $notification = new CertificateWillExpire($this->website, $this->scan); + } elseif (in_array($this->scan->grade, ['C', 'D', 'E', 'F'])) { + $notification = new CertificateIsWeak($this->website, $this->scan); + } + + if (!$notification) { + return null; + } + $this->website->user->notify($notification); } } diff --git a/app/Checkers/Dns.php b/app/Checkers/Dns.php index 765962d..6b89ddb 100644 --- a/app/Checkers/Dns.php +++ b/app/Checkers/Dns.php @@ -28,24 +28,24 @@ public function run() private function fetch() { -// $response = (new DNSParser('array'))->lookup($this->website->dns_hostname); -// -// $flat = collect($response['records'])->transform(function ($item) { -// return sprintf( -// '%s %s %s ttl:%d', -// $item['host'], -// $item['type'], -// $item['ip'] ?? $item['target'] ?? $item['mname'] ?? $item['txt'] ?? $item['ipv6'] ?? '', -// $item['ttl'] -// ); -// })->sort()->values()->implode("\n"); -// -// $scan = new DnsScan([ -// 'records' => $response['records'], -// 'flat' => $flat, -// ]); -// -// $this->website->dns()->save($scan); + $response = (new DNSParser('array'))->lookup($this->website->dns_hostname); + + $flat = collect($response['records'])->transform(function ($item) { + return sprintf( + '%s %s %s ttl:%d', + $item['host'], + $item['type'], + $item['ip'] ?? $item['target'] ?? $item['mname'] ?? $item['txt'] ?? $item['ipv6'] ?? '', + $item['ttl'] + ); + })->sort()->values()->implode("\n"); + + $scan = new DnsScan([ + 'records' => $response['records'], + 'flat' => $flat, + ]); + + $this->website->dns()->save($scan); } private function compare() diff --git a/app/Notifications/CertificateHasExpired.php b/app/Notifications/CertificateHasExpired.php new file mode 100644 index 0000000..263a93a --- /dev/null +++ b/app/Notifications/CertificateHasExpired.php @@ -0,0 +1,81 @@ +website = $website; + $this->scan = $scan; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database', 'mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('🔒 SSL has expired on: ' . $this->website->url) + ->markdown('mail.ssl-expired', [ + 'website' => $this->website, + 'scan' => $this->scan, + ]); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + 'website_id' => $this->website->id, + 'website' => $this->website->certificate_hostname, + 'was_valid' => $this->scan->was_valid, + 'did_expire' => $this->scan->did_expire, + 'grade' => $this->scan->grade, + ]; + } +} diff --git a/app/Notifications/CertificateIsExpiring.php b/app/Notifications/CertificateIsExpiring.php new file mode 100644 index 0000000..512b5ba --- /dev/null +++ b/app/Notifications/CertificateIsExpiring.php @@ -0,0 +1,81 @@ +website = $website; + $this->scan = $scan; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database', 'mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('🔒 SSL expires today on: ' . $this->website->url) + ->markdown('mail.ssl-expiring', [ + 'website' => $this->website, + 'scan' => $this->scan, + ]); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + 'website_id' => $this->website->id, + 'website' => $this->website->certificate_hostname, + 'was_valid' => $this->scan->was_valid, + 'did_expire' => $this->scan->did_expire, + 'grade' => $this->scan->grade, + ]; + } +} diff --git a/app/Notifications/CertificateIsInvalid.php b/app/Notifications/CertificateIsInvalid.php new file mode 100644 index 0000000..451d1b7 --- /dev/null +++ b/app/Notifications/CertificateIsInvalid.php @@ -0,0 +1,81 @@ +website = $website; + $this->scan = $scan; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database', 'mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('🔒 SSL is invalid on: ' . $this->website->url) + ->markdown('mail.ssl-invalid', [ + 'website' => $this->website, + 'scan' => $this->scan, + ]); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + 'website_id' => $this->website->id, + 'website' => $this->website->certificate_hostname, + 'was_valid' => $this->scan->was_valid, + 'did_expire' => $this->scan->did_expire, + 'grade' => $this->scan->grade, + ]; + } +} diff --git a/app/Notifications/CertificateIsWeak.php b/app/Notifications/CertificateIsWeak.php new file mode 100644 index 0000000..9bb3728 --- /dev/null +++ b/app/Notifications/CertificateIsWeak.php @@ -0,0 +1,81 @@ +website = $website; + $this->scan = $scan; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database', 'mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('🔒 SSL is ineffective on: ' . $this->website->url) + ->markdown('mail.ssl-weak', [ + 'website' => $this->website, + 'scan' => $this->scan, + ]); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + 'website_id' => $this->website->id, + 'website' => $this->website->certificate_hostname, + 'was_valid' => $this->scan->was_valid, + 'did_expire' => $this->scan->did_expire, + 'grade' => $this->scan->grade, + ]; + } +} diff --git a/app/Notifications/CertificateWillExpire.php b/app/Notifications/CertificateWillExpire.php new file mode 100644 index 0000000..62365b4 --- /dev/null +++ b/app/Notifications/CertificateWillExpire.php @@ -0,0 +1,81 @@ +website = $website; + $this->scan = $scan; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database', 'mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('🔒 SSL expiring this week on: ' . $this->website->url) + ->markdown('mail.ssl-will-expire', [ + 'website' => $this->website, + 'scan' => $this->scan, + ]); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + 'website_id' => $this->website->id, + 'website' => $this->website->certificate_hostname, + 'was_valid' => $this->scan->was_valid, + 'did_expire' => $this->scan->did_expire, + 'grade' => $this->scan->grade, + ]; + } +} diff --git a/app/Notifications/DnsHasChanged.php b/app/Notifications/DnsHasChanged.php index 3e57882..24648d8 100644 --- a/app/Notifications/DnsHasChanged.php +++ b/app/Notifications/DnsHasChanged.php @@ -12,6 +12,7 @@ class DnsHasChanged extends Notification { use Queueable; + /** * @var Website */ diff --git a/public/js/maelstrom.js b/public/js/maelstrom.js index 5f86301..344b202 100644 --- a/public/js/maelstrom.js +++ b/public/js/maelstrom.js @@ -322213,6 +322213,7 @@ function _defineProperty(obj, key, value) { + var TreeNode = antd__WEBPACK_IMPORTED_MODULE_2__["Tree"].TreeNode; var CertificateReport = @@ -322339,7 +322340,7 @@ function (_React$Component) { } }, _this.state.grade); } - }), react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { + }), _this.state.issuer && react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "mt-8 pr-12" }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "font-bold mb-2" @@ -322404,6 +322405,10 @@ function (_React$Component) { }); _defineProperty(_assertThisInitialized(_this), "renderReport", function () { + if (!_this.state.issuer) { + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(_helpers__WEBPACK_IMPORTED_MODULE_3__["NoData"], null); + } + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "flex" }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { @@ -322649,6 +322654,7 @@ function _defineProperty(obj, key, value) { + var DnsReport = /*#__PURE__*/ function (_React$Component) { @@ -322726,9 +322732,14 @@ function (_React$Component) { var _this$state = _this.state, now = _this$state.now, previous = _this$state.previous; + + if (!now) { + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(_helpers__WEBPACK_IMPORTED_MODULE_4__["NoData"], null); + } + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_1___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "flex" - }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { + }, previous && react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "w-1/2" }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("h3", null, "Before (", previous && Object(_helpers__WEBPACK_IMPORTED_MODULE_4__["formatDateTime"])(previous.created_at), ")")), react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "w-1/2" @@ -322976,6 +322987,7 @@ function _defineProperty(obj, key, value) { + var RobotsReport = /*#__PURE__*/ function (_React$Component) { @@ -323053,9 +323065,14 @@ function (_React$Component) { var _this$state = _this.state, now = _this$state.now, previous = _this$state.previous; + + if (!now) { + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(_helpers__WEBPACK_IMPORTED_MODULE_4__["NoData"], null); + } + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_1___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "flex" - }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { + }, previous && react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "w-1/2" }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("h3", null, "Before (", previous && Object(_helpers__WEBPACK_IMPORTED_MODULE_4__["formatDateTime"])(previous.created_at), ")")), react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "w-1/2" @@ -323324,6 +323341,7 @@ function _defineProperty(obj, key, value) { + var UptimeReport = /*#__PURE__*/ function (_React$Component) { @@ -323408,9 +323426,10 @@ function (_React$Component) { }); _defineProperty(_assertThisInitialized(_this), "renderReport", function () { - var _this$state = _this.state, - now = _this$state.now, - previous = _this$state.previous; + if (!_this.state.events.length) { + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(_helpers__WEBPACK_IMPORTED_MODULE_4__["NoData"], null); + } + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { className: "flex flex-wrap" }, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { @@ -323431,9 +323450,9 @@ function (_React$Component) { }); _defineProperty(_assertThisInitialized(_this), "renderBusy", function () { - var _this$state2 = _this.state, - now = _this$state2.now, - previous = _this$state2.previous; + var _this$state = _this.state, + now = _this$state.now, + previous = _this$state.previous; if (now && previous) { return _this.renderReport(); @@ -323515,9 +323534,9 @@ function (_React$Component) { }, { key: "renderCurrentStatus", value: function renderCurrentStatus() { - var _this$state3 = this.state, - online = _this$state3.online, - online_time = _this$state3.online_time; + var _this$state2 = this.state, + online = _this$state2.online, + online_time = _this$state2.online_time; return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", null, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("h4", null, react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(antd__WEBPACK_IMPORTED_MODULE_2__["Icon"], { style: { marginRight: 10 @@ -323717,21 +323736,57 @@ function (_React$Component) { /*!*********************************!*\ !*** ./resources/js/helpers.js ***! \*********************************/ -/*! exports provided: formatDateTime, GREEN, YELLOW, RED */ +/*! exports provided: formatDateTime, NoData, GREEN, YELLOW, RED */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "formatDateTime", function() { return formatDateTime; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "NoData", function() { return NoData; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GREEN", function() { return GREEN; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "YELLOW", function() { return YELLOW; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RED", function() { return RED; }); /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! moment */ "./node_modules/moment/moment.js"); /* harmony import */ var moment__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(moment__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ "./node_modules/react/index.js"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__); + var formatDateTime = function formatDateTime(string) { return moment__WEBPACK_IMPORTED_MODULE_0___default()(string).utc().format('ddd Do MMM "YY @ H:mma'); }; +var NoData = function NoData() { + return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_1___default.a.Fragment, null, " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { + className: "ant-empty ant-empty-normal" + }, " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("div", { + className: "ant-empty-image" + }, " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("svg", { + width: "64", + height: "41", + viewBox: "0 0 64 41", + xmlns: "http://www.w3.org/2000/svg" + }, " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("g", { + transform: "translate(0 1)", + fill: "none", + fillRule: "evenodd" + }, " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("ellipse", { + fill: "#F5F5F5", + cx: "32", + cy: "33", + rx: "32", + ry: "7" + }), " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("g", { + fillRule: "nonzero", + stroke: "#D9D9D9" + }, " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("path", { + d: "M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z" + }), " ", react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("path", { + d: "M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z", + fill: "#FAFAFA" + }), " "), " "), " "), " "), react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement("p", { + className: "ant-empty-description" + }, "No Data"))); +}; var GREEN = '#72c040'; var YELLOW = '#efaf41'; var RED = '#e23c39'; diff --git a/resources/js/components/CertificateReport.js b/resources/js/components/CertificateReport.js index d7f0014..0e168ae 100644 --- a/resources/js/components/CertificateReport.js +++ b/resources/js/components/CertificateReport.js @@ -1,6 +1,7 @@ import React from 'react'; import { Card, Button, Spin, Progress, Icon, Tree, Tag } from 'antd'; import {formatDateTime, GREEN, RED, YELLOW} from "../helpers"; +import { NoData } from '../helpers' const TreeNode = Tree.TreeNode; @@ -80,16 +81,16 @@ export default class CertificateReport extends React.Component { format={ () => { this.state.grade } } /> -
+ { this.state.issuer &&
Issued By:
- { this.state.issuer} + { this.state.issuer }
Issued On:
{ formatDateTime(this.state.valid_from) }
-
+
} ) }; @@ -126,6 +127,10 @@ export default class CertificateReport extends React.Component { }; renderReport = () => { + if (!this.state.issuer) { + return + } + return (
diff --git a/resources/js/components/DnsReport.js b/resources/js/components/DnsReport.js index ffc1d4b..206834a 100644 --- a/resources/js/components/DnsReport.js +++ b/resources/js/components/DnsReport.js @@ -2,6 +2,7 @@ import React from 'react'; import { Card, Button, Spin } from 'antd'; import ReactDiffViewer from 'react-diff-viewer' import { formatDateTime } from "../helpers"; +import { NoData } from '../helpers' export default class DnsReport extends React.Component { @@ -42,10 +43,14 @@ export default class DnsReport extends React.Component { renderReport = () => { const { now, previous } = this.state; + if (!now) { + return + } + return ( <>
-

Before ({ previous && formatDateTime(previous.created_at) })

+ { previous &&

Before ({ previous && formatDateTime(previous.created_at) })

}

Now ({ now && formatDateTime(now.created_at) })

diff --git a/resources/js/components/RobotsReport.js b/resources/js/components/RobotsReport.js index 00eb0d6..79af50b 100644 --- a/resources/js/components/RobotsReport.js +++ b/resources/js/components/RobotsReport.js @@ -2,6 +2,7 @@ import React from 'react'; import { Card, Button, Spin } from 'antd'; import ReactDiffViewer from 'react-diff-viewer' import { formatDateTime } from "../helpers"; +import { NoData } from '../helpers' export default class RobotsReport extends React.Component { @@ -42,10 +43,14 @@ export default class RobotsReport extends React.Component { renderReport = () => { const { now, previous } = this.state; + if (!now) { + return + } + return ( <>
-

Before ({ previous && formatDateTime(previous.created_at) })

+ { previous &&

Before ({ previous && formatDateTime(previous.created_at) })

}

Now ({ now && formatDateTime(now.created_at) })

diff --git a/resources/js/components/UptimeReport.js b/resources/js/components/UptimeReport.js index a8f2861..f60f8f0 100644 --- a/resources/js/components/UptimeReport.js +++ b/resources/js/components/UptimeReport.js @@ -2,6 +2,7 @@ import React from 'react'; import { Card, Button, Spin, Progress, Icon, Table, Tag } from 'antd'; import { Line } from 'react-chartjs-2' import { formatDateTime, GREEN, YELLOW, RED } from '../helpers'; +import { NoData } from '../helpers' export default class UptimeReport extends React.Component { @@ -230,8 +231,11 @@ export default class UptimeReport extends React.Component { ) }; - renderReport = () => { - const { now, previous } = this.state; + renderReport = () =>{ + + if (!this.state.events.length) { + return + } return (
diff --git a/resources/js/helpers.js b/resources/js/helpers.js index 962a0e7..dba90c5 100644 --- a/resources/js/helpers.js +++ b/resources/js/helpers.js @@ -1,8 +1,10 @@ import moment from 'moment'; +import React from "react"; export const formatDateTime = string => { return moment(string).utc().format('ddd Do MMM "YY @ H:mma') }; +export const NoData = () => <>

No Data

export const GREEN = '#72c040'; export const YELLOW = '#efaf41'; diff --git a/resources/views/mail/ssl-expired.blade.php b/resources/views/mail/ssl-expired.blade.php new file mode 100644 index 0000000..69a5418 --- /dev/null +++ b/resources/views/mail/ssl-expired.blade.php @@ -0,0 +1,14 @@ +@component('mail::message') +# 🔒 The SSL has expired on: {{ $website->url }} + +SSL expired {{ now()->diffAsCarbonInterval($scan->valid_to)->forHumans(['join' => true]) }} ago. + +Please advise the account manager to resolve. + +@component('mail::button', ['url' => $website->show_link]) + Open Monitor +@endcomponent + +Thanks,
+{{ config('app.name') }} +@endcomponent diff --git a/resources/views/mail/ssl-expiring.blade.php b/resources/views/mail/ssl-expiring.blade.php new file mode 100644 index 0000000..74df729 --- /dev/null +++ b/resources/views/mail/ssl-expiring.blade.php @@ -0,0 +1,14 @@ +@component('mail::message') +# 🔒 The SSL is expiring today on: {{ $website->url }} + +The SSL will expire in {{ now()->diffAsCarbonInterval($scan->valid_to)->forHumans(['join' => true]) }} + +Please liaise with the account manager to resolve. + +@component('mail::button', ['url' => $website->show_link]) + Open Monitor +@endcomponent + +Thanks,
+{{ config('app.name') }} +@endcomponent diff --git a/resources/views/mail/ssl-invalid.blade.php b/resources/views/mail/ssl-invalid.blade.php new file mode 100644 index 0000000..40d06f8 --- /dev/null +++ b/resources/views/mail/ssl-invalid.blade.php @@ -0,0 +1,12 @@ +@component('mail::message') +# 🔒 Invalid SSL on: {{ $website->url }} + +Please advise the account manager to resolve. + +@component('mail::button', ['url' => $website->show_link]) + Open Monitor +@endcomponent + +Thanks,
+{{ config('app.name') }} +@endcomponent diff --git a/resources/views/mail/ssl-weak.blade.php b/resources/views/mail/ssl-weak.blade.php new file mode 100644 index 0000000..dfe7196 --- /dev/null +++ b/resources/views/mail/ssl-weak.blade.php @@ -0,0 +1,14 @@ +@component('mail::message') +# 🔒 Weak SSL found on: {{ $website->url }} + +Anything less than a Grade B SSL is considered weak and will get flagged. + +Current rating is: {{ $scan->grade }} + +@component('mail::button', ['url' => $website->show_link]) + Open Monitor +@endcomponent + +Thanks,
+{{ config('app.name') }} +@endcomponent diff --git a/resources/views/mail/ssl-will-expire.blade.php b/resources/views/mail/ssl-will-expire.blade.php new file mode 100644 index 0000000..3334a74 --- /dev/null +++ b/resources/views/mail/ssl-will-expire.blade.php @@ -0,0 +1,14 @@ +@component('mail::message') +# 🔒 The SSL is expiring this week on: {{ $website->url }} + +The SSL will expire in {{ now()->diffAsCarbonInterval($scan->valid_to)->forHumans(['join' => true]) }} + +Please liaise with the account manager to resolve. + +@component('mail::button', ['url' => $website->show_link]) + Open Monitor +@endcomponent + +Thanks,
+{{ config('app.name') }} +@endcomponent