Compare commits

...

18 commits

Author SHA1 Message Date
28f1bcfce9 Changing frequency 2022-01-10 20:34:23 +01:00
9fe028e1cc Removing old version of Monitolite 2022-01-06 16:14:19 +01:00
ec4fb68630 Revert "Removing old version of Monitolite"
This reverts commit 42fe9281f7.
2022-01-06 16:12:51 +01:00
42fe9281f7 Removing old version of Monitolite 2022-01-06 16:10:22 +01:00
3daa19f2de Building as production 2021-12-29 11:32:44 +01:00
25f5a8800d Fixing API request 2021-12-29 11:32:01 +01:00
1414c83cc9 Improving query 2021-12-28 20:22:22 +01:00
78dc7baa73 Adding task type 2021-12-28 18:37:37 +01:00
83d401daa2 Adding missing icon 2021-12-28 18:31:56 +01:00
1996b4fb32 Updating README for DNS 2021-12-28 18:25:57 +01:00
5986875818 Adding DNS check 2021-12-28 18:24:40 +01:00
80ba45b83b README 2021-12-28 17:44:16 +01:00
f2139a7b01 Modifying the README 2021-12-28 17:43:32 +01:00
42f1187776 Adding FTP (anonymous) check 2021-12-28 17:41:52 +01:00
dde679411a Fixing relative URL 2021-12-28 16:37:48 +01:00
f1e1cbfdd9 Adding message when no data 2021-12-28 15:19:47 +01:00
d9c2c6f5f6 Fixing case when task not found 2021-12-28 14:04:44 +01:00
24ed50d9d2 Moving to migrations 2021-12-28 13:34:50 +01:00
31 changed files with 536 additions and 3353 deletions

View file

@ -7,8 +7,10 @@ I figured it could be useful for others so I **rewrote** and **updated** it from
## What it does
**MonitoLite** is a very simple monitoring tool developed in PHP powered by Lumen (by Laravel). It supports :
* **ping monitoring**: sends a `ping` command to the specified host. Raises an alert if the host is down
* **http monitoring**: requests the provided URL and raises an alert if the URL returns an error. Optionally you may specify a string to search on the page using the `param` database field. It raises an alert if the specified text could not be found on the page.
* **PING monitoring**: sends a `ping` command to the specified host. Raises an alert if the host is down
* **HTTP monitoring**: requests the provided URL and raises an alert if the URL returns an error. Optionally you may specify a string to search on the page using the `param` database field. It raises an alert if the specified text could not be found on the page.
* **FTP monitoring**: connects to the provided FTP server as anonymous (authentication not supported yet).
* **DNS monitoring**: runs a DNS lookup on a given DNS server for the hostname specified in the params
In case of an alert, the script sends an email notifications to the specified contacts (one or many).
The script also sends a recovery email notification when the alert is over.

View file

