New version with VueJS
This commit is contained in:
parent
a0fd4aea60
commit
187eae16e1
7 changed files with 289 additions and 188 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
web/vendor/**/*
|
||||
.env
|
||||
web/sync.php
|
||||
/**/node_modules
|
||||
|
|
80
web/DB.php
80
web/DB.php
|
@ -9,24 +9,28 @@ $dotenv->required(['DB_TYPE', 'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD']);
|
|||
|
||||
class DB {
|
||||
private $link;
|
||||
|
||||
|
||||
|
||||
|
||||
public function __construct() {
|
||||
if (! is_resource($this->link)) {
|
||||
$dsn = $_ENV['DB_TYPE'].':dbname='.$_ENV['DB_NAME'].';host='.$_ENV['DB_HOST'].';port='.$_ENV['DB_PORT'];
|
||||
$this->link = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASSWORD']);
|
||||
$this->link->query('SET NAMES "UTF8"');
|
||||
$driver_options = array(
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'",
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
);
|
||||
$this->link = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $driver_options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function get_all_contacts($task = null) {
|
||||
$query = '
|
||||
SELECT c.id, c.surname, c.firstname, c.email, c.phone, c.creation_date, c.active
|
||||
FROM contacts c
|
||||
SELECT c.id, c.surname, c.firstname, c.email, c.phone, c.creation_date, c.active
|
||||
FROM contacts c
|
||||
LEFT JOIN notifications as n ON (n.contact_id = c.id)
|
||||
LEFT JOIN tasks as t ON (n.task_id = t.id)
|
||||
';
|
||||
|
||||
|
||||
if (! is_null($task)) {
|
||||
$query .= ' WHERE t.id = '.$task;
|
||||
}
|
||||
|
@ -37,19 +41,24 @@ class DB {
|
|||
public function get_all_tasks($status = null) {
|
||||
if (is_null($status)) {
|
||||
$query = '
|
||||
SELECT id, host, type, params, creation_date, frequency, last_execution, active
|
||||
FROM tasks
|
||||
SELECT DISTINCT t.id, t.host, t.type, t.params, t.frequency, t.creation_date, t.last_execution, t.active, t.group_id, h.status, g.name as group_name
|
||||
FROM `tasks` as t
|
||||
LEFT JOIN `tasks_history` as h ON (h.task_id = t.id)
|
||||
LEFT JOIN `groups` as g ON (g.id = t.group_id)
|
||||
WHERE (t.last_execution IS NULL OR h.datetime = t.last_execution)
|
||||
ORDER BY group_id DESC
|
||||
';
|
||||
}
|
||||
else {
|
||||
$query = '
|
||||
SELECT DISTINCT t.id, t.host, t.type, t.params, t.creation_date, t.last_execution, t.active
|
||||
FROM tasks as t
|
||||
SELECT DISTINCT t.id, t.host, t.type, t.params, t.creation_date, t.last_execution, t.active, t.group_id
|
||||
FROM tasks as t
|
||||
JOIN tasks_history as h ON (h.task_id = t.id)
|
||||
WHERE h.status = '.intval($status).' AND h.datetime = t.last_execution
|
||||
ORDER BY group_id DESC
|
||||
';
|
||||
}
|
||||
|
||||
|
||||
return $this->query($query);
|
||||
}
|
||||
|
||||
|
@ -57,21 +66,21 @@ class DB {
|
|||
$args = [
|
||||
':limit' => $limit
|
||||
];
|
||||
|
||||
|
||||
$query = '
|
||||
SELECT id, status, datetime, task_id
|
||||
SELECT id, status, datetime, task_id
|
||||
FROM tasks_history
|
||||
';
|
||||
|
||||
|
||||
if (! is_null($task)) {
|
||||
$query .= ' WHERE task_id = :task_id';
|
||||
$args[':task_id'] = $task;
|
||||
}
|
||||
$query .= ' ORDER BY datetime DESC LIMIT :limit ';
|
||||
|
||||
|
||||
return $this->query($query, $args);
|
||||
}
|
||||
|
||||
|
||||
public function get_task_last_status($task) {
|
||||
$result = $this->query('SELECT t.id, th.status FROM tasks_history th JOIN tasks t ON (th.task_id = t.id) WHERE t.id = :task ORDER BY datetime DESC LIMIT 1', [':task' => $task]);
|
||||
foreach ($result as $r) {
|
||||
|
@ -81,45 +90,58 @@ class DB {
|
|||
|
||||
|
||||
public function query($query, $args = null) {
|
||||
$result = $this->prepare($query, $args);
|
||||
|
||||
if (! $result->execute()) {
|
||||
throw new DatabaseException($result->errorInfo()[2]. ' in ('.$query.')');
|
||||
}
|
||||
|
||||
return $result->fetchAll();
|
||||
}
|
||||
|
||||
private function prepare($query, $args = null) {
|
||||
if (! $result = $this->link->prepare($query)) {
|
||||
throw new DatabaseException('Cannot prepare query ('.$query.') for execution');
|
||||
}
|
||||
|
||||
|
||||
if (! is_null($args)) {
|
||||
foreach ($args as $n => $v) {
|
||||
$type = gettype($v);
|
||||
|
||||
|
||||
switch ($type) {
|
||||
|
||||
|
||||
case 'boolean':
|
||||
$cast = PDO::PARAM_BOOL;
|
||||
break;
|
||||
|
||||
|
||||
case 'integer':
|
||||
$cast = PDO::PARAM_INT;
|
||||
break;
|
||||
|
||||
|
||||
case 'null':
|
||||
$cast = PDO::PARAM_NULL;
|
||||
break;
|
||||
|
||||
|
||||
case 'double':
|
||||
case 'string':
|
||||
default:
|
||||
$cast = PDO::PARAM_STR;
|
||||
|
||||
|
||||
}
|
||||
$result->bindValue($n, $v, $cast);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function insert($query, $args = null) {
|
||||
$result = $this->prepare($query, $args);
|
||||
|
||||
if (! $result->execute()) {
|
||||
throw new DatabaseException($result->errorInfo()[2]. ' in ('.$query.')');
|
||||
}
|
||||
|
||||
return $result->fetchAll();
|
||||
|
||||
return $this->link->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
64
web/api.php
Normal file
64
web/api.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/DB.php';
|
||||
|
||||
|
||||
class Api {
|
||||
private $db;
|
||||
|
||||
public function __construct($db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function get_tasks() {
|
||||
$tasks = [];
|
||||
$ret = $this->db->get_all_tasks();
|
||||
foreach ($ret as $t) {
|
||||
if (is_null($t['group_id'])) {
|
||||
$group_id = $t['id'];
|
||||
$group_name = 'ungrouped';
|
||||
}
|
||||
else {
|
||||
$group_id = $t['group_id'];
|
||||
$group_name = $t['group_name'];
|
||||
}
|
||||
|
||||
|
||||
if (isset($tasks[$group_id])) {
|
||||
array_push($tasks[$group_id]['tasks'], $t);
|
||||
}
|
||||
else {
|
||||
$tasks[$group_id] = [
|
||||
'id' => $group_id,
|
||||
'name' => $group_name,
|
||||
'tasks' => [ $t ]
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (isset($_GET['a'])) {
|
||||
$action = trim(htmlentities($_GET['a']), '_');
|
||||
$api = new Api($db);
|
||||
|
||||
if (method_exists($api, $action)) {
|
||||
try {
|
||||
echo json_encode(call_user_func([$api, $action]));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'result' => false
|
||||
]);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
header("HTTP/1.1 404 Not Found");
|
||||
|
|
@ -16,9 +16,18 @@ body {
|
|||
color: #3D3D3D;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: .8rem;
|
||||
margin-bottom: .8rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -28,7 +37,7 @@ h1 {
|
|||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
font-size: 1.4rem;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
@ -63,13 +72,54 @@ td {
|
|||
border: 1px solid #9ccece;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#tasks_tbl, #contacts_tbl {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.quick-view {
|
||||
margin: 3rem auto;
|
||||
max-width: 1000px;
|
||||
padding: 1rem;
|
||||
-webkit-box-shadow: 5px 5px 15px 0px rgba(0,0,0,0.3);
|
||||
-moz-box-shadow: 5px 5px 15px 0px rgba(0,0,0,0.3);
|
||||
box-shadow: 5px 5px 15px 0px rgba(0,0,0,0.3);
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.new-group {
|
||||
margin: .2rem;
|
||||
display: inline-block;
|
||||
border: 1px solid rgba(7, 7, 7, 0.432);
|
||||
padding: 1px;
|
||||
|
||||
}
|
||||
|
||||
.quick-view .square {
|
||||
width: 2rem;
|
||||
min-width: 2rem;
|
||||
max-width: 2rem;
|
||||
height: 2rem;
|
||||
min-height: 2rem;
|
||||
max-height: 2rem;
|
||||
margin: 0.1rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
clear:both;
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
|
||||
.task {
|
||||
margin: 3rem auto;
|
||||
max-width: 1000px;
|
||||
|
@ -124,6 +174,23 @@ td {
|
|||
background-image: url('../img/collapse.png');
|
||||
}
|
||||
|
||||
.up {
|
||||
background-color: #c9ecc9;
|
||||
}
|
||||
|
||||
.down {
|
||||
background-color: #ffc5c5;
|
||||
}
|
||||
|
||||
.unknown {
|
||||
background-color: rgb(243, 192, 97);
|
||||
}
|
||||
|
||||
|
||||
.square.unknown img, .square.down img, .square.up img {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10%, 90% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
|
|
BIN
web/img/unknown.png
Normal file
BIN
web/img/unknown.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
231
web/index.php
231
web/index.php
|
@ -5,144 +5,115 @@
|
|||
<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
|
||||
src="https://code.jquery.com/jquery-3.5.1.min.js"
|
||||
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<script type="text/javascript" src="js/scripts.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="css/styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>MonitoLite Dashboard</h1>
|
||||
|
||||
<?php if ($tasks = $db->get_all_tasks()): ?>
|
||||
|
||||
<?php foreach ($tasks as $task): ?>
|
||||
<div class="task">
|
||||
<p class="exp-icon" title="Click here to expand/collapse the task"> </p>
|
||||
<!--<p class="task-overlay"><img src="img/expand.png" width="32"></p>-->
|
||||
<h2>Task <small>#</small><?php echo $task['id']; ?> » <span class="highlight"><?php echo $task['type']; ?></span> for host <span class="highlight"><?php echo $task['host']; ?></span></h2>
|
||||
<div id="app">
|
||||
<h1>MonitoLite Dashboard</h1>
|
||||
|
||||
<table id="tasks_tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">Up?</th>
|
||||
<th width="*">Host</th>
|
||||
<th width="5%">Type</th>
|
||||
<th width="10%">Parameters</th>
|
||||
<th width="20%">Last execution</th>
|
||||
<th width="10%">Frequency (min)</th>
|
||||
<th width="5%">Active</th$query>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$status = $db->get_task_last_status($task['id']);
|
||||
$color = $status == 1 ? '#c9ecc9' : '#ffc5c5';
|
||||
$icon = $status == 1 ? 'up.png': 'down.png';
|
||||
?>
|
||||
<tr>
|
||||
<td style="background-color: <?php echo $color; ?>"><img src="img/<?php echo $icon; ?>" width="16" alt="Status" /></td>
|
||||
<td style="background-color: <?php echo $color; ?>"><?php echo $task['host']; ?></td>
|
||||
<td>
|
||||
<?php if ($task['type'] == 'http'): ?>
|
||||
<img src="img/http.png" width="16" alt="Warning" title="Type: <?php echo $task['type']; ?>"/>
|
||||
<?php elseif ($task['type'] == 'ping'): ?>
|
||||
<img src="img/ping.png" width="16" alt="Warning" title="Type: <?php echo $task['type']; ?>"/>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo $task['params']; ?></td>
|
||||
<td><?php echo $task['last_execution']; ?></td>
|
||||
<td><?php echo ($task['frequency'] / 60); ?></td>
|
||||
<td><?php echo ($task['active'] == 1) ? 'Yes' : 'No'; ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="hidden">
|
||||
<div id="history">
|
||||
<h3>Task history</h3>
|
||||
<?php if ($histories = $db->get_all_history($task['id'], 5)): ?>
|
||||
<table id="history_tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Datetime</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($histories as $history): ?>
|
||||
<?php
|
||||
$color = ($history['status'] == 1) ? '#c9ecc9' : '#ffc5c5';
|
||||
?>
|
||||
<tr>
|
||||
<td width="20%" align="center"><?php echo $history['datetime']; ?></td>
|
||||
|
||||
<?php if ($history['status'] == 1): ?>
|
||||
<td width="20%" align="center" style="background-color:#c9ecc9;">
|
||||
<img src="img/success.png" width="16" alt="Success"> SUCCESS
|
||||
</td>
|
||||
<?php else: ?>
|
||||
<td width="20%" align="center" style="background-color:#ffc5c5;">
|
||||
<img src="img/error.png" width="16" alt="Success"> ERROR
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><small>Only the 5 latest entries are displayed</small></p>
|
||||
<?php else: ?>
|
||||
<p class="no_result">No history found here</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div id="contacts">
|
||||
<h3>Task contacts</h3>
|
||||
|
||||
<?php if ($contacts = $db->get_all_contacts($task['id'])): ?>
|
||||
<table id="contacts_tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Surname</th>
|
||||
<th>Firstname</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>Creation date</th>
|
||||
<th>Active</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($contacts as $contact): ?>
|
||||
<tr>
|
||||
<td width="15%"><?php echo $contact['surname']; ?></td>
|
||||
<td width="15%"><?php echo $contact['firstname']; ?></td>
|
||||
<td width="20%"><?php echo $contact['email']; ?></td>
|
||||
<td width="15%"><?php echo $contact['phone']; ?></td>
|
||||
<td width="15%"><?php echo $contact['creation_date']; ?></td>
|
||||
<td width="15%"><?php echo ($contact['active'] == 1) ? 'Yes' : 'No'; ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<p class="no_result">
|
||||
<img src="img/warning.png" width="16" alt="Warning"/>
|
||||
No contact found here. That means that nobody will get any notification in case of an error.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quick-view">
|
||||
<h3>Quick overview</h3>
|
||||
<div
|
||||
v-for="group in tasks"
|
||||
v-bind:key="group.id"
|
||||
class="new-group"
|
||||
:title="group.name"
|
||||
>
|
||||
<a
|
||||
v-for="task in group.tasks"
|
||||
v-bind:key="task.id"
|
||||
:href="'#task-'+task.id"
|
||||
>
|
||||
<p :class="statusText(task.status)" class="square">
|
||||
<img :src="'/img/'+statusText(task.status)+'.png'" width="16" alt="">
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="no_result">No task found here</p>
|
||||
<?php endif; ?>
|
||||
<p class="spacer"> </p>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
v-for="group in tasks"
|
||||
v-bind:key="group.group_id"
|
||||
class="task"
|
||||
>
|
||||
<h3>Tasks for group <span class="highlight">{{ group.name }}</span> <small>(#{{ group.id }})</small> </h3>
|
||||
<table id="tasks_tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">Up?</th>
|
||||
<th width="*">Host</th>
|
||||
<th width="5%">Type</th>
|
||||
<th width="20%">Last execution</th>
|
||||
<th width="20%">Frequency (min)</th>
|
||||
<th width="5%">Active</th$query>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="task in group.tasks"
|
||||
v-bind:key="task.id"
|
||||
>
|
||||
<td :class="statusText(task.status)">
|
||||
<img :src="'img/'+statusText(task.status)+'.png'" width="16" alt="Status" />
|
||||
<a :name="'task-'+task.id"></a>
|
||||
</td>
|
||||
<td :class="statusText(task.status)">
|
||||
<a :href="task.host" target="_blank">{{ task.host }}</a>
|
||||
</td>
|
||||
<td>
|
||||
<img :src="task.type == 'http' ? 'img/http.png' : 'img/ping.png'" width="16" alt="Type of check" :title="'Type: '+task.type" />
|
||||
</td>
|
||||
<td>{{ task.last_execution ?? 'never' }}</td>
|
||||
<td>{{ task.frequency }}</td>
|
||||
<td>{{ task.active == 1 ? 'Yes' : 'No' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
props: [
|
||||
'refresh'
|
||||
],
|
||||
data: {
|
||||
tasks: []
|
||||
},
|
||||
methods: {
|
||||
// a computed getter
|
||||
statusText: function (status) {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return 'up';
|
||||
break;
|
||||
case '0':
|
||||
return 'down';
|
||||
break;
|
||||
default:
|
||||
return 'unknown';
|
||||
}
|
||||
},
|
||||
getTasks: function() {
|
||||
axios.get('api.php?a=get_tasks')
|
||||
.then(response => this.tasks = response.data)
|
||||
.catch(error => window.alert('Cannot get tasks'))
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.getTasks()
|
||||
this.refresh = window.setInterval(() => {
|
||||
this.getTasks();
|
||||
}, 60000)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
$(document).ready(function() {
|
||||
$('.task .exp-icon').on('click', function() {
|
||||
var el = $(this).parent('.task');
|
||||
|
||||
if (el.hasClass('active')) {
|
||||
el.removeClass('active');
|
||||
el.children('.hidden').slideUp();
|
||||
}
|
||||
else {
|
||||
$('.task').removeClass('active');
|
||||
$('.task').children('.hidden').slideUp();
|
||||
el.addClass('active');
|
||||
el.children('.hidden').slideDown();
|
||||
}
|
||||
});
|
||||
|
||||
$('.task').not('.active').on('mouseover', function() {
|
||||
$(this).children('.task-overlay').show();
|
||||
});
|
||||
|
||||
$('.task').not('.active').on('mouseout', function() {
|
||||
$(this).children('.task-overlay').hide();
|
||||
});
|
||||
|
||||
})
|
Loading…
Add table
Reference in a new issue