diff --git a/public/assets/css/style.css b/public/assets/css/style.css index f341183..ee295e7 100644 --- a/public/assets/css/style.css +++ b/public/assets/css/style.css @@ -1,6 +1,98 @@ +*, :after, :before { + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +a { + color: #0063ff; + text-decoration: none; + border-bottom: .0625rem solid rgba(0, 99, 255, .3); +} + +a:hover, +a:visited:hover { + color: #ff475d; + border-bottom: .0625rem solid rgba(255, 71, 93, .24); + +} + +a:visited { + color: #cf00cf; + border-bottom: .0625rem solid rgba(207, 0, 207, .3); +} + body { font-family: Verdana, "Geneva CY", "DejaVu Sans", sans-serif; font-size: 1rem; font-weight: normal; + line-height: 1.25rem; } +.header { + display: grid; + grid-template-rows: 1fr 1fr; + grid-template-columns: 1fr 6fr; + grid-gap: 1vw; +} + +.header, .main, .footer, .navigate ul { + margin: 0 auto; + max-width: 1000px; +} + +.header__img { + grid-row: 1/3; + grid-column: 1; + margin-left: -1.75rem; + margin-right: -1.75rem; +} + +.header__title { + margin-bottom: 0; + line-height: 1; + margin-top: 3rem; +} + +.navigate { + background: black; + margin-top: 1.25rem; +} + +.navigate ul { + list-style: none unset none; + padding: .375rem 0; +} + +.navigate ul li { + display: inline-block; + width: 32%; + text-align: center; +} + +.navigate ul li a { + color: white; + text-decoration: none; +} + +.navigate ul li a:hover { + color: white; + border-bottom: .0625rem solid rgba(255, 255, 255, .6); +} + +.footer { + margin-top: 2rem; + padding-top: 1rem; + padding-bottom: 1rem; +} +.footer::before { + width: 100%; + display: block; + content: " "; + border-top: .25rem solid black; + position: absolute; + left: 0; +} + +table { + border-collapse: collapse; +} \ No newline at end of file diff --git a/src/App.php b/src/App.php index 8e58e30..9d50c2f 100644 --- a/src/App.php +++ b/src/App.php @@ -11,6 +11,7 @@ class App 'Controller\\' => 'controllers/', 'Core\\' => 'core/', 'Model\\' => 'models/', + 'Service\\' => 'services/', 'View\\' => 'views/', ]; private $basedir = __DIR__; @@ -26,7 +27,7 @@ class App /** * App constructor. * - * @param bool $start Start router. Default false. + * @param bool $start Start router. Default true. */ public function __construct(bool $start = true) { diff --git a/src/controllers/DefaultController.php b/src/controllers/DefaultController.php index e955b8c..a40d6c6 100644 --- a/src/controllers/DefaultController.php +++ b/src/controllers/DefaultController.php @@ -12,7 +12,6 @@ class DefaultController extends Controller */ public function index() { - (new View())->index(); - $Model = new \MyApp\Model\GroupsModel(); + $this->htmlResponse((new View())->index()); } } diff --git a/src/controllers/ErrorsController.php b/src/controllers/ErrorsController.php index 81310fc..0a2b436 100644 --- a/src/controllers/ErrorsController.php +++ b/src/controllers/ErrorsController.php @@ -3,13 +3,14 @@ namespace MyApp\Controller; use MyApp\Core\Controller; +use MyApp\Core\View; class ErrorsController extends Controller { public function get404() { - http_response_code(404); - echo 404; + $this->htmlResponse((new View())->setTitle('404 Page not found')->render('404'), + 404); } public function post404() diff --git a/src/controllers/GroupsController.php b/src/controllers/GroupsController.php new file mode 100644 index 0000000..8a6e19f --- /dev/null +++ b/src/controllers/GroupsController.php @@ -0,0 +1,43 @@ +<?php + +namespace MyApp\Controller; + +use MyApp\Core\Controller; +use MyApp\View\GroupsView as View; +use MyApp\Service\GroupsService; + +class GroupsController extends Controller +{ + /** + * @RouteRegExp = "|^/groups$|" + */ + public function index() + { + $this->htmlResponse((new View())->index((new GroupsService())->getTableData())); + } + + /** + * @RouteRegExp = "|^/groups/add$|" + */ + public function add() + { + + } + + /** + * @RouteRegExp = "|^/groups/edit/(?<id>\d+)$|" + */ + public function edit($id) + { + $group_service = new GroupsService(); + + $model = $group_service->getGroup($id); + + if ($model) { + $this->htmlResponse((new View())->edit($group_service->getGroup($id), + $group_service->getTableData())); + } else { + $this->redirect('/groups'); + } + } +} diff --git a/src/controllers/ServersController.php b/src/controllers/ServersController.php new file mode 100644 index 0000000..9f9556b --- /dev/null +++ b/src/controllers/ServersController.php @@ -0,0 +1,30 @@ +<?php + +namespace MyApp\Controller; + +use MyApp\Core\Controller; + +class ServersController extends Controller +{ + /** + * @RouteRegExp = "|^/servers$|" + */ + public function index() + { + + } + + /** + * @RouteRegExp = "|^/servers/add$|" + */ + public function add() + { + } + + /** + * @RouteRegExp = "|^/servers/edit/(?<id>\d+)$|" + */ + public function edit($id) + { + } +} diff --git a/src/core/Config.php b/src/core/Config.php index 204c48b..3448ed6 100644 --- a/src/core/Config.php +++ b/src/core/Config.php @@ -2,6 +2,9 @@ namespace MyApp\Core; +/** + * Class Config. + */ class Config { /** diff --git a/src/core/Controller.php b/src/core/Controller.php index eabeab2..bedcc49 100644 --- a/src/core/Controller.php +++ b/src/core/Controller.php @@ -2,6 +2,49 @@ namespace MyApp\Core; +/** + * Class Controller. + */ class Controller { + public function __construct() + { + } + + /** + * Send JSON response. + * + * @param mixed $data + * @param int $http_response_code HTTP code. Default 200 + */ + public function jsonResponse($data = null, $http_response_code = 200) + { + http_response_code($http_response_code); + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($data, JSON_UNESCAPED_UNICODE); + exit(0); + } + + /** + * Send html response. + * + * @param mixed $data + * @param int $http_response_code HTTP code. Default 200 + */ + public function htmlResponse($data = null, $http_response_code = 200) + { + http_response_code($http_response_code); + header('Content-Type: text/html; charset=utf-8'); + echo $data; + exit(0); + } + + /** + * @param string $path + * @param int $http_response_code + */ + public function redirect( $path = '/', $http_response_code = 302 ) { + header( "Location: $path", true, $http_response_code ); + exit; + } } diff --git a/src/core/View.php b/src/core/View.php index 5e8a24e..d3564da 100644 --- a/src/core/View.php +++ b/src/core/View.php @@ -8,23 +8,36 @@ class View * @var string */ public $title = ''; + /** + * @var string + */ + public $siteName = ''; - public function json($data) + public function __construct() { } - public function render($template) + /** + * @param string $template + * + * @return string + */ + public function render(string $template): string { /* @var \App $app */ global $app; $fileName = $app->config->getSrcDir().'/templates/'.$template.'.tpl.php'; + $out = ''; if (file_exists($fileName)) { ob_start(); require $fileName; - ob_end_flush(); + $out = ob_get_contents(); + ob_end_clean(); } + + return $out; } /** @@ -37,7 +50,8 @@ class View /* @var \App $app */ global $app; - $this->title = $title.' — '.$app->config->getSiteName(); + $this->siteName = $app->config->getSiteName(); + $this->title = $title; return $this; } diff --git a/src/models/GroupsModel.php b/src/models/GroupsModel.php index 1f29a97..c055482 100644 --- a/src/models/GroupsModel.php +++ b/src/models/GroupsModel.php @@ -74,4 +74,12 @@ class GroupsModel extends Model $this->parent = $parent; return $this; } + + /** + * @return int + */ + public function getParent(): ?int + { + return $this->parent; + } } \ No newline at end of file diff --git a/src/models/ServersModel.php b/src/models/ServersModel.php index a88b918..5062f58 100644 --- a/src/models/ServersModel.php +++ b/src/models/ServersModel.php @@ -1,13 +1,113 @@ <?php - namespace MyApp\Model; +use MyApp\Core\Model; -class ServersModel +/** + * Class ServersModel. + * + * @TableName = "servers" + * + * @package MyApp\Model + */ +class ServersModel extends Model { + /** + * @ColumnName = "id" + * @ColumnOption = "id" + * @ColumnType = "int unsigned auto_increment primary key" + * + * @var int + */ private $id; + /** + * @ColumnName = "name" + * @ColumnType = "varchar(255) null" + * + * @var string + */ private $name; + /** + * @ColumnName = "ip" + * @ColumnType = "int unsigned null" + * + * @var int + */ private $ip; + /** + * @ColumnName = "group_id" + * @ColumnType = "int unsigned null, constraint servers_groups_id_fk foreign key (group_id) references `groups` (id) on delete cascade" + * + * @var int + */ private $group; -} \ No newline at end of file + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @param string $name + * + * @return ServersModel + */ + public function setName(string $name): self + { + $this->name = $name; + + return $this; + } + + /** + * @return string + */ + public function getIp(): string + { + return long2ip($this->ip); + } + + /** + * @param string $ip + * + * @return ServersModel + */ + public function setIp(string $ip): self + { + $this->ip = ip2long($ip); + + return $this; + } + + /** + * @return int + */ + public function getGroup(): int + { + return $this->group; + } + + /** + * @param int $group + * + * @return ServersModel + */ + public function setGroup(int $group): self + { + $this->group = $group; + + return $this; + } +} diff --git a/src/models/TallyLogsModel.php b/src/models/TallyLogsModel.php new file mode 100644 index 0000000..dc74242 --- /dev/null +++ b/src/models/TallyLogsModel.php @@ -0,0 +1,146 @@ +<?php + +namespace MyApp\Model; + +use MyApp\Core\Model; +use DateTime; + +/** + * Class LogsModel. + * + * @TableName = "logs" + */ +class TallyLogsModel extends Model +{ + /** + * @ColumnName = "id" + * @ColumnOption = "id" + * @ColumnType = "int unsigned auto_increment primary key" + * + * @var int + */ + private $id; + /** + * @ColumnName = "time" + * @ColumnType = "timestamp null" + * + * @var int + */ + private $time; + /** + * @ColumnName = "transmitted" + * @ColumnType = "int null" + * + * @var int + */ + private $transmitted; + /** + * @ColumnName = "received" + * @ColumnType = "int null" + * + * @var int + */ + private $received; + /** + * @ColumnName = "lost" + * @ColumnType = "int null" + * + * @var int + */ + private $lost; + /** + * @ColumnName = "server_id" + * @ColumnType = "int unsigned null, constraint logs_servers_id_fk foreign key (server_id) references `servers` (id) on delete cascade" + * + * @var int + */ + private $server; + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return int + */ + public function getTime(): int + { + return $this->time; + } + + /** + * @param DateTime $time + * + * @return TallyLogsModel + */ + public function setTime(DateTime $time): self + { + $this->time = $time->getTimestamp(); + + return $this; + } + + /** + * @return int + */ + public function getTransmitted(): int + { + return $this->transmitted; + } + + /** + * @param int $transmitted + * + * @return TallyLogsModel + */ + public function setTransmitted(int $transmitted): self + { + $this->transmitted = $transmitted; + + return $this; + } + + /** + * @return int + */ + public function getReceived(): int + { + return $this->received; + } + + /** + * @param int $received + * + * @return TallyLogsModel + */ + public function setReceived(int $received): self + { + $this->received = $received; + + return $this; + } + + /** + * @return int + */ + public function getLost(): int + { + return $this->lost; + } + + /** + * @param int $lost + * + * @return TallyLogsModel + */ + public function setLost(int $lost): self + { + $this->lost = $lost; + + return $this; + } +} diff --git a/src/services/GroupsService.php b/src/services/GroupsService.php new file mode 100644 index 0000000..fe12c0a --- /dev/null +++ b/src/services/GroupsService.php @@ -0,0 +1,34 @@ +<?php + + +namespace MyApp\Service; + +use MyApp\Model\GroupsModel; +use Exception; + +class GroupsService +{ + /** + * @return GroupsModel[]|null + * + * @throws Exception + */ + public function getTableData(): ? array + { + $models = GroupsModel::find(null,null,'1 = 1'); + + return $models; + } + + public function getGroup($id): ? GroupsModel + { + /** @var GroupsModel[] $model */ + $model = GroupsModel::find([$id],null,null,[],'','1'); + + if (!empty($model)){ + return $model[0]; + } + + return null; + } +} \ No newline at end of file diff --git a/src/services/PingService.php b/src/services/PingService.php new file mode 100644 index 0000000..5c3f78e --- /dev/null +++ b/src/services/PingService.php @@ -0,0 +1,38 @@ +<?php + +namespace MyApp\Service; + +use MyApp\Model\TallyLogsModel; +use MyApp\Core\Model; +use MyApp\Model\ServersModel; +use DateTime; + +class PingService +{ + public function ping($server_id): ? TallyLogsModel + { + $server = ServersModel::find([$server_id],null,null,[],'','1'); + + if (!empty($server)){ + $ip = $server[0]->getIp(); + exec("ping -c 10 $ip", $output, $status); + foreach ($output as $line) { + if (preg_match('/(?<transmitted>\d+)[\w\ ]*,\ (?<received>\d+)[\w\ ]*, (?<lost>\d+)%/', + $line, $m)) { + $log = new TallyLogsModel(); + + $log->setTime(new DateTime()) + ->setLost($m['lost']) + ->setTransmitted($m['transmitted']) + ->setReceived($m['received']); + + Model::save([$log]); + + return $log; + } + } + } + + return null; + } +} diff --git a/src/templates/404.tpl.php b/src/templates/404.tpl.php new file mode 100644 index 0000000..6ff84cc --- /dev/null +++ b/src/templates/404.tpl.php @@ -0,0 +1,5 @@ +<?php +require __DIR__ . '/shared/head.php'; +?> +<?php +require __DIR__ . '/shared/footer.php'; \ No newline at end of file diff --git a/src/templates/default/index.tpl.php b/src/templates/default/index.tpl.php index 06e597c..a119214 100644 --- a/src/templates/default/index.tpl.php +++ b/src/templates/default/index.tpl.php @@ -1,3 +1,5 @@ <?php +require __DIR__ . '/../shared/head.php'; ?> -require __DIR__.'/../shared/head.php'; +<?php +require __DIR__ . '/../shared/footer.php'; diff --git a/src/templates/groups/add.tpl.php b/src/templates/groups/add.tpl.php new file mode 100644 index 0000000..4fffdd8 --- /dev/null +++ b/src/templates/groups/add.tpl.php @@ -0,0 +1,7 @@ +<?php +require __DIR__ . '/../shared/head.php'; ?> + <form action=""> + + </form> +<?php +require __DIR__ . '/../shared/footer.php'; diff --git a/src/templates/groups/edit.tpl.php b/src/templates/groups/edit.tpl.php new file mode 100644 index 0000000..d8ab2b9 --- /dev/null +++ b/src/templates/groups/edit.tpl.php @@ -0,0 +1,51 @@ +<?php +require __DIR__ . '/../shared/head.php'; + +/** @var \MyApp\Model\GroupsModel $model */ +$model = $this->model; + +function getTree($table, $parent_id = null, $level = 1) +{ + $out = []; + foreach ($table as $item) { + /** @var \MyApp\Model\GroupsModel $item */ + if ( + $item->getParent() === $parent_id) { + $item->{"select-level"} = str_repeat("-", $level); + $out[] = $item; + $out = array_merge($out, + getTree($table, $item->getId(), $level + 1)); + } + } + + return $out; +} + +?> + <br> + + <form action="" method="post"> + <input type="hidden" value="<?= $model->getId() ?>" name="id"> + <div> + <label for="parent">Parent:</label> + <select name="parent" id="parent"> + <option value="null" + <?php if (is_null($model->getParent())): ?>selected<?php endif; ?>> + Root + </option> + <?php foreach (getTree($this->table) as $item): ?> + <option value="<?= $item->getId() ?>" + <?php if ($item->getId() === $model->getParent()): ?>selected<?php endif; ?>> + <?= $item->{"select-level"} ?><?= $item->getName() ?> + </option> + <?php endforeach; ?> + </select> + </div> + <div> + <label for="name">Name:</label> + <input type="text" value="<?= $model->getName() ?>" name="name" id="name"> + </div> + <button type="submit">Submit</button> + </form> +<?php +require __DIR__ . '/../shared/footer.php'; diff --git a/src/templates/groups/index.tpl.php b/src/templates/groups/index.tpl.php new file mode 100644 index 0000000..b1b12df --- /dev/null +++ b/src/templates/groups/index.tpl.php @@ -0,0 +1,24 @@ +<?php +require __DIR__ . '/../shared/head.php'; ?> + <?php if ($this->table): ?> + <br> + <table border="1" width="100%"> + <tr> + <th>Id</th> + <th>Name</th> + <th>Parent</th> + <th>Actions</th> + </tr> + <?php foreach ($this->table as $item):?> + <tr> + <td><?= $item->getId() ?></td> + <td><?= $item->getName() ?></td> + <td><?= $item->getParent() ?></td> + <td><a href="/groups/edit/<?= $item->getId() ?>">edit</a></td> + </tr> + + <?php endforeach; ?> + </table> + <?php endif; ?> +<?php +require __DIR__ . '/../shared/footer.php'; diff --git a/src/templates/servers/add.tpl.php b/src/templates/servers/add.tpl.php new file mode 100644 index 0000000..4fffdd8 --- /dev/null +++ b/src/templates/servers/add.tpl.php @@ -0,0 +1,7 @@ +<?php +require __DIR__ . '/../shared/head.php'; ?> + <form action=""> + + </form> +<?php +require __DIR__ . '/../shared/footer.php'; diff --git a/src/templates/servers/index.tpl.php b/src/templates/servers/index.tpl.php new file mode 100644 index 0000000..e968237 --- /dev/null +++ b/src/templates/servers/index.tpl.php @@ -0,0 +1,7 @@ +<?php +require __DIR__ . '/../shared/head.php'; ?> +<table> + +</table> +<?php +require __DIR__ . '/../shared/footer.php'; diff --git a/src/templates/shared/footer.php b/src/templates/shared/footer.php new file mode 100644 index 0000000..5172c93 --- /dev/null +++ b/src/templates/shared/footer.php @@ -0,0 +1,7 @@ +</section> +<footer class="footer"> + <br> + <a href="https://belousovv.ru" target="_blank">Belousovv.ru</a> +</footer> +</body> +</html> diff --git a/src/templates/shared/head.php b/src/templates/shared/head.php index 3825df7..a0ec8d5 100644 --- a/src/templates/shared/head.php +++ b/src/templates/shared/head.php @@ -14,5 +14,21 @@ <meta name="theme-color" content="#ffffff"> <link rel="stylesheet" href="/assets/css/normalize.css"> <link rel="stylesheet" href="/assets/css/style.css"> - <title><?= $this->title; ?></title> + <title><?= $this->title; ?> — <?= $this->siteName; ?></title> </head> +<body> +<header class="header"> + <img class="header__img" src="/assets/img/favicon/android-chrome-384x384.png" alt="" height="192" width="192"> + <h1 class="header__title"><?= $this->siteName; ?></h1> + <h2 class="header__title"><?= $this->title; ?></h2> +</header> +<nav class="navigate"> + <ul> + <li><a href="/">Home</a></li> + <li><a href="/servers">Edit servers</a></li> + <li><a href="/groups">Edit groups</a></li> + </ul> +</nav> +<section class="main"> + + diff --git a/src/views/DefaultView.php b/src/views/DefaultView.php index 6929ac8..731c52c 100644 --- a/src/views/DefaultView.php +++ b/src/views/DefaultView.php @@ -6,8 +6,8 @@ use MyApp\Core\View; class DefaultView extends View { - public function index() + public function index(): string { - $this->setTitle('Index')->render('default/index'); + return $this->setTitle('Index')->render('default/index'); } } diff --git a/src/views/GroupsView.php b/src/views/GroupsView.php new file mode 100644 index 0000000..e20d7a0 --- /dev/null +++ b/src/views/GroupsView.php @@ -0,0 +1,26 @@ +<?php + + +namespace MyApp\View; + + +use MyApp\Core\View; + +class GroupsView extends View +{ + + public function index(?array $table) + { + $this->{"table"} = $table; + + return $this->setTitle('Edit groups')->render('groups/index'); + } + + public function edit($model, ?array $table) + { + $this->{"model"} = $model; + $this->{"table"} = $table; + + return $this->setTitle('Edit groups')->render('groups/edit'); + } +} \ No newline at end of file diff --git a/src/views/ServersView.php b/src/views/ServersView.php new file mode 100644 index 0000000..ae7d554 --- /dev/null +++ b/src/views/ServersView.php @@ -0,0 +1,12 @@ +<?php + + +namespace MyApp\View; + + +use MyApp\Core\View; + +class ServersView extends View +{ + +} \ No newline at end of file