after 6 years update. hell yeah

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2025-05-15 19:04:24 +07:00
parent 7e1993730a
commit bb3a611b7d
57 changed files with 1221 additions and 1125 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
codeception.yml Normal file → Executable file
View File

80
composer.json Normal file → Executable file
View File

@ -1,42 +1,42 @@
{ {
"name": "mirzaev/beejee", "name": "mirzaev/beejee",
"description": "Test BeeJee", "description": "Task manager. MVC + CRUD. Developed in 2 days (2019)",
"type": "project", "type": "project",
"license": "AGPL-3.0-or-later", "license": "WTFPL",
"homepage": "https://git.hood.su/mirzaev/beejee", "homepage": "https://git.hood.su/mirzaev/beejee",
"authors": [ "authors": [
{ {
"name": "Arsen Mirzaev Tatyano-Muradovich", "name": "Arsen Mirzaev Tatyano-Muradovich",
"email": "red@hood.su", "email": "arsen@mirzaev.sexy",
"role": "Developer" "role": "Developer"
} }
], ],
"require": { "require": {
"php": ">=7.4.0", "php": ">=8.4.0",
"ext-PDO": "^7.4", "ext-PDO": "^8.4",
"twbs/bootstrap": "^4.5", "twbs/bootstrap": "^4.5",
"twig/twig": "^3.1" "twig/twig": "^3.1"
}, },
"require-dev": { "require-dev": {
"codeception/codeception": "^4.1", "codeception/codeception": "^4.1",
"codeception/module-webdriver": "^1.0.0" "codeception/module-webdriver": "^1.0.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"mirzaev\\beejee\\": "mirzaev/beejee/system" "mirzaev\\beejee\\": "mirzaev/beejee/system"
} }
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"mirzaev\\beejee\\tests\\": "mirzaev/beejee/tests" "mirzaev\\beejee\\tests\\": "mirzaev/beejee/tests"
} }
}, },
"scripts": { "scripts": {
"post-update-cmd": [ "post-update-cmd": [
"mkdir -p .\\mirzaev\\beejee\\system\\web\\css\\bootstrap > /dev/null 2>&1 || true", "mkdir -p .\\mirzaev\\beejee\\system\\web\\css\\bootstrap > /dev/null 2>&1 || true",
"mkdir -p .\\mirzaev\\beejee\\system\\web\\js\\bootstrap > /dev/null 2>&1 || true", "mkdir -p .\\mirzaev\\beejee\\system\\web\\js\\bootstrap > /dev/null 2>&1 || true",
"cp -R .\\vendor\\twbs\\bootstrap\\dist\\css\\* .\\mirzaev\\beejee\\system\\web\\css\\bootstrap", "cp -R .\\vendor\\twbs\\bootstrap\\dist\\css\\* .\\mirzaev\\beejee\\system\\web\\css\\bootstrap",
"cp -R .\\vendor\\twbs\\bootstrap\\dist\\js\\* .\\\\mirzaev\\beejee\\system\\web\\js\\bootstrap" "cp -R .\\vendor\\twbs\\bootstrap\\dist\\js\\* .\\\\mirzaev\\beejee\\system\\web\\js\\bootstrap"
] ]
} }
} }

1751
composer.lock generated Normal file → Executable file

File diff suppressed because it is too large Load Diff

2
mirzaev/beejee/system/controllers/authController.php Normal file → Executable file
View File

