some status stuff
This commit is contained in:
parent
9f7416e32b
commit
fa0c4ff0d2
6 changed files with 234 additions and 72 deletions
|
@ -6,6 +6,10 @@
|
||||||
|
|
||||||
namespace Sakura\Controllers;
|
namespace Sakura\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Sakura\DB;
|
||||||
|
use Sakura\Status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The status page and related stuff.
|
* The status page and related stuff.
|
||||||
* @package Sakura
|
* @package Sakura
|
||||||
|
@ -13,12 +17,56 @@ namespace Sakura\Controllers;
|
||||||
*/
|
*/
|
||||||
class StatusController extends Controller
|
class StatusController extends Controller
|
||||||
{
|
{
|
||||||
|
protected const EVENT_STATE = [
|
||||||
|
0 => 'Monitoring',
|
||||||
|
1 => 'Investigating',
|
||||||
|
2 => 'Resolved',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the base status page.
|
* Renders the base status page.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function index(): string
|
public function index()
|
||||||
{
|
{
|
||||||
return view('status/index');
|
$endpoints = array_keys(config('status.check'));
|
||||||
|
$events = DB::table('status_events')
|
||||||
|
->get();
|
||||||
|
$incidents = [];
|
||||||
|
|
||||||
|
foreach ($events as $row) {
|
||||||
|
$date = Carbon::createFromFormat('Y-m-d H:i:s', $row->event_date);
|
||||||
|
$incidents[$date->format('M j, Y')][] = [
|
||||||
|
'time' => $date->format('G:i'),
|
||||||
|
'state' => static::EVENT_STATE[$row->event_state],
|
||||||
|
'comment' => $row->event_text,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('status/index', compact('endpoints', 'incidents'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets status information.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function data()
|
||||||
|
{
|
||||||
|
$endpoints = config('status.check');
|
||||||
|
$name = $_GET['name'] ?? null;
|
||||||
|
|
||||||
|
if (!array_key_exists($name, $endpoints)) {
|
||||||
|
return $this->json(['error' => 'Unknown host.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
[$address, $port, $protocol] = explode('/', $endpoints[$name]);
|
||||||
|
$status = new Status($address, $port, $protocol);
|
||||||
|
|
||||||
|
if ($status->state === null) {
|
||||||
|
$status->check();
|
||||||
|
$status->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json($status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
102
app/Status.php
Normal file
102
app/Status.php
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Status checking.
|
||||||
|
* @package Sakura
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Sakura;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status checking.
|
||||||
|
* @package Sakura
|
||||||
|
* @author Julian van de Groep <me@flash.moe>
|
||||||
|
*/
|
||||||
|
class Status
|
||||||
|
{
|
||||||
|
private $address = '';
|
||||||
|
private $port = 0;
|
||||||
|
private $protocol = '';
|
||||||
|
|
||||||
|
public $state = null;
|
||||||
|
public $history = [];
|
||||||
|
|
||||||
|
public const OK = 2;
|
||||||
|
public const ERROR = 1;
|
||||||
|
public const FAIL = 0;
|
||||||
|
|
||||||
|
public function __construct(string $address, int $port, string $protocol = '', bool $populate = true)
|
||||||
|
{
|
||||||
|
$this->address = $address;
|
||||||
|
$this->port = $port;
|
||||||
|
$this->protocol = $protocol;
|
||||||
|
|
||||||
|
if ($populate) {
|
||||||
|
$this->populate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function populate(): 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')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$this->state = isset($this->history[0]) ? intval($this->history[0]->history_state) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check(): int
|
||||||
|
{
|
||||||
|
$this->state = static::FAIL;
|
||||||
|
$sock = checkdnsrr($this->address) ? fsockopen($this->address, $this->port, $errno, $errstr, 1) : false;
|
||||||
|
|
||||||
|
if ($sock !== false) {
|
||||||
|
fclose($sock);
|
||||||
|
$this->state = static::OK;
|
||||||
|
|
||||||
|
if ($this->protocol === 'http' || $this->protocol === 'https') {
|
||||||
|
$header = strstr(
|
||||||
|
Net::request("{$this->protocol}://{$this->address}:{$this->port}/", 'head', null, 1),
|
||||||
|
"\r\n",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($header !== false && strtolower(substr($header, 0, 4)) == 'http') {
|
||||||
|
list($protocol, $response, $text) = explode(' ', $header, 3);
|
||||||
|
|
||||||
|
if ($response >= 400 && $response < 500) {
|
||||||
|
$this->state = static::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response >= 500 && $response < 600) {
|
||||||
|
$this->state = static::FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save()
|
||||||
|
{
|
||||||
|
DB::table('status_history')
|
||||||
|
->insert([
|
||||||
|
'history_name' => $this->address,
|
||||||
|
'history_port' => $this->port,
|
||||||
|
'history_protocol' => $this->protocol,
|
||||||
|
'history_state' => $this->state
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$this->history = array_merge(DB::table('status_history')
|
||||||
|
->where('history_name', $this->address)
|
||||||
|
->where('history_port', $this->port)
|
||||||
|
->where('history_protocol', $this->protocol)
|
||||||
|
->orderBy('history_date', 'desc')
|
||||||
|
->limit(1)
|
||||||
|
->get(), $this->history);
|
||||||
|
}
|
||||||
|
}
|
|
@ -203,12 +203,17 @@ min_length = 1
|
||||||
|
|
||||||
; Content for the contact page, the variables function like a normal associative array
|
; Content for the contact page, the variables function like a normal associative array
|
||||||
[contact]
|
[contact]
|
||||||
mail['Administrator'] = sakura@localghost
|
mail['Administrator'] = me@flash.moe
|
||||||
|
|
||||||
twit['smugwave'] = "Sakura's main developer"
|
twit['smugwave'] = "Sakura's main developer"
|
||||||
|
|
||||||
repo['Sakura'] = https://github.com/flashwave/sakura
|
repo['Sakura'] = https://github.com/flashwave/sakura
|
||||||
|
|
||||||
|
; Status settings
|
||||||
|
[status]
|
||||||
|
check['flash.moe http'] = "flash.moe/80/http"
|
||||||
|
check['flash.moe https'] = "flash.moe/443/https"
|
||||||
|
|
||||||
; LastFM settings
|
; LastFM settings
|
||||||
[lastfm]
|
[lastfm]
|
||||||
api_key =
|
api_key =
|
||||||
|
|
43
database/2016_12_09_225434_status_tables.php
Normal file
43
database/2016_12_09_225434_status_tables.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Sakura\DB;
|
||||||
|
|
||||||
|
class StatusTables extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$schema = DB::getSchemaBuilder();
|
||||||
|
|
||||||
|
$schema->create('status_history', function (Blueprint $table) {
|
||||||
|
$table->string('history_name');
|
||||||
|
$table->smallInteger('history_port')->unsigned();
|
||||||
|
$table->string('history_protocol');
|
||||||
|
$table->tinyInteger('history_state')->unsigned();
|
||||||
|
$table->timestamp('history_date')->useCurrent = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
$schema->create('status_events', function (Blueprint $table) {
|
||||||
|
$table->increments('event_id');
|
||||||
|
$table->timestamp('event_date')->useCurrent = true;
|
||||||
|
$table->tinyInteger('event_state')->unsigned();
|
||||||
|
$table->string('event_text');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$schema = DB::getSchemaBuilder();
|
||||||
|
|
||||||
|
$schema->drop('status_history');
|
||||||
|
$schema->drop('status_events');
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
<span class="status__overall-icon"></span> <span class="status__overall-text">all services are go!</span>
|
<span class="status__overall-icon"></span> <span class="status__overall-text">all services are go!</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status__last-update">
|
<div class="status__last-update">
|
||||||
this is a static design preview
|
this is an not as static as it was before design preview
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -18,79 +18,42 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="platform status__response">
|
<div class="platform status__response">
|
||||||
<div class="status__services">
|
<div class="status__services">
|
||||||
<div class="status__service status__service--good status__service--selected">
|
{% for name in endpoints %}
|
||||||
<div class="status__service-icon"></div>
|
|
||||||
<div class="status__service-name">main site</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__service status__service--good">
|
<div class="status__service status__service--good">
|
||||||
<div class="status__service-icon"></div>
|
<div class="status__service-icon"></div>
|
||||||
<div class="status__service-name">test site</div>
|
<div class="status__service-name">{{ name }}</div>
|
||||||
</div>
|
|
||||||
<div class="status__service status__service--bad">
|
|
||||||
<div class="status__service-icon"></div>
|
|
||||||
<div class="status__service-name">api</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__service status__service--good">
|
|
||||||
<div class="status__service-icon"></div>
|
|
||||||
<div class="status__service-name">chat server</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__service status__service--good">
|
|
||||||
<div class="status__service-icon"></div>
|
|
||||||
<div class="status__service-name">chat client</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__service status__service--bad">
|
|
||||||
<div class="status__service-icon"></div>
|
|
||||||
<div class="status__service-name">irc gateway</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__service status__service--bad">
|
|
||||||
<div class="status__service-icon"></div>
|
|
||||||
<div class="status__service-name">tekkit</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="status__graph">graph goes here</div>
|
<div class="status__graph">graph goes here</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="platform status__incidents">
|
<div class="platform status__incidents">
|
||||||
|
{% for date, events in incidents %}
|
||||||
<div class="status__incident">
|
<div class="status__incident">
|
||||||
<div class="status__incident-date">Aug 4, 2016</div>
|
<div class="status__incident-date">{{ date }}</div>
|
||||||
<div class="status__updates">
|
<div class="status__updates">
|
||||||
<div class="status__update status__update--good">
|
{% 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-meta">
|
||||||
<div class="status__update-time">9:01 UTC</div>
|
<div class="status__update-time">{{ event.time }} UTC</div>
|
||||||
<div class="status__update-state">Resolved</div>
|
<div class="status__update-state">{{ event.state }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status__update-text">
|
<div class="status__update-text">
|
||||||
That fixed it for some reason.
|
{{ event.comment }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status__update status__update--busy">
|
{% endfor %}
|
||||||
<div class="status__update-meta">
|
{% else %}
|
||||||
<div class="status__update-time">8:45 UTC</div>
|
|
||||||
<div class="status__update-state">Monitoring</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__update-text">
|
|
||||||
Dropkicking the server out of the window.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__update status__update--bad">
|
|
||||||
<div class="status__update-meta">
|
|
||||||
<div class="status__update-time">8:30 UTC</div>
|
|
||||||
<div class="status__update-state">Investigating</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__update-text">
|
|
||||||
Having connectivity issues with the server.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status__incident">
|
|
||||||
<div class="status__incident-date">Aug 3, 2016</div>
|
|
||||||
<div class="status__updates">
|
|
||||||
<div class="status__update status__update">
|
<div class="status__update status__update">
|
||||||
<div class="status__update-text">
|
<div class="status__update-text">
|
||||||
No incidents occurred.
|
No incidents occurred.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -77,6 +77,7 @@ Router::group(['before' => 'maintenance'], function () {
|
||||||
// Status
|
// Status
|
||||||
Router::group(['prefix' => 'status'], function () {
|
Router::group(['prefix' => 'status'], function () {
|
||||||
Router::get('/', 'StatusController@index', 'status.index');
|
Router::get('/', 'StatusController@index', 'status.index');
|
||||||
|
Router::get('/data', 'StatusController@data', 'status.data');
|
||||||
});
|
});
|
||||||
|
|
||||||
// News
|
// News
|
||||||
|
|
Reference in a new issue