@ -119,12 +119,20 @@ class RunMonitoring extends Command
break;
case 'http':
$result = $this->checkHttp($task);
$result = $this->checkRequest($task, CURLPROTO_HTTP | CURLPROTO_HTTPS);
break;
case 'ftp':
$result = $this->checkRequest($task, CURLPROTO_FTP | CURLPROTO_FTPS);
break;
case 'dns':
$result = $this->checkDns($task);
break;
default:
// Nothing to do here
continue 2;
throw new Exception('Unknown type "'.$task->type.'"');
}
$new_status = 1;
@ -135,7 +143,8 @@ class RunMonitoring extends Command
}
catch(Exception $e) {
//TODO: handle system exception differently
$history = $this->saveHistory($task, false, $e->getMessage());
//$history = $this->saveHistory($task, false, $e->getMessage());
$this->error($e->getMessage());
}
finally {
// Changing task timestamps and status
@ -250,17 +259,43 @@ class RunMonitoring extends Command
return true;
}
final private function checkHttp(Task $task) {
final private function checkDns(Task $task) {
if (! function_exists('exec') || ! is_callable('exec')) {
throw new MonitoringException('The "exec" command is required');
}
if (is_null($task->params) || empty($task->params)) {
throw new Exception('Params are required');
}
$cmd = 'nslookup '.trim($task->params).' '.$task->host;
// If command failed
if (false === $exec = exec($cmd.' '.$task->host, $output, $code)) {
throw new MonitoringException('Unable to execute DNS lookup');
}
// If command returned a non-zero code
if ($code > 0) {
throw new MonitoringException('DNS lookup task failed ('.$exec.')');
}
return true;
}
final private function checkRequest(Task $task, $protocol = CURLPROTO_HTTP | CURLPROTO_HTTPS) {
if (app()->environment() == 'local') {
//throw new MonitoringException('Forcing error for testing');
}
// Preparing cURL
$opts = [
CURLOPT_HEADER => true,
CURLOPT_HTTPGET => true,
CURLOPT_FRESH_CONNECT => true,
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
CURLOPT_PROTOCOLS => $protocol,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,

View file

@ -114,7 +114,7 @@ class SyncCustomers extends Command
'type' => 'http',
'params' => 'restovisio.com',
'creation_date' => date('Y-m-d H:i:s'),
'frequency' => 600,
'frequency' => 3600,
'active' => 1,
'group_id' => $c->id
]);

View file

@ -26,14 +26,14 @@ class ApiController extends Controller
$query = Task
::leftJoin('groups', 'groups.id', 'tasks.group_id')
->leftJoin('task_history', 'task_id', 'task_history.id')
->select(
'tasks.id', 'tasks.host', 'tasks.status', 'tasks.type', 'tasks.params', 'tasks.frequency', 'tasks.created_at', 'tasks.executed_at', 'tasks.active', 'tasks.group_id',
'task_history.output',
'groups.name as group_name')
->get()
;
//dd($query->toSql());
foreach ($query as $t) {
if (is_null($t->group_id)) {
$group_id = $t->id;
@ -61,7 +61,7 @@ class ApiController extends Controller
$days = ($request->input('days', 15) - 1);
$task = Task::with(['group'])
->find($id)
->findOrFail($id)
;
if (! is_null($task)) {
@ -133,7 +133,7 @@ class ApiController extends Controller
// Getting the notifications sent
$notifications = $task
->notifications()
->with('contact')
->with(['contact', 'task_history'])
->where('notifications.created_at', '>', $first_day->toDateString())
->orderBy('notifications.created_at', 'desc')
->get()

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateContactTaskTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('contact_task', function (Blueprint $table) {
$table->bigInteger('task_id')->unsigned();
$table->bigInteger('contact_id')->unsigned();
$table->primary(['task_id', 'contact_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('contact_task');
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateContactsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('contacts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('surname', 200);
$table->string('firstname', 200);
$table->string('email', 250);
$table->string('phone', 20);
$table->timestamps();
$table->tinyInteger('active')->default(1);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('contacts');
}
}

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateGroupsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('groups', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 128)->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('groups');
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateNotificationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('notifications', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('contact_id');
$table->unsignedBigInteger('task_history_id');
$table->enum('status', ['pending', 'sent', 'error'])->default('pending');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('notifications');
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTaskHistoryTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('task_history', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedTinyInteger('status');
$table->text('output')->nullable();
$table->float('duration', 5, 3)->unsigned()->nullable();
$table->unsignedBigInteger('task_id');
$table->timestamps();
$table->index(['status', 'created_at']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('task_history');
}
}

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTasksArchivesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks_archives', function (Blueprint $table) {
$table->bigIncrements('id');
$table->date('day');
$table->unsignedInteger('uptime')->default('0');
$table->unsignedBigInteger('task_id')->default('0');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tasks_archives');
}
}

View file

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTasksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('host');
$table->enum('type', ['ping', 'http', 'dns', 'ftp']);
$table->string('params')->nullable();
$table->unsignedInteger('frequency');
$table->unsignedTinyInteger('attempts')->default('0');
$table->unsignedTinyInteger('active')->default(1);
$table->unsignedTinyInteger('status')->nullable();
$table->unsignedBigInteger('group_id')->nullable();
$table->timestamps();
$table->timestamp('executed_at')->nullable();
$table->unique(['host', 'type']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tasks');
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddForeignKeysToContactTaskTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('contact_task', function (Blueprint $table) {
$table->foreign(['task_id'], 'contact_task_ibfk_1')->references(['id'])->on('tasks')->onUpdate('NO ACTION')->onDelete('CASCADE');
$table->foreign(['contact_id'], 'contact_task_ibfk_2')->references(['id'])->on('contacts')->onUpdate('NO ACTION')->onDelete('CASCADE');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('contact_task', function (Blueprint $table) {
$table->dropForeign('contact_task_ibfk_1');
$table->dropForeign('contact_task_ibfk_2');
});
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddForeignKeysToNotificationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('notifications', function (Blueprint $table) {
$table->foreign(['contact_id'], 'contact_id_frgn')->references(['id'])->on('contacts')->onUpdate('NO ACTION')->onDelete('CASCADE');
$table->foreign(['task_history_id'], 'task_history_id_frgn')->references(['id'])->on('task_history')->onUpdate('NO ACTION')->onDelete('CASCADE');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('notifications', function (Blueprint $table) {
$table->dropForeign('contact_id_frgn');
$table->dropForeign('task_history_id_frgn');
});
}
}

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddForeignKeysToTaskHistoryTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('task_history', function (Blueprint $table) {
$table->foreign(['task_id'], 'task_history_ibfk_1')->references(['id'])->on('tasks')->onUpdate('NO ACTION')->onDelete('CASCADE');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('task_history', function (Blueprint $table) {
$table->dropForeign('task_history_ibfk_1');
});
}
}

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddForeignKeysToTasksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tasks', function (Blueprint $table) {
$table->foreign(['group_id'], 'group_id_frgn')->references(['id'])->on('groups')->onUpdate('NO ACTION')->onDelete('CASCADE');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tasks', function (Blueprint $table) {
$table->dropForeign('group_id_frgn');
});
}
}

View file

@ -1,128 +0,0 @@
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `contact_task`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `contact_task` (
`task_id` int unsigned NOT NULL,
`contact_id` int unsigned NOT NULL,
PRIMARY KEY (`task_id`,`contact_id`),
KEY `contact_id` (`contact_id`),
CONSTRAINT `contact_task_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE,
CONSTRAINT `contact_task_ibfk_2` FOREIGN KEY (`contact_id`) REFERENCES `contacts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `contacts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `contacts` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`surname` varchar(200) NOT NULL,
`firstname` varchar(200) NOT NULL,
`email` varchar(250) NOT NULL,
`phone` varchar(20) NOT NULL,
`creation_date` datetime NOT NULL,
`active` int NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `groups`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `groups` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `notifications`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `notifications` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`contact_id` int unsigned NOT NULL DEFAULT '0',
`task_history_id` int unsigned NOT NULL DEFAULT '0',
`status` enum('pending','sent','error') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'pending',
`created_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:01',
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `contact_id_frgn` (`contact_id`),
KEY `task_history_id_frgn` (`task_history_id`),
CONSTRAINT `contact_id_frgn` FOREIGN KEY (`contact_id`) REFERENCES `contacts` (`id`) ON DELETE CASCADE,
CONSTRAINT `task_history_id_frgn` FOREIGN KEY (`task_history_id`) REFERENCES `task_history` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `task_history`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `task_history` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`status` int unsigned NOT NULL,
`output` text CHARACTER SET utf8 COLLATE utf8_general_ci,
`duration` float(5,3) unsigned DEFAULT NULL,
`task_id` int unsigned NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:01',
PRIMARY KEY (`id`),
KEY `task_id` (`task_id`),
KEY `status` (`status`,`created_at`) USING BTREE,
CONSTRAINT `task_history_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tasks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tasks` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`host` varchar(255) NOT NULL,
`type` enum('ping','http') NOT NULL,
`params` varchar(255) NOT NULL,
`frequency` int unsigned NOT NULL,
`attempts` int unsigned NOT NULL DEFAULT '0',
`active` int NOT NULL DEFAULT '0',
`status` int unsigned DEFAULT NULL,
`group_id` int unsigned DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime DEFAULT NULL,
`executed_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `host` (`host`,`type`),
KEY `group_id_frgn` (`group_id`),
KEY `attempts` (`attempts`) USING BTREE,
CONSTRAINT `group_id_frgn` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tasks_archives`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tasks_archives` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`day` date NOT NULL,
`uptime` int unsigned NOT NULL DEFAULT '0',
`task_id` int unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
INSERT INTO `migrations` VALUES (1,'2021_12_21_101954_create_jobs_table',1);
INSERT INTO `migrations` VALUES (2,'2021_12_21_102355_create_failed_jobs_table',2);

View file

@ -1,350 +0,0 @@
#!/usr/bin/perl
################################
# #
# M O N I T O L I T E #
# #
# Lightweight Monitoring Tool #
# #
# @author: Axel de Vignon #
# @copyright: www.vidax.net #
# @license: Mozilla Public 1.1 #
# #
################################
use warnings;
use strict;
use DBI;
use Dotenv;
use Net::Ping;
use Email::MIME;
use Email::Sender::Simple qw(sendmail);
use Email::Sender::Transport::SMTP qw();
use LWP::Simple;
use LWP::UserAgent;
use LWP::Protocol::https;
my $query;
my $result;
my $tasks;
my $update_query;
my $emails;
my $email;
my $message;
my $response;
my $html;
my $numtasks;
my $previous_status;
my $subject;
my $datas;
############################
# #
# S E T T I N G S #
# #
############################
Dotenv->load;
my $dbtype = $ENV{'DB_CONNECTION'};
my $hostname = $ENV{'DB_HOST'};
my $database = $ENV{'DB_DATABASE'};
my $login = $ENV{'DB_USERNAME'};
my $port = $ENV{'DB_PORT'};
my $password = $ENV{'DB_PASSWORD'};
my $email_from = $ENV{'MAIL_FROM'};
my $number_tries = $ENV{'NB_TRIES'};
my $days_history_archive = $ENV{'ARCHIVE_DAYS'};
my $smtp_host = $ENV{'SMTP_HOST'};
my $smtp_user = $ENV{'SMTP_USER'};
my $smtp_password = $ENV{'SMTP_PASSWORD'};
my $smtp_port = $ENV{'SMTP_PORT'};
my $smtp_ssl = $ENV{'SMTP_SSL'};
############################
######
# Testing database connection
######
my $dsn = "DBI:$dbtype:database=$database;host=$hostname;port=$port";
my $dbh = DBI->connect($dsn, $login, $password) or output('cannot connect to database', 'ERROR', 1);
######
# Getting tasks
######
my $execution_time = server_time();
my $query1 = $dbh->prepare('SELECT id, host, type, params FROM tasks WHERE ( DATE_SUB(now(), INTERVAL frequency SECOND) > last_execution OR last_execution IS NULL ) AND active = 1');
$query1->execute() or output('Cannot execute query fetching all pending tasks', 'ERROR', 1);
$numtasks = $query1->rows;
#####
# Processing all tasks
#####
if ($numtasks > 0) {
while ($tasks = $query1->fetchrow_hashref()) {
print "\n";
my $status = -1;
$previous_status = -1;
$message = 'Host is back up';
####
# Getting last history for this host
####
my $query2 = $dbh->prepare('SELECT status FROM tasks_history WHERE task_id = ' . $tasks->{'id'} . ' ORDER BY datetime DESC LIMIT 1');
$query2->execute() or output('Cannot get history for this task', 'ERROR', 0);
if ($query2->rows > 0) {
my $history = $query2->fetchrow_hashref();
$previous_status = $history->{'status'};
}
if ($tasks->{'type'} =~ 'ping') {
# Ping check returned an error
if (! check_ping($tasks->{'host'})) {
$status = 0;
output('Host "'. $tasks->{'host'} .'" [' . $tasks->{'type'} . '] is down', 'ALERT');
$message = 'Host does not reply to ping. Timed out after 5s. Giving up...';
}
# Ping check went fine
else {
$status = 1;
output('Host "'. $tasks->{'host'} .'" [' . $tasks->{'type'} . '] is up', 'SUCCESS');
}
}
elsif ($tasks->{'type'} =~ 'http') {
$response = check_http($tasks->{'host'}, $tasks->{'params'});
# HTTP check went fine
if ($response =~ 'OK') {
$status = 1;
output('Host "'. $tasks->{'host'} .'" [' . $tasks->{'type'} . '] is up', 'SUCCESS');
}
# HTTP check returned an error
else {
$status = 0;
output('Host "'. $tasks->{'host'} .'" [' . $tasks->{'type'} . '] is down', 'ALERT');
$message = 'HTTP response was: ' . $response;
}
}
else {
output('dunno how to process this task', 'DEBUG');
next;
}
# Notify on status changes only
if ($previous_status != -1 && $status != $previous_status) {
output('Should send notification', 'DEBUG');
&send_notifications($tasks->{'id'}, $tasks->{'host'}, $tasks->{'type'}, $message, $status);
}
# Saving Status into DB
if ($status >= 0) {
save_history($tasks->{'id'}, $status, $execution_time, $response);
}
}
}
else {
output('nothing to monitor, sleeping back', 'DEBUG');
}
#####
# Function used for the PING test
#####
sub check_ping {
my ($host, $round) = @_;
$round = 1 if (! $round);
my $ping = Net::Ping->new('icmp');
output('ping check n°' . $round . ' on ' . $host, 'DEBUG');
if (! $ping->ping($host)) {
$ping->close();
if ($number_tries && $round <= $number_tries) {
sleep (2);
return check_ping($host, $round + 1)
}
else {
return undef;
}
} else {
$ping->close();
return 'OK';
}
}
#####
# Function used to check HTTP service
#####
sub check_http {
my ($host, $find, $round) = @_;
$round = 1 if (! $round);
$host = 'http://'.$host if ($host !~ m/^http/i);
my $check = LWP::UserAgent->new(
ssl_opts => { verify_hostname => 1 },
protocols_allowed => ['http', 'https']
);
$check->timeout(20);
$check->env_proxy;
my $response = $check->get($host, ':content_cb' => \&process_data);
output('http check n°' . $round . ' on ' . $host, 'DEBUG');
if ($response->is_success) {
if ($find && length($find) > 0) {
output('searching "' . $find . '" into html content on ' . $host, 'DEBUG');
if ($html =~ m/$find/i) {
output('html content found, looks fine', 'SUCCESS');
return 'OK';
}
else {
output('html content not found', 'ERROR');
return 'Could not find "' . $find . '" into the page';
}
}
else {
return 'OK';
}
}
else {
output('HTTP response error was: '.$response->status_line, 'DEBUG');
if ($number_tries && $round < $number_tries) {
sleep (2);
return check_http($host, $find, $round + 1);
}
else {
return $response->status_line;
}
}
}
#####
# Save the page HTML content
#####
sub process_data {
my ($content, $handler1, $handler2) = @_;
$html .= $content;
}
#####
# Function managing DEBUG and OUTPUT
#####
sub output {
my ($output, $level, $fatal) = @_;
$output = server_time().' - '.$level.' - '.$output."\n";
if ($fatal && $fatal == 1) {
die ('FATAL '.$output);
}
else {
print ($output);
}
return 1;
}
#####
# Function that keeps an history
#####
sub save_history {
my ($task_id, $status, $datetime, $response) = @_;
my $query = $dbh->prepare('INSERT INTO `tasks_history` (`status`, `datetime`, `task_id`, `output`) VALUES(' . $status . ', "'.$datetime.'", ' . $task_id . ', "' . $response . '")');
if ($query->execute()) {
output('saving status to history', 'DEBUG');
}
else {
output('cannot save status to history', 'ERROR');
}
$update_query = $dbh->prepare('UPDATE `tasks` SET `last_execution` = "'.$datetime.'" WHERE id = ' . $task_id);
if ($update_query->execute()) {
output('saving last execution time for this task', 'DEBUG');
}
else {
output('cannot save last execution time for this task', 'ERROR');
}
return 1;
}
#####
# Function sending notifications
#####
sub send_notifications {
my ($task_id, $host, $type, $message, $status) = @_;
if ($status == 0) {
$subject = 'DOWN: host "' . $host . '" [' . $type . '] is down';
$datas = "------ ALERT DETECTED BY MONITORING SERVICE ------ \n\n\nDATETIME: " . server_time() . " (server time)\nHOST: " . $host . "\nSERVICE: " . $type . "\nMESSAGE: " . $message;
}
else {
$subject = 'UP: host "' . $host . '" [' . $type . '] is up';
$datas = "------ RECOVERY DETECTED BY MONITORING SERVICE ------ \n\n\nDATETIME: " . server_time() . " (server time)\nHOST: " . $host . "\nSERVICE: " . $type . "\nMESSAGE: " . $message;
}
my $query = $dbh->prepare('SELECT c.email FROM contacts as c JOIN notifications as n ON (n.contact_id = c.id) WHERE c.active = 1 AND n.task_id = '.$task_id);
if ($query->execute()) {
while ($emails = $query->fetchrow_hashref()) {
my $email = Email::MIME->create(
header_str => [
From => $email_from,
To => $emails->{'email'},
Subject => $subject
],
parts => [
$datas
],
);
eval {
sendmail(
$email,
{
from => $email_from,
transport => Email::Sender::Transport::SMTP->new({
host => $smtp_host,
port => $smtp_port,
sasl_username => $smtp_user,
sasl_password => $smtp_password,
ssl => $smtp_ssl,
timeout => 10
})
}
);
output('Notification email was sent to '.$emails->{'email'}, 'DEBUG');
};
warn $@ if $@;
}
return 1
}
output('failed to send notifications', 'ERROR');
return undef;
}
#####
# Function getting datetime
#####
sub server_time {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my $now = (1900 + $year).'-'.($mon + 1).'-'.$mday.' '.$hour.':'.$min.':00';
return $now;
}

View file

@ -1,222 +1,4 @@
@import url(https://fonts.googleapis.com/css2?family=Hind:wght@300;400;500;600;700&display=swap);
@font-face {
font-family: "Digital7";
src: url("/fonts/digital.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
/**
* SETTINGS
**/
* {
padding: 0;
margin: 0;
}
@font-face{font-family:Digital7;font-style:normal;font-weight:400;src:url(/fonts/digital.ttf) format("truetype")}*{margin:0;padding:0}html{font-size:100%;scroll-behavior:smooth}html body{background-attachment:fixed;background-image:url(../img/bush.png);color:#3d3d3d;font-family:Hind,sans-serif;font-size:1rem;padding:10px}html body a,html body a:visited{color:inherit;text-decoration:inherit}html body a:hover,html body a:visited:hover{text-decoration:underline}html body .container{margin:0 auto;max-width:1000px;padding:0}html body h1,html body h2,html body h3,html body h4{margin-bottom:.8rem;margin-top:.8rem}html body h1{font-size:2.4rem;margin:3rem 0;text-align:center}html body h2{font-size:1.4rem;margin-top:0;padding-top:0}html body h3{background-color:#0a9f9a;color:#f0f0f0;font-size:1.4rem;margin:0;padding:.8rem;position:relative}html body h3 small{font-size:.9rem}html body h3 .context-menu{cursor:pointer;font-size:1rem;position:absolute;right:.7rem;top:.7rem}html body div.round{background-color:#fff;border-radius:5px;box-shadow:3px 3px 6px 0 rgba(0,0,0,.3);margin-bottom:3rem;overflow:hidden;position:relative}html body div.round h3{margin-bottom:1rem}html body img{vertical-align:sub}html body table{border:1px solid #abc;border-collapse:collapse;border-spacing:0;font-size:14px}html body table th{background-color:#e6eeee}html body table td,html body table th{border:1px solid #9ccece;padding:.3rem}html body table td{background-color:#fff;color:#3d3d3d;text-align:center}html body table td,html body table td img{vertical-align:middle}html body table td.right{text-align:right}html body table#contacts_tbl,html body table#tasks_tbl{width:100%}html body .no-data{color:#727272;font-size:.9rem;font-style:italic;margin-bottom:1.3rem;text-align:center}html body .quick-view .new-group{border-radius:.4rem;cursor:pointer;display:inline-block;margin:.2rem;overflow:hidden}html body .quick-view .new-group .square{float:left;height:100%;line-height:1.2rem;margin:0;min-width:1.4rem;padding:.2rem .6rem;text-align:center;vertical-align:middle}html body .quick-view .new-group .square:not(:first-of-type){border-left:1px solid #fff}html body .tasks .task{background-color:#fff;border-radius:5px;box-shadow:3px 3px 6px 0 rgba(0,0,0,.3);margin-top:2rem;overflow:hidden;padding:0;position:relative}html body .spacer{clear:both;line-height:0;margin:0;padding:0}html body .block-content{padding:.8rem}html body .highlight{background-color:#166260;border-radius:.5rem;color:#fff;display:inline-block;font-size:1rem;padding:0 1rem;vertical-align:middle}html body .small{font-size:.8rem}html body .hidden{display:none}html body .up{background-color:#8adf8a}html body .down{background-color:#f79292}html body .unknown{background-color:#f5d69e}html body .inactive{background-color:#dfdfdf!important;opacity:.5}html body .refreshed-time{font-size:.8rem;margin-bottom:2rem;text-align:right}html body .refreshed-time .clock{background-color:#000;border-radius:4px;color:#fff;font-family:Digital7;font-size:1.2rem;padding:.3rem .5rem}@-webkit-keyframes shake{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}@keyframes shake{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}
html {
font-size: 100%;
scroll-behavior: smooth;
}
html body {
padding: 10px;
font-family: "Hind", sans-serif;
font-size: 1rem;
color: #3D3D3D;
background-image: url(../img/bush.png);
background-attachment: fixed;
}
html body a, html body a:visited {
color: inherit;
text-decoration: inherit;
}
html body a:hover, html body a:visited:hover {
text-decoration: underline;
}
html body .container {
padding: 0;
margin: 0 auto;
max-width: 1000px;
}
html body h1, html body h2, html body h3, html body h4 {
margin-top: 0.8rem;
margin-bottom: 0.8rem;
}
html body h1 {
font-size: 2.4rem;
text-align: center;
margin: 3rem 0;
}
html body h2 {
font-size: 1.4rem;
margin-top: 0;
padding-top: 0;
}
html body h3 {
font-size: 1.4rem;
background-color: #0a9f9a;
margin: 0;
padding: 0.8rem;
color: #f0f0f0;
position: relative;
}
html body h3 small {
font-size: 0.9rem;
}
html body h3 .context-menu {
position: absolute;
right: 0.7rem;
top: 0.7rem;
cursor: pointer;
font-size: 1rem;
}
html body div.round {
box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.3);
border-radius: 5px;
position: relative;
background-color: white;
overflow: hidden;
margin-bottom: 3rem;
}
html body div.round h3 {
margin-bottom: 1rem;
}
html body img {
vertical-align: sub;
}
html body table {
border: 1px solid #ABC;
font-size: 14px;
border-spacing: 0;
border-collapse: collapse;
}
html body table th {
background-color: #e6EEEE;
border: 1px solid #9ccece;
padding: 0.3rem;
}
html body table td {
color: #3D3D3D;
padding: 0.3rem;
background-color: #FFF;
border: 1px solid #9ccece;
text-align: center;
vertical-align: middle;
}
html body table td img {
vertical-align: middle;
}
html body table td.right {
text-align: right;
}
html body table#tasks_tbl, html body table#contacts_tbl {
width: 100%;
}
html body .quick-view .new-group {
margin: 0.2rem;
display: inline-block;
border-radius: 0.4rem;
overflow: hidden;
cursor: pointer;
}
html body .quick-view .new-group .square {
height: 100%;
margin: 0;
text-align: center;
vertical-align: middle;
float: left;
line-height: 1.2rem;
min-width: 1.4rem;
padding: 0.2rem 0.6rem;
}
html body .quick-view .new-group .square:not(:first-of-type) {
border-left: 1px solid white;
}
html body .tasks .task {
margin-top: 2rem;
padding: 0;
box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.3);
border-radius: 5px;
position: relative;
background-color: white;
overflow: hidden;
}
html body .spacer {
clear: both;
line-height: 0;
padding: 0;
margin: 0;
}
html body .block-content {
padding: 0.8rem;
}
html body .highlight {
background-color: #166260;
padding: 0px 1rem;
display: inline-block;
color: #FFF;
vertical-align: middle;
border-radius: 0.5rem;
font-size: 1rem;
}
html body .small {
font-size: 0.8rem;
}
html body .hidden {
display: none;
}
html body .up {
background-color: #8adf8a;
}
html body .down {
background-color: #f79292;
}
html body .unknown {
background-color: #f5d69e;
}
html body .inactive {
background-color: #dfdfdf !important;
opacity: 0.5;
}
html body .refreshed-time {
text-align: right;
font-size: 0.8rem;
margin-bottom: 2rem;
}
html body .refreshed-time .clock {
font-family: Digital7;
font-size: 1.2rem;
background-color: #000;
border-radius: 4px;
color: #FFF;
padding: 0.3rem 0.5rem;
}
@-webkit-keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
/*# sourceMappingURL=app.css.map*/

1
public/css/app.css.map Normal file

File diff suppressed because one or more lines are too long

1
public/img/dns.svg Normal file
View file

@ -0,0 +1 @@
<?xml version="1.0" ?><svg data-name="Layer 1" id="Layer_1" viewBox="0 0 272 272" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#424242;}.cls-2{fill:#f3f4f2;}</style></defs><title/><rect class="cls-1" height="8" width="136" x="68" y="68"/><rect class="cls-1" height="8" width="136" x="68" y="92"/><rect class="cls-1" height="8" width="136" x="68" y="116"/><rect class="cls-1" height="8" width="136" x="68" y="140"/><rect class="cls-1" height="8" width="136" x="68" y="164"/><rect class="cls-1" height="8" width="136" x="68" y="188"/><rect class="cls-1" height="8" width="136" x="68" y="212"/><path class="cls-1" d="M216,40H192V16l-8-8H48V264H224V48Zm0,216H56V16H184V48h32Z"/><rect class="cls-2" height="8" width="136" x="68" y="68"/><rect class="cls-2" height="8" width="136" x="68" y="92"/><rect class="cls-2" height="8" width="136" x="68" y="116"/><rect class="cls-2" height="8" width="136" x="68" y="140"/><rect class="cls-2" height="8" width="136" x="68" y="164"/><rect class="cls-2" height="8" width="136" x="68" y="188"/><rect class="cls-2" height="8" width="136" x="68" y="212"/><path class="cls-1" d="M184,8V48h40Zm8,32V27.31L204.69,40Z"/><rect class="cls-1" height="8" width="136" x="68" y="68"/><rect class="cls-1" height="8" width="136" x="68" y="92"/><rect class="cls-1" height="8" width="136" x="68" y="116"/><rect class="cls-1" height="8" width="136" x="68" y="140"/><rect class="cls-1" height="8" width="136" x="68" y="164"/><rect class="cls-1" height="8" width="136" x="68" y="188"/><rect class="cls-1" height="8" width="136" x="68" y="212"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

1
public/img/ftp.svg Normal file
View file

@ -0,0 +1 @@
<?xml version="1.0" ?><svg data-name="Layer 1" id="Layer_1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M7,25A7,7,0,0,1,7,11a1,1,0,0,1,0,2A5,5,0,0,0,7,23a1,1,0,0,1,0,2Z"/><path d="M25,25a1,1,0,0,1,0-2,5,5,0,0,0,0-10,1,1,0,0,1,0-2,7,7,0,0,1,0,14Z"/><path d="M25,13a1,1,0,0,1-1-1A8,8,0,0,0,8,12a1,1,0,0,1-2,0,10,10,0,0,1,20,0A1,1,0,0,1,25,13Z"/><path d="M21,22H11a1,1,0,0,1,0-2H21a1,1,0,0,1,0,2Z"/><path d="M21,28H11a1,1,0,0,1,0-2H21a1,1,0,0,1,0,2Z"/><path d="M21,28a1,1,0,0,1-.83-.45l-2-3a1,1,0,1,1,1.66-1.1l2,3a1,1,0,0,1-.28,1.38A.94.94,0,0,1,21,28Z"/><path d="M19,31a.94.94,0,0,1-.55-.17,1,1,0,0,1-.28-1.38l2-3a1,1,0,0,1,1.66,1.1l-2,3A1,1,0,0,1,19,31Z"/><path d="M13,25a1,1,0,0,1-.83-.45l-2-3a1,1,0,0,1,1.66-1.1l2,3a1,1,0,0,1-.28,1.38A.94.94,0,0,1,13,25Z"/><path d="M11,22a.94.94,0,0,1-.55-.17,1,1,0,0,1-.28-1.38l2-3a1,1,0,0,1,1.66,1.1l-2,3A1,1,0,0,1,11,22Z"/><path d="M9,25H7a1,1,0,0,1,0-2H9a1,1,0,0,1,0,2Z"/><path d="M25,25H23a1,1,0,0,1,0-2h2a1,1,0,0,1,0,2Z"/></svg>

After

Width:  |  Height:  |  Size: 989 B

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,29 @@
/*!
* ApexCharts v3.32.0
* (c) 2018-2021 ApexCharts
* Released under the MIT License.
*/
/*!
* Vue.js v2.6.14
* (c) 2014-2021 Evan You
* Released under the MIT License.
*/
/*!
* vuex v3.6.2
* (c) 2021 Evan You
* @license MIT
*/
/*! svg.draggable.js - v2.2.2 - 2019-01-08
* https://github.com/svgdotjs/svg.draggable.js
* Copyright (c) 2019 Wout Fierens; Licensed MIT */
/*! svg.filter.js - v2.0.2 - 2016-02-24
* https://github.com/wout/svg.filter.js
* Copyright (c) 2016 Wout Fierens; Licensed MIT */
//! moment.js
//! moment.js locale configuration

1
public/js/app.js.map Normal file

File diff suppressed because one or more lines are too long

View file

@ -111,41 +111,48 @@ html {
}
table {
border: 1px solid #ABC;
font-size: 14px;
border-spacing : 0;
border-collapse : collapse;
border: 1px solid #ABC;
font-size: 14px;
border-spacing : 0;
border-collapse : collapse;
th {
background-color: #e6EEEE;
border: 1px solid #9ccece;
padding: 0.3rem;
th {
background-color: #e6EEEE;
border: 1px solid #9ccece;
padding: 0.3rem;
}
}
td {
color: #3D3D3D;
padding: 0.3rem;
background-color: #FFF;
border: 1px solid #9ccece;
text-align: center;
td {
color: #3D3D3D;
padding: 0.3rem;
background-color: #FFF;
border: 1px solid #9ccece;
text-align: center;
vertical-align: middle;
img {
vertical-align: middle;
img {
vertical-align: middle;
}
&.right {
text-align: right;
}
}
&#tasks_tbl, &#contacts_tbl {
width: 100%;
&.right {
text-align: right;
}
}
&#tasks_tbl, &#contacts_tbl {
width: 100%;
}
}
.no-data {
text-align: center;
font-style: italic;
margin-bottom: 1.3rem;
font-size: .9rem;
color: #727272;
}
.quick-view {

View file

@ -4,8 +4,8 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>MonitoLite - Network monitoring tool</title>
<script type="text/javascript" src="/js/app.js"></script>
<link type="text/css" rel="stylesheet" href="/css/app.css" />
<script type="text/javascript" src="{{ url('js/app.js') }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url('css/app.css') }}" />
</head>
<body>

View file

@ -34,16 +34,15 @@
getTasks: function() {
this.$http.get('/api/getTasks')
.then(response => this.$store.commit('setTasks', response.data))
.then(() => {
this.refreshed_time = this.moment();
this.loading.hide()
})
.catch(error => {
this.loading.hide()
clearTimeout(this.refresh)
window.alert('An error occurred when getting tasks. Automatic refresh has been disabled. You should fix and reload this page.')
})
this.refreshed_time = this.moment();
.then(() => {
this.refreshed_time = this.moment();
this.loading.hide()
})
}
},
beforeRouteLeave(to, from, next) {

View file

@ -5,7 +5,7 @@
</h3>
<div class="block-content">
<div
v-if="1 == 1"
v-if="tasks && Object.keys(tasks).length > 0"
>
<div
v-for="group in tasks"
@ -28,9 +28,10 @@
<p class="spacer">&nbsp;</p>
</div>
<div
class="no-data"
v-else
>
<center>Sorry, there is no task here.</center>
Sorry, there is no task here.
</div>
</div>

View file

@ -17,7 +17,7 @@
<tr>
<th width="5%">Up?</th>
<th width="*">Host</th>
<th width="5%">Type</th>
<th width="10%">Type</th>
<th width="20%">Last checked</th>
<th width="13%">Frequency (min)</th>
<th width="5%">Active</th>
@ -38,14 +38,14 @@
<a :href="task.host" target="_blank">{{ task.host }}</a>
</td>
<td>
<img :src="task.type == 'http' ? '/img/http.svg' : '/img/ping.svg'" width="16" alt="Type of check" :title="'Type: '+task.type" />
<img :src="'/img/'+task.type+'.svg'" width="16" alt="Type of check" :title="'Type: '+task.type" />
{{ task.type.toUpperCase() }}
</td>
<td>
<span
v-if="task.executed_at"
>
{{ moment(task.executed_at).fromNow() }}
<img src="/img/info.svg" alt="Infos" width="16" :title="'Result: '+task.output" />
</span>
<span
v-else
@ -83,7 +83,7 @@ export default {
computed: {
tasks: function() {
return this.$store.state.tasks
}
},
},
methods: {
statusText: function (status) {

View file

@ -13,6 +13,7 @@
v-model="days"
@change="refreshTask"
>
<option value="3">3 days</option>
<option value="7">7 days</option>
<option value="15">15 days</option>
<option value="30">30 days</option>
@ -23,6 +24,7 @@
<h3>Last {{ days }} days uptime</h3>
<div class="block-content">
<apexchart class="graph" v-if="charts.uptime.render" type="bar" height="350" :options="charts.uptime.options" :series="charts.uptime.series"></apexchart>
<p class="no-data" v-else>No chart to display here</p>
</div>
</div>
@ -31,6 +33,7 @@
<h3>Last {{ days }} days response time</h3>
<div class="block-content">
<apexchart class="graph" v-if="charts.response.render" type="line" height="350" :options="charts.response.options" :series="charts.response.series"></apexchart>
<p class="no-data" v-else>No chart to display here</p>
</div>
</div>
@ -39,16 +42,16 @@
<!-- History backlog -->
<div class="round">
<h3>Last {{ days }} days history log</h3>
<div class="block-content" v-if="history">
<div class="block-content" v-if="history && Object.keys(history).length > 0">
<p><i>Showing only records where status has changed</i></p>
<table id="tasks_tbl">
<thead>
<tr>
<th width="10%">Status</th>
<th width="10%">Date</th>
<th width="10%">Time</th>
<th width="*">Output</th>
<th width="10%">Duration</th>
<th width="10%">Status</th>
</tr>
</thead>
<tbody>
@ -56,6 +59,9 @@
v-for="h in history"
v-bind:key="h.id"
>
<td :class="statusText(h.status)">
<img :src="'/img/'+statusText(h.status)+'.svg'" width="16" alt="Status" />
</td>
<td>{{ moment(h.created_at).format('YYYY-MM-DD') }}</td>
<td>{{ moment(h.created_at).format('HH:mm:ss') }}</td>
<td>
@ -70,28 +76,26 @@
<span v-if="h.duration != null">{{ h.duration+'s' }}</span>
<span v-else><i>No duration</i></span>
</td>
<td :class="statusText(h.status)">
<img :src="'/img/'+statusText(h.status)+'.svg'" width="16" alt="Status" />
</td>
</tr>
</tbody>
</table>
</div>
<p v-else><center>No history to display here</center></p>
<p class="no-data" v-else>No history to display here</p>
</div>
<!-- Notifications block -->
<div class="round">
<h3>Last {{ days }} days notifications log</h3>
<div class="block-content" v-if="notifications">
<div class="block-content" v-if="notifications && Object.keys(notifications).length > 0">
<table id="tasks_tbl">
<thead>
<tr>
<th width="20%">Date</th>
<th width="20%">Time</th>
<th width="*">Firstname</th>
<th width="10%">Lastname</th>
<th width="10%">Email</th>
<th width="10%">Date</th>
<th width="10%">Time</th>
<th width="15">Firstname</th>
<th width="15%">Lastname</th>
<th width="30%">Email</th>
<th width="10%">Type</th>
<th width="10%">Status</th>
</tr>
</thead>
@ -105,12 +109,13 @@
<td>{{ n.contact.firstname }}</td>
<td>{{ n.contact.surname }}</td>
<td>{{ n.contact.email }}</td>
<td>{{ n.status }}</td>
<td>{{ n.task_history.status == 1 ? 'UP' : 'DOWN' }}</td>
<td>{{ n.status.toUpperCase() }}</td>
</tr>
</tbody>
</table>
</div>
<p v-else><center>No notification to display here</center></p>
<p class="no-data" v-else>No notification to display here</p>
</div>
</div>
</div>
@ -128,7 +133,7 @@
notifications: null,
refresh: null,
loader: null,
days: 7,
days: 3,
first_day: null,
charts: {
@ -179,6 +184,9 @@
}, 10000)
}
})
.catch(error => {
//TODO: do something
})
.then(() => {
this.loader.hide()
})

View file

@ -13,10 +13,10 @@
|
*/
$router->group(['prefix' => 'api/'], function () use ($router) {
$router->get('getTasks/', ['uses' => 'ApiController@getTasks']);
$router->post('getTask/{id}', ['uses' => 'ApiController@getTaskDetails']);
$router->patch('toggleTaskStatus/{id}', ['uses' => 'ApiController@toggleTaskStatus']);
$router->group(['prefix' => '/api'], function () use ($router) {
$router->get('/getTasks/', ['uses' => 'ApiController@getTasks']);
$router->post('/getTask/{id}', ['uses' => 'ApiController@getTaskDetails']);
$router->patch('/toggleTaskStatus/{id}', ['uses' => 'ApiController@toggleTaskStatus']);
});
$router->get('/{route:.*}/', function () {