status page
This commit is contained in:
parent
68c3fcb909
commit
537776f0c5
8 changed files with 168 additions and 50 deletions
|
@ -31,9 +31,15 @@ class StatusController extends Controller
|
|||
{
|
||||
$endpoints = array_keys(config('status.check'));
|
||||
$events = DB::table('status_events')
|
||||
->where('event_date', '>', Carbon::now()->subWeek(1)->format('Y-m-d'))
|
||||
->get();
|
||||
$incidents = [];
|
||||
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
$date = Carbon::now()->subDay($i);
|
||||
$incidents[$date->format('M j, Y')] = [];
|
||||
}
|
||||
|
||||
foreach ($events as $row) {
|
||||
$date = Carbon::createFromFormat('Y-m-d H:i:s', $row->event_date);
|
||||
$incidents[$date->format('M j, Y')][] = [
|
||||
|
@ -60,7 +66,7 @@ class StatusController extends Controller
|
|||
}
|
||||
|
||||
[$address, $port, $protocol] = explode('/', $endpoints[$name]);
|
||||
$status = new Status($address, $port, $protocol);
|
||||
$status = new Status($address, $port, $protocol, true, ($_GET['history'] ?? '1') !== '0');
|
||||
|
||||
if ($status->state === null) {
|
||||
$status->check();
|
||||
|
|
|
@ -220,9 +220,18 @@ class Net
|
|||
CURLOPT_CONNECTTIMEOUT => 2,
|
||||
CURLOPT_TIMEOUT => 4,
|
||||
CURLOPT_USERAGENT => 'Sakura/1.0 (+https://sakura.flash.moe)',
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false, // for CF flexible SSL
|
||||
]);
|
||||
|
||||
switch (strtolower($method)) {
|
||||
case 'head':
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_NOBODY => true,
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_POST => is_array($params) ? count($params) : 1,
|
||||
|
|
|
@ -24,24 +24,25 @@ class Status
|
|||
public const ERROR = 1;
|
||||
public const FAIL = 0;
|
||||
|
||||
public function __construct(string $address, int $port, string $protocol = '', bool $populate = true)
|
||||
public function __construct(string $address, int $port, string $protocol = '', bool $populate = true, bool $history = true)
|
||||
{
|
||||
$this->address = $address;
|
||||
$this->port = $port;
|
||||
$this->protocol = $protocol;
|
||||
|
||||
if ($populate) {
|
||||
$this->populate();
|
||||
$this->populate($history);
|
||||
}
|
||||
}
|
||||
|
||||
public function populate(): void
|
||||
public function populate(bool $history = true): void
|
||||
{
|
||||
$this->history = DB::table('status_history')
|
||||
->where('history_name', $this->address)
|
||||
->where('history_port', $this->port)
|
||||
->where('history_protocol', $this->protocol)
|
||||
->orderBy('history_date', 'desc')
|
||||
->limit($history ? 10 : 1)
|
||||
->get();
|
||||
|
||||
$this->state = isset($this->history[0]) ? intval($this->history[0]->history_state) : null;
|
||||
|
@ -50,7 +51,14 @@ class Status
|
|||
public function check(): int
|
||||
{
|
||||
$this->state = static::FAIL;
|
||||
$sock = checkdnsrr($this->address) ? fsockopen($this->address, $this->port, $errno, $errstr, 1) : false;
|
||||
|
||||
$sock = Net::detectIPVersion(
|
||||
$this->address[0] === '[' && $this->address[strlen($this->address) - 1] === ']'
|
||||
? substr($this->address, 1, -1)
|
||||
: $this->address
|
||||
) !== 0 || dns_get_record($this->address, DNS_A | DNS_A6 | DNS_MX)
|
||||
? fsockopen($this->address, $this->port, $errno, $errstr, 1)
|
||||
: false;
|
||||
|
||||
if ($sock !== false) {
|
||||
fclose($sock);
|
||||
|
@ -64,7 +72,7 @@ class Status
|
|||
);
|
||||
|
||||
if ($header !== false && strtolower(substr($header, 0, 4)) == 'http') {
|
||||
list($protocol, $response, $text) = explode(' ', $header, 3);
|
||||
[$protocol, $response, $text] = explode(' ', $header, 3);
|
||||
|
||||
if ($response >= 400 && $response < 500) {
|
||||
$this->state = static::ERROR;
|
||||
|
|
|
@ -211,6 +211,10 @@ repo['Sakura'] = https://github.com/flashwave/sakura
|
|||
|
||||
; Status settings
|
||||
[status]
|
||||
; Format:
|
||||
; check['display name'] = "address/port/protocol"
|
||||
; protocol only really matters if you're pinging to http or https
|
||||
; in which case it should be set to the respective value
|
||||
check['flash.moe http'] = "flash.moe/80/http"
|
||||
check['flash.moe https'] = "flash.moe/443/https"
|
||||
|
||||
|
|
11
resources/assets/less/yuuno/bem/status.less
Normal file
11
resources/assets/less/yuuno/bem/status.less
Normal file
|
@ -0,0 +1,11 @@
|
|||
.status {
|
||||
&__services {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__service {
|
||||
padding: 10px 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
@import "bem/profile";
|
||||
@import "bem/settings";
|
||||
@import "bem/sidepanel-table";
|
||||
@import "bem/status";
|
||||
@import "bem/topic";
|
||||
@import "bem/uploader";
|
||||
@import "bem/user";
|
||||
|
|
59
resources/views/aitemu/status/index.twig
Normal file
59
resources/views/aitemu/status/index.twig
Normal file
|
@ -0,0 +1,59 @@
|
|||
{% extends 'master.twig' %}
|
||||
|
||||
{% set title = 'Status' %}
|
||||
{% set banner = '/images/status-banner.png' %}
|
||||
{% set banner_large = true %}
|
||||
|
||||
{% block banner_content %}
|
||||
<div class="banner__bottom">
|
||||
<div class="status__overall status__overall--good">
|
||||
<span class="status__overall-icon"></span> <span class="status__overall-text">all services are go!</span>
|
||||
</div>
|
||||
<div class="status__last-update">
|
||||
this is an not as static as it was before design preview
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="platform status__response">
|
||||
<div class="status__services">
|
||||
{% for name in endpoints %}
|
||||
<div class="status__service status__service--good">
|
||||
<div class="status__service-icon"></div>
|
||||
<div class="status__service-name">{{ name }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="status__graph">graph goes here</div>
|
||||
</div>
|
||||
<div class="platform status__incidents">
|
||||
{% for date, events in incidents %}
|
||||
<div class="status__incident">
|
||||
<div class="status__incident-date">{{ date }}</div>
|
||||
<div class="status__updates">
|
||||
{% if events|length > 0 %}
|
||||
{% for event in events %}
|
||||
{% set state = event.state|lower %}
|
||||
<div class="status__update status__update--{% if state == 'resolved' %}good{% elseif state == 'monitoring' %}bad{% else %}busy{% endif %}">
|
||||
<div class="status__update-meta">
|
||||
<div class="status__update-time">{{ event.time }} UTC</div>
|
||||
<div class="status__update-state">{{ event.state }}</div>
|
||||
</div>
|
||||
<div class="status__update-text">
|
||||
{{ event.comment }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="status__update status__update">
|
||||
<div class="status__update-text">
|
||||
No incidents occurred.
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,59 +1,79 @@
|
|||
{% extends '@aitemu/master.twig' %}
|
||||
{% extends 'master.twig' %}
|
||||
|
||||
{% set title = 'Status' %}
|
||||
{% set banner = '/images/status-banner.png' %}
|
||||
{% set banner_large = true %}
|
||||
|
||||
{% block banner_content %}
|
||||
<div class="banner__bottom">
|
||||
<div class="status__overall status__overall--good">
|
||||
<span class="status__overall-icon"></span> <span class="status__overall-text">all services are go!</span>
|
||||
</div>
|
||||
<div class="status__last-update">
|
||||
this is an not as static as it was before design preview
|
||||
</div>
|
||||
</div>
|
||||
{% block js %}
|
||||
<script>
|
||||
var yuunoStatusStates = [
|
||||
{"text": "Offline", "colour": "#800"},
|
||||
{"text": "Experiencing Issues", "colour": "#840"},
|
||||
{"text": "Online", "colour": "#080"}
|
||||
];
|
||||
|
||||
var yuunoStatusEndpoints = {{ endpoints|json_encode|raw }};
|
||||
|
||||
function yuunoCheckStatus(endpoint) {
|
||||
var client = new Sakura.AJAX;
|
||||
client.SetUrl("{{ route('status.data') }}?history=0&name=" + endpoint);
|
||||
client.AddCallback(200, function () {
|
||||
var result = client.JSON(),
|
||||
field = document.querySelector('[data-status-name="' + endpoint + '"]');
|
||||
|
||||
if (result.error) {
|
||||
if (field) {
|
||||
field.innerText = 'error';
|
||||
}
|
||||
|
||||
var error = new Sakura.Dialogue;
|
||||
error.Title = "Status";
|
||||
error.Text = result.error;
|
||||
error.Display();
|
||||
} else {
|
||||
var state = yuunoStatusStates[result.state];
|
||||
field.innerText = state.text;
|
||||
field.style.color = state.colour;
|
||||
}
|
||||
});
|
||||
client.Start(Sakura.HTTPMethod.GET);
|
||||
}
|
||||
|
||||
window.addEventListener("load", function () {
|
||||
for (var i in yuunoStatusEndpoints) {
|
||||
var endpoint = yuunoStatusEndpoints[i];
|
||||
yuunoCheckStatus(endpoint);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="platform status__response">
|
||||
<div class="content status">
|
||||
<div class="content__header">Status</div>
|
||||
<div class="status__services">
|
||||
{% for name in endpoints %}
|
||||
<div class="status__service status__service--good">
|
||||
<div class="status__service-icon"></div>
|
||||
<div class="status__service-name">{{ name }}</div>
|
||||
<div class="status__service">
|
||||
<h1>{{ name }}</h1>
|
||||
<h3 data-status-name="{{ name }}">loading...</h3>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="status__graph">graph goes here</div>
|
||||
</div>
|
||||
<div class="platform status__incidents">
|
||||
<div class="content__header">Incidents</div>
|
||||
{% for date, events in incidents %}
|
||||
<div class="status__incident">
|
||||
<div class="status__incident-date">{{ date }}</div>
|
||||
<div class="status__updates">
|
||||
{% if events|length > 0 %}
|
||||
{% for event in events %}
|
||||
{% set state = event.state|lower %}
|
||||
<div class="status__update status__update--{% if state == 'resolved' %}good{% elseif state == 'monitoring' %}bad{% else %}busy{% endif %}">
|
||||
<div class="status__update-meta">
|
||||
<div class="status__update-time">{{ event.time }} UTC</div>
|
||||
<div class="status__update-state">{{ event.state }}</div>
|
||||
</div>
|
||||
<div class="status__update-text">
|
||||
{{ event.comment }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="status__update status__update">
|
||||
<div class="status__update-text">
|
||||
No incidents occurred.
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="news__head">{{ date }}</div>
|
||||
{% if events|length > 0 %}
|
||||
<table>
|
||||
{% for event in events %}
|
||||
{% set state = event.state|lower %}
|
||||
<tr>
|
||||
<td><b><span style="font-size: .8em">{{ event.time }} UTC</span></b></td>
|
||||
<td><b style="font-size: .8em; color: {% if state == 'resolved' %}#080{% elseif state == 'monitoring' %}#800{% else %}#840{% endif %}">{{ event.state }}</b></td>
|
||||
<td>{{ event.comment }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<i>No incidents occurred.</i>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
Reference in a new issue