diff --git a/.gitignore b/.gitignore index 1840dc6..ec04b3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ web/vendor/**/* .env +web/sync.php +/**/node_modules diff --git a/web/DB.php b/web/DB.php index 137d69f..dd9b92a 100644 --- a/web/DB.php +++ b/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(); } } diff --git a/web/api.php b/web/api.php new file mode 100644 index 0000000..6909e0c --- /dev/null +++ b/web/api.php @@ -0,0 +1,64 @@ +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"); + diff --git a/web/css/styles.css b/web/css/styles.css index 1af524c..8455596 100644 --- a/web/css/styles.css +++ b/web/css/styles.css @@ -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); diff --git a/web/img/unknown.png b/web/img/unknown.png new file mode 100644 index 0000000..10b42f8 Binary files /dev/null and b/web/img/unknown.png differ diff --git a/web/index.php b/web/index.php index 4a5d2a0..c011b61 100644 --- a/web/index.php +++ b/web/index.php @@ -5,144 +5,115 @@
Up? | -Host | -Type | -Parameters | -Last execution | -Frequency (min) | -Active | -
---|---|---|---|---|---|---|
- |
-
- ![]() ![]() |
- - | - | - | - |
Datetime | -Status | -|
---|---|---|
- - - |
- ![]() |
-
-
- ![]() |
-
-
Only the 5 latest entries are displayed
- -No history found here
- -Surname | -Firstname | -Phone | -Creation date | -Active | -|
---|---|---|---|---|---|
- | - | - | - | - | - |
-
- No contact found here. That means that nobody will get any notification in case of an error.
-
Up? | +Host | +Type | +Last execution | +Frequency (min) | +Active | +
---|---|---|---|---|---|
+ |
+ + {{ task.host }} + | +
+ |
+ {{ task.last_execution ?? 'never' }} | +{{ task.frequency }} | +{{ task.active == 1 ? 'Yes' : 'No' }} | +