@ -22,7 +22,7 @@ final class authController extends controller
* @return array|null * @return array|null
*/ */
public function auth(array $params = null): ?array public function auth(array $params = null): ?array
{ {
$login = $params['login'] ?? $_COOKIE['login']; $login = $params['login'] ?? $_COOKIE['login'];
$password = $params['password'] ?? $_COOKIE['password']; $password = $params['password'] ?? $_COOKIE['password'];

234
mirzaev/beejee/system/controllers/controller.php Normal file → Executable file
View File

@ -5,10 +5,10 @@ declare(strict_types=1);
namespace mirzaev\beejee\controllers; namespace mirzaev\beejee\controllers;
use mirzaev\beejee\core, use mirzaev\beejee\core,
mirzaev\beejee\models\model; mirzaev\beejee\models\model;
use Twig\Loader\FilesystemLoader, use Twig\Loader\FilesystemLoader,
Twig\Environment as view; Twig\Environment as view;
use Exception; use Exception;
@ -20,129 +20,131 @@ use Exception;
*/ */
class controller class controller
{ {
/** /**
* @var model $model Модель * @var model $model Модель
*/ */
protected model $model; protected model $model;
/** /**
* @var view $view Шаблонизатор представления * @var view $view Шаблонизатор представления
*/ */
protected view $view; protected view $view;
/** /**
* Конструктор * Конструктор
* *
* @return void * @return void
*/ */
public function __construct() public function __construct()
{ {
// Установка значения по умолчанию для модели (если будет найдена) // Установка значения по умолчанию для модели (если будет найдена)
$this->__get('model'); $this->__get('model');
// Установка значения по умолчанию для шаблонизатора представлений
$this->__get('view');
}
/** // Установка значения по умолчанию для шаблонизатора представлений
* Отрисовка шаблона $this->__get('view');
* }
* @param string $route Маршрут
*/
public function view(string $route)
{
// Чтение представления по шаблону пути: "/views/[controller]/index
// Никаких слоёв и шаблонизаторов
// Не стал в ядре записывать путь до шаблонов
if (file_exists($view = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $route . DIRECTORY_SEPARATOR . 'index.html')) {
include $view;
}
}
/** /**
* Записать свойство * Отрисовка шаблона
* *
* @param mixed $name Название * @param string $route Маршрут
* @param mixed $value Значение */
* public function view(string $route)
* @return void {
*/ // Чтение представления по шаблону пути: "/views/[controller]/index
public function __set($name, $value): void // Никаких слоёв и шаблонизаторов
{ // Не стал в ядре записывать путь до шаблонов
if ($name === 'model') { if (file_exists($view = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $route . DIRECTORY_SEPARATOR . 'index.html')) {
if (!isset($this->model)) { include $view;
$this->model = $value; }
return; }
} else {
throw new Exception('Запрещено переопределять модель');
}
} else if ($name === 'view') {
if (!isset($this->view)) {
$this->view = $value;
return;
} else {
throw new Exception('Запрещено переопределять шаблонизатор представления');
}
}
throw new Exception('Свойство не найдено: ' . $name); /**
} * Записать свойство
*
* @param mixed $name Название
* @param mixed $value Значение
*
* @return void
*/
public function __set($name, $value): void
{
if ($name === 'model') {
if (!isset($this->model)) {
$this->model = $value;
return;
} else {
throw new Exception('Запрещено переопределять модель');
}
} else if ($name === 'view') {
if (!isset($this->view)) {
$this->view = $value;
return;
} else {
throw new Exception('Запрещено переопределять шаблонизатор представления');
}
}
/** throw new Exception('Свойство не найдено: ' . $name);
* Прочитать свойство }
*
* @param mixed $name Название
*
* @return mixed
*/
public function __get($name)
{
if ($name === 'model') {
if (isset($this->model)) {
// Если модель найдена
return $this->model;
} else {
// Инициализация класса модели
$model = preg_replace('/' . core::controllerPostfix() . '$/i', '', basename(get_class($this))) . core::modelPostfix();
// Иначе
if (class_exists($model_class = core::namespace() . '\\models\\' . $model)) {
// Если найдена одноимённая с контроллером модель (без постфикса)
return $this->model = new $model_class;
}
return;
}
} else if ($name === 'view') {
if (isset($this->view)) {
// Если модель найдена
return $this->view;
} else {
$path = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views';
$loader = new FilesystemLoader($path);
return $this->view = (new view($loader, [ /**
// 'cache' => $path . DIRECTORY_SEPARATOR . 'cache', * Прочитать свойство
])); *
} * @param mixed $name Название
} *
* @return mixed
*/
public function __get($name)
{
if ($name === 'model') {
if (isset($this->model)) {
// Если модель найдена
return $this->model;
} else {
// Инициализация класса модели
$model = preg_replace('/' . core::controllerPostfix() . '$/i', '', substr($this::class, strrpos($this::class, '\\') + 1)) . core::modelPostfix();
throw new Exception('Свойство не найдено: ' . $name); // Иначе
} if (class_exists($model_class = core::namespace() . '\\models\\' . $model)) {
// Если найдена одноимённая с контроллером модель (без постфикса)
return $this->model = new $model_class;
}
return;
}
} else if ($name === 'view') {
if (isset($this->view)) {
// Если модель найдена
return $this->view;
} else {
$path = core::path() . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views';
$loader = new FilesystemLoader($path);
return $this->view = (new view($loader, [
// 'cache' => $path . DIRECTORY_SEPARATOR . 'cache',
]));
}
}
throw new Exception('Свойство не найдено: ' . $name);
}
/** /**
* Проверить свойство на инициализированность * Проверить свойство на инициализированность
* *
* @param string $name Название * @param string $name Название
* *
* @return mixed * @return mixed
*/ */
public function __isset(string $name) public function __isset(string $name)
{ {
if ($name === 'model') { if ($name === 'model') {
return isset($this->model); return isset($this->model);
} else if ($name === 'view') { } else if ($name === 'view') {
return isset($this->view); return isset($this->view);
} }
throw new Exception('Свойство не найдено: ' . $name); throw new Exception('Свойство не найдено: ' . $name);
} }
} }

0
mirzaev/beejee/system/controllers/errorsController.php Normal file → Executable file
View File

36
mirzaev/beejee/system/controllers/mainController.php Normal file → Executable file
View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace mirzaev\beejee\controllers; namespace mirzaev\beejee\controllers;
use mirzaev\beejee\controllers\controller, use mirzaev\beejee\controllers\controller,
mirzaev\beejee\controllers\tasksController; mirzaev\beejee\controllers\tasksController;
use PDO; use PDO;
@ -17,24 +17,26 @@ use PDO;
*/ */
final class mainController extends controller final class mainController extends controller
{ {
public function index(array $params) public function index(array $params)
{ {
$tasks = new tasksController; $tasks = new tasksController;
// Нормализация $params['page'] ??= '/';
$page['current'] = filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT);
$page['sort'] = filter_var($params['page'], FILTER_SANITIZE_STRING);
// Инициализация страниц заданий // Нормализация
$page['count'] = $tasks->count(); $page['current'] = filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT);
$page['current'] = filter_var($params['page'], FILTER_VALIDATE_INT, ['options' => ['default' => 1]]); $page['sort'] = filter_var($params['page'], FILTER_SANITIZE_STRING);
$page['previous'] = $page['current'] > 1 ? $page['current'] - 1 : 1;
$page['next'] = (int) $page['current'] < (int) $page['count'] ? $page['current'] + 1 : $page['current'];
// Инициализация сортировки // Инициализация страниц заданий
$sort = empty($params['sort']) ? 'id' : $params['sort']; $page['count'] = $tasks->count();
$page['current'] = filter_var($params['page'], FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['previous'] = $page['current'] > 1 ? $page['current'] - 1 : 1;
$page['next'] = (int) $page['current'] < (int) $page['count'] ? $page['current'] + 1 : $page['current'];
// Генерация представления // Инициализация сортировки
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', ['page' => $page, 'sort' => $sort]); $sort = empty($params['sort']) ? 'id' : $params['sort'];
}
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', ['page' => $page, 'sort' => $sort]);
}
} }

222
mirzaev/beejee/system/controllers/tasksController.php Normal file → Executable file
View File

@ -14,134 +14,134 @@ use mirzaev\beejee\controllers\controller;
*/ */
final class tasksController extends controller final class tasksController extends controller
{ {
/** /**
* Записать в базу данных * Записать в базу данных
* *
* @param array $params * @param array $params
* *
* @return string|null * @return string|null
*/ */
public function create(array $params): ?string public function create(array $params): ?string
{ {
// Инициализация параметров // Инициализация параметров
$name = $params['name']; $name = $params['name'];
$email = $params['email']; $email = $params['email'];
$task = $params['task']; $task = $params['task'];
session_start(); session_start();
if (!isset($_SESSION['task_create_last']) || (isset($_SESSION['task_create_last']) && $_SESSION['task_create_last'] < time() - 5)) { if (!isset($_SESSION['task_create_last']) || (isset($_SESSION['task_create_last']) && $_SESSION['task_create_last'] < time() - 5)) {
// Если это первый вызов или последний вызов был более 5 секунд назад // Если это первый вызов или последний вызов был более 5 секунд назад
// Запись задания // Запись задания
$this->model->create($name, $email, $task); $this->model->create($name, $email, $task);
$_SESSION['task_create_last'] = time(); $_SESSION['task_create_last'] = time();
return null; return null;
} }
return 'Следующее задание можно создать через ' . (5 - (time() - $_SESSION['task_create_last'])) . ' секунд'; return 'Следующее задание можно создать через ' . (5 - (time() - $_SESSION['task_create_last'])) . ' секунд';
} }
/** /**
* Прочитать из базы данных * Прочитать из базы данных
* *
* @param array $params * @param array $params
* *
* @return array * @return array
*/ */
public function read(array $params): array public function read(array $params): array
{ {
// Нормализация // Нормализация
$page['current'] = filter_var(filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]); $page['current'] = filter_var(filter_var($params['page'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['sort'] = filter_var($params['sort'], FILTER_SANITIZE_STRING); $page['sort'] = filter_var($params['sort'] ?? '', FILTER_SANITIZE_STRING);
// Инициализация параметров // Инициализация параметров
$limit = 3; $limit = 3;
$sort = empty($params['sort']) ? 'id' : $params['sort']; $sort = empty($params['sort']) ? 'id' : $params['sort'];
$page = ($params['page'] - 1) * $limit; $page = ($params['page'] - 1) * $limit;
// Чтение заданий // Чтение заданий
return $this->model->read($page, $limit, $sort); return $this->model->read($page, $limit, $sort);
} }
/** /**
* Обновить в базе данных * Обновить в базе данных
* *
* @param array $params * @param array $params
* *
* @return void * @return void
*/ */
public function update(array $params): void public function update(array $params): void
{ {
session_start(); session_start();
// Нормализация // Нормализация
$page['id'] = filter_var(filter_var($params['id'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]); $page['id'] = filter_var(filter_var($params['id'], FILTER_SANITIZE_NUMBER_INT), FILTER_VALIDATE_INT, ['options' => ['default' => 1]]);
$page['name'] = filter_var($params['name'], FILTER_SANITIZE_STRING); $page['name'] = filter_var($params['name'], FILTER_SANITIZE_STRING);
$page['email'] = filter_var($params['email'], FILTER_SANITIZE_EMAIL); $page['email'] = filter_var($params['email'], FILTER_SANITIZE_EMAIL);
$page['task'] = filter_var($params['task'], FILTER_SANITIZE_STRING); $page['task'] = filter_var($params['task'], FILTER_SANITIZE_STRING);
// Инициализация параметров // Инициализация параметров
$id = (int) $params['id']; $id = (int) $params['id'];
$name = $params['name']; $name = $params['name'];
$email = $params['email']; $email = $params['email'];
$task = $params['task']; $task = $params['task'];
$completed = $_SESSION['admin'] == 1 ? $params['completed'] : null; $completed = $_SESSION['admin'] == 1 ? $params['completed'] : null;
// Запись задания // Запись задания
$this->model->update($id, $name, $email, $task, $completed); $this->model->update($id, $name, $email, $task, $completed);
} }
/** /**
* Удалить из базы данных * Удалить из базы данных
* *
* @param array $params * @param array $params
* *
* @return void * @return void
*/ */
public function delete(array $params): void public function delete(array $params): void
{ {
// Инициализация параметров // Инициализация параметров
$id = (int) $params['id']; $id = (int) $params['id'];
// ВНИМАНИЕ: Сессию можно подобрать брутфорсом (либо ещё как-нибудь узнать) // ВНИМАНИЕ: Сессию можно подобрать брутфорсом (либо ещё как-нибудь узнать)
// Я бы не стал так делать на нормальном проекте - писал привязку к IP, например // Я бы не стал так делать на нормальном проекте - писал привязку к IP, например
session_start(); session_start();
if ($_SESSION['admin'] == 1) { if ($_SESSION['admin'] == 1) {
// Если пользователь является админстратором // Если пользователь является админстратором
$this->model->delete($id); $this->model->delete($id);
} }
} }
/** /**
* Сгенерировать представление HTML-листа заданий * Сгенерировать представление HTML-листа заданий
* *
* @param array $params * @param array $params
* *
* @return string * @return string
*/ */
public function genList(array $params): string public function genList(array $params): string
{ {
// Инициализация заданий // Инициализация заданий
$tasks = $this->read($params); $tasks = $this->read($params);
// Инициализация админ-прав // Инициализация админ-прав
session_start(); session_start();
$admin = $_SESSION['admin']; $admin = $_SESSION['admin'];
// Генерация представления // Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'tasks' . DIRECTORY_SEPARATOR . 'list.html', ['tasks' => $tasks, 'admin' => $admin]); return $this->view->render(DIRECTORY_SEPARATOR . 'tasks' . DIRECTORY_SEPARATOR . 'list.html', ['tasks' => $tasks, 'admin' => $admin]);
} }
/** /**
* Подсчитать количество заданий * Подсчитать количество заданий
* *
* @return int * @return int
*/ */
public function count(): int public function count(): int
{ {
return $this->model->count(); return $this->__get('model')->count();
} }
} }

0
mirzaev/beejee/system/core.php Normal file → Executable file
View File

0
mirzaev/beejee/system/models/authModel.php Normal file → Executable file
View File

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace mirzaev\beejee\models;
use mirzaev\beejee\models\model;
use PDO;
/**
* Бебра
*
* @package mirzaev\beejee\models
* @author Arsen Mirzaev Tatyano-Muradovich <red@hood.su>
*/
final class mainModel extends model {}

0
mirzaev/beejee/system/models/model.php Normal file → Executable file
View File

0
mirzaev/beejee/system/models/tasksModel.php Normal file → Executable file
View File

0
mirzaev/beejee/system/public/Nginx_1.17_vhost.conf Normal file → Executable file
View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-grid.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-grid.min.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-reboot.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap-reboot.min.css vendored Normal file → Executable file
View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/bootstrap/bootstrap.min.css vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/css/main.css Normal file → Executable file
View File

0
mirzaev/beejee/system/public/img/avatar.webp Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

2
mirzaev/beejee/system/public/index.php Normal file → Executable file
View File

@ -25,4 +25,4 @@ router::create('deauth', 'auth', 'deauth', 'POST');
router::create('auth', 'auth', 'genSidebarPanel', 'GET'); router::create('auth', 'auth', 'genSidebarPanel', 'GET');
router::create('reg', 'auth', 'reg', 'POST'); router::create('reg', 'auth', 'reg', 'POST');
new Core('mysql:dbname=beejee;host=127.0.0.1', 'root', 'root'); new Core('mysql:dbname=task_manager;unix_socket=/run/mysqld/mysqld.sock', login: 'magomed', password: 'varyeniki_besplatno_1988');

0
mirzaev/beejee/system/public/js/auth.js Normal file → Executable file
View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.bundle.js vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.bundle.min.js vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.js vendored Normal file → Executable file
View File

View File

0
mirzaev/beejee/system/public/js/bootstrap/bootstrap.min.js vendored Normal file → Executable file
View File

View File

View File

0
mirzaev/beejee/system/public/js/tasks.js Normal file → Executable file
View File

0
mirzaev/beejee/system/router.php Normal file → Executable file
View File

0
mirzaev/beejee/system/views/auth/auth_sidebar.html Normal file → Executable file
View File

0
mirzaev/beejee/system/views/main/index.html Normal file → Executable file
View File

0
mirzaev/beejee/system/views/tasks/list.html Normal file → Executable file
View File

0
mirzaev/beejee/tests/.gitignore vendored Normal file → Executable file
View File

0
mirzaev/beejee/tests/MainCest.php Normal file → Executable file
View File

0
mirzaev/beejee/tests/_data/.gitkeep Normal file → Executable file
View File

0
mirzaev/beejee/tests/_output/.gitkeep Normal file → Executable file
View File

View File

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

0
mirzaev/beejee/tests/_output/failed Normal file → Executable file
View File

0
mirzaev/beejee/tests/_support/AcceptanceTester.php Normal file → Executable file
View File

0
mirzaev/beejee/tests/_support/Helper/Acceptance.php Normal file → Executable file
View File

View File