ребрендинг + строка пакостей
|
@ -1,16 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "mirzaev/site-virus",
|
"name": "mirzaev/site-ff",
|
||||||
"description": "Site with viruses and scary pictures",
|
"description": "our FUCKING.FORUM",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"virus",
|
"forum",
|
||||||
"download",
|
|
||||||
"Evil Alliance",
|
"Evil Alliance",
|
||||||
"imageboard",
|
"imageboard",
|
||||||
"site"
|
"site"
|
||||||
],
|
],
|
||||||
"type": "site",
|
"type": "site",
|
||||||
"homepage": "https://git.mirzaev.sexy/mirzaev/site-virus",
|
"homepage": "https://git.mirzaev.sexy/mirzaev/site-ff",
|
||||||
"license": "WTFPL",
|
"license": "WTFPL",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@ -21,31 +20,31 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"docs": "https://git.mirzaev.sexy/mirzaev/site-virus/manual",
|
"docs": "https://git.mirzaev.sexy/mirzaev/site-ff/manual",
|
||||||
"issues": "https://git.mirzaev.sexy/mirzaev/site-virus/issues"
|
"issues": "https://git.mirzaev.sexy/mirzaev/site-ff/issues"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "~8.1",
|
"php": "~8.2",
|
||||||
"ext-sodium": "~8.1",
|
"ext-sodium": "~8.2",
|
||||||
"mirzaev/minimal": "^2.0.x-dev",
|
|
||||||
"mirzaev/accounts": "~1.2.x-dev",
|
"mirzaev/accounts": "~1.2.x-dev",
|
||||||
"mirzaev/arangodb": "^1.0.0",
|
"mirzaev/arangodb": "^1.0.0",
|
||||||
"mirzaev/vk": "^4.0",
|
"mirzaev/vk": "^4.0",
|
||||||
"triagens/arangodb": "~3.9.x-dev",
|
"triagens/arangodb": "~3.9.x-dev",
|
||||||
"twig/twig": "^3.4",
|
"twig/twig": "^3.4",
|
||||||
"guzzlehttp/guzzle": "^7.5"
|
"guzzlehttp/guzzle": "^7.5",
|
||||||
|
"mirzaev/minimal": "^2.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "~9.5"
|
"phpunit/phpunit": "~9.5"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"mirzaev\\site\\virus\\": "mirzaev/site/virus/system"
|
"mirzaev\\site\\ff\\": "mirzaev/site/ff/system"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"mirzaev\\site\\virus\\tests\\": "mirzaev/site/virus/tests"
|
"mirzaev\\site\\ff\\tests\\": "mirzaev/site/ff/tests"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\controllers;
|
namespace mirzaev\site\ff\controllers;
|
||||||
|
|
||||||
// Файлы проекта
|
// Файлы проекта
|
||||||
use mirzaev\site\virus\controllers\core;
|
use mirzaev\site\ff\controllers\core;
|
||||||
use mirzaev\site\virus\models\account_model as account;
|
use mirzaev\site\ff\models\account_model as account;
|
||||||
use mirzaev\site\virus\models\session_model as session;
|
use mirzaev\site\ff\models\session_model as session;
|
||||||
use mirzaev\site\virus\models\vk_model as vk;
|
use mirzaev\site\ff\models\vk_model as vk;
|
||||||
|
|
||||||
// Библиотека для ArangoDB
|
// Библиотека для ArangoDB
|
||||||
use ArangoDBClient\Document as _document;
|
use ArangoDBClient\Document as _document;
|
||||||
|
@ -21,7 +21,7 @@ use mirzaev\vk\core as api;
|
||||||
/**
|
/**
|
||||||
* Контроллер аккаунтов
|
* Контроллер аккаунтов
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\controllers
|
* @package mirzaev\site\ff\controllers
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class account_controller extends core
|
final class account_controller extends core
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\controllers;
|
namespace mirzaev\site\ff\controllers;
|
||||||
|
|
||||||
// Файлы проекта
|
// Файлы проекта
|
||||||
use mirzaev\site\virus\views\manager;
|
use mirzaev\site\ff\views\manager;
|
||||||
use mirzaev\site\virus\models\core as models;
|
use mirzaev\site\ff\models\core as models;
|
||||||
use mirzaev\site\virus\models\account_model as account;
|
use mirzaev\site\ff\models\account_model as account;
|
||||||
use mirzaev\site\virus\models\session_model as session;
|
use mirzaev\site\ff\models\session_model as session;
|
||||||
|
|
||||||
// Библиотека для ArangoDB
|
// Библиотека для ArangoDB
|
||||||
use ArangoDBClient\Document as _document;
|
use ArangoDBClient\Document as _document;
|
||||||
|
@ -23,7 +23,7 @@ use mirzaev\vk\robots\user as robot;
|
||||||
/**
|
/**
|
||||||
* Ядро контроллеров
|
* Ядро контроллеров
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\controllers
|
* @package mirzaev\site\ff\controllers
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
class core extends controller
|
class core extends controller
|
||||||
|
@ -69,7 +69,7 @@ class core extends controller
|
||||||
// Запись хеша новой сессии
|
// Запись хеша новой сессии
|
||||||
setcookie('session', $this->variables['session']->hash, [
|
setcookie('session', $this->variables['session']->hash, [
|
||||||
'expires' => $expires,
|
'expires' => $expires,
|
||||||
'domain' => 'virus.mirzaev.sexy',
|
'domain' => 'ff.mirzaev.sexy',
|
||||||
'path' => '/',
|
'path' => '/',
|
||||||
'secure' => true,
|
'secure' => true,
|
||||||
'httponly' => true,
|
'httponly' => true,
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\controllers;
|
namespace mirzaev\site\ff\controllers;
|
||||||
|
|
||||||
// Файлы проекта
|
// Файлы проекта
|
||||||
use mirzaev\site\virus\controllers\core;
|
use mirzaev\site\ff\controllers\core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер ошибок
|
* Контроллер ошибок
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\controllers
|
* @package mirzaev\site\ff\controllers
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class error_controller extends core
|
final class error_controller extends core
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\controllers;
|
namespace mirzaev\site\ff\controllers;
|
||||||
|
|
||||||
// Файлы проекта
|
// Файлы проекта
|
||||||
use mirzaev\site\virus\controllers\core;
|
use mirzaev\site\ff\controllers\core;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Контроллер бегущей строки
|
* Контроллер бегущей строки
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\controllers
|
* @package mirzaev\site\ff\controllers
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class hotline_controller extends core
|
final class hotline_controller extends core
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\site\ff\controllers;
|
||||||
|
|
||||||
|
// Файлы проекта
|
||||||
|
use mirzaev\site\ff\controllers\core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Контроллер основной страницы
|
||||||
|
*
|
||||||
|
* @package mirzaev\site\ff\controllers
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class index_controller extends core
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Главная страница
|
||||||
|
*
|
||||||
|
* @param array $parameters Параметры запроса
|
||||||
|
*/
|
||||||
|
public function index(array $parameters = []): ?string
|
||||||
|
{
|
||||||
|
// Инициализация загружаемых категорий
|
||||||
|
$this->variables['include'] = [
|
||||||
|
'head' => ['self'],
|
||||||
|
'body' => ['self']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Инициализация бегущей строки
|
||||||
|
$this->variables['hotline'] = [
|
||||||
|
'id' => $this->variables['request']['id'] ?? 'hotline'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Инициализация параметров бегущей строки
|
||||||
|
$this->variables['hotline']['parameters'] = [
|
||||||
|
'step' => '0.3'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Инициализация аттрибутов бегущей строки
|
||||||
|
$this->variables['hotline']['attributes'] = [];
|
||||||
|
|
||||||
|
// Инициализация элементов бегущей строки
|
||||||
|
$this->variables['hotline']['elements'] = [
|
||||||
|
['html' => '1'],
|
||||||
|
[
|
||||||
|
'tag' => 'article',
|
||||||
|
'attributes' => [
|
||||||
|
'class' => 'trash'
|
||||||
|
],
|
||||||
|
'html' => $this->view->render(DIRECTORY_SEPARATOR . 'hotline' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'trash.html', [
|
||||||
|
'id' => 'trash_1',
|
||||||
|
'title' => 'Linoleum',
|
||||||
|
'main' => '<p>Do you really like the rotting smell, dull sound and disgusting greasy shine of parquet-like fake pattern on a polymer toxic film? <b>Are you fucking insane?</b></p>',
|
||||||
|
'image' => [
|
||||||
|
'src' => '/images/trash/linoleum.png',
|
||||||
|
'alt' => 'Linoleum'
|
||||||
|
]
|
||||||
|
])
|
||||||
|
],
|
||||||
|
['html' => '3'],
|
||||||
|
['html' => '4'],
|
||||||
|
['html' => '5'],
|
||||||
|
['html' => '6'],
|
||||||
|
['html' => '7'],
|
||||||
|
['html' => '8'],
|
||||||
|
['html' => '9'],
|
||||||
|
['html' => '10'],
|
||||||
|
['html' => '11'],
|
||||||
|
['html' => '12'],
|
||||||
|
['html' => '13'],
|
||||||
|
['html' => '14'],
|
||||||
|
['html' => '15']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Генерация представления
|
||||||
|
return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', $this->variables);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\models;
|
namespace mirzaev\site\ff\models;
|
||||||
|
|
||||||
// Файлы проекта
|
// Файлы проекта
|
||||||
use mirzaev\site\virus\models\vk_model as vk;
|
use mirzaev\site\ff\models\vk_model as vk;
|
||||||
|
|
||||||
// Фреймворк ArangoDB
|
// Фреймворк ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -20,7 +20,7 @@ use exception;
|
||||||
/**
|
/**
|
||||||
* Модель регистрации, аутентификации и авторизации
|
* Модель регистрации, аутентификации и авторизации
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\models
|
* @package mirzaev\site\ff\models
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class account_model extends core
|
final class account_model extends core
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\models;
|
namespace mirzaev\site\ff\models;
|
||||||
|
|
||||||
use mirzaev\minimal\model;
|
use mirzaev\minimal\model;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use exception;
|
||||||
/**
|
/**
|
||||||
* Ядро моделей
|
* Ядро моделей
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\models
|
* @package mirzaev\site\ff\models
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
class core extends model
|
class core extends model
|
|
@ -0,0 +1,216 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\site\ff\models;
|
||||||
|
|
||||||
|
// Файлы проекта
|
||||||
|
use mirzaev\site\ff\models\account_model as account;
|
||||||
|
|
||||||
|
// Фреймворк ArangoDB
|
||||||
|
use mirzaev\arangodb\collection,
|
||||||
|
mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
// Библиотека для ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Встроенные библиотеки
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Модель сессий
|
||||||
|
*
|
||||||
|
* @package mirzaev\site\ff\models
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class session_model extends core
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Коллекция
|
||||||
|
*/
|
||||||
|
public const COLLECTION = 'session';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация
|
||||||
|
*
|
||||||
|
* @param ?string $hash Хеш сессии в базе данных
|
||||||
|
* @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии)
|
||||||
|
* @param array &$errors Журнал ошибок
|
||||||
|
*
|
||||||
|
* @return ?_document Инстанция сессии, если удалось найти или создать
|
||||||
|
*/
|
||||||
|
public static function initialization(?string $hash = null, ?int $expires = null, array &$errors = []): ?_document
|
||||||
|
{
|
||||||
|
$a = new _document();
|
||||||
|
$a->hash = 'dolboeb227';
|
||||||
|
return $a;
|
||||||
|
try {
|
||||||
|
if (collection::init(static::$db->session, self::COLLECTION)) {
|
||||||
|
// Инициализирована коллекция
|
||||||
|
|
||||||
|
if (isset($hash) && $session = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR d IN %s
|
||||||
|
FILTER d.hash == '$hash' && d.expires > %d
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
self::COLLECTION,
|
||||||
|
time()
|
||||||
|
))) {
|
||||||
|
// Найдена сессия по хешу
|
||||||
|
|
||||||
|
// Возврат сессии
|
||||||
|
return $session;
|
||||||
|
} else if ($session = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR d IN %s
|
||||||
|
FILTER d.ip == '%s' && d.expires > %d
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
self::COLLECTION,
|
||||||
|
$_SERVER['REMOTE_ADDR'],
|
||||||
|
time()
|
||||||
|
))) {
|
||||||
|
// Найдена сессия по данным пользователя
|
||||||
|
|
||||||
|
// Возврат сессии
|
||||||
|
return $session;
|
||||||
|
} else {
|
||||||
|
// Не найдена сессия
|
||||||
|
|
||||||
|
// Запись сессии в базу данных
|
||||||
|
$_id = document::write(static::$db->session, self::COLLECTION, [
|
||||||
|
'ip' => $_SERVER['REMOTE_ADDR'],
|
||||||
|
'expires' => $expires ?? time() + 604800
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($session = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR d IN %s
|
||||||
|
FILTER d._id == '$_id' && d.expires > %d
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
self::COLLECTION,
|
||||||
|
time()
|
||||||
|
))) {
|
||||||
|
// Найдена созданная сессия
|
||||||
|
|
||||||
|
// Запись хеша
|
||||||
|
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
|
||||||
|
|
||||||
|
if (document::update(static::$db->session, $session)) {
|
||||||
|
// Записано обновление
|
||||||
|
|
||||||
|
return $session;
|
||||||
|
} else throw new exception('Не удалось записать данные сессии');
|
||||||
|
} else throw new exception('Не удалось создать или найти созданную сессию');
|
||||||
|
}
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в журнал ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Связь сессии с аккаунтом
|
||||||
|
*
|
||||||
|
* @param _document $session Инстанция сессии
|
||||||
|
* @param _document $account Инстанция аккаунта
|
||||||
|
* @param array &$errors Журнал ошибок
|
||||||
|
*
|
||||||
|
* @return bool Статус выполнения
|
||||||
|
*/
|
||||||
|
public static function connect(_document $session, _document $account, array &$errors = []): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
collection::init(static::$db->session, self::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, account::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
|
||||||
|
) {
|
||||||
|
// Инициализирована коллекция
|
||||||
|
|
||||||
|
if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [
|
||||||
|
'_from' => $session->getId(),
|
||||||
|
'_to' => $account->getId()
|
||||||
|
])) {
|
||||||
|
// Создано ребро: session -> account
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else throw new exception('Не удалось создать ребро: session -> account');
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в журнал ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск связанного аккаунта
|
||||||
|
*
|
||||||
|
* @param _document $session Инстанция сессии
|
||||||
|
* @param array &$errors Журнал ошибок
|
||||||
|
*
|
||||||
|
* @return ?_document Инстанция аккаунта, если удалось найти
|
||||||
|
*/
|
||||||
|
public static function account(_document $session, array &$errors = []): ?_document
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
collection::init(static::$db->session, self::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, account::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
|
||||||
|
) {
|
||||||
|
// Инициализированы коллекции
|
||||||
|
|
||||||
|
if ($account = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR document IN %s
|
||||||
|
LET edge = (
|
||||||
|
FOR edge IN %s
|
||||||
|
FILTER edge._from == '%s'
|
||||||
|
SORT edge._key DESC
|
||||||
|
LIMIT 1
|
||||||
|
RETURN edge
|
||||||
|
)
|
||||||
|
FILTER document._id == edge[0]._to
|
||||||
|
LIMIT 1
|
||||||
|
RETURN document
|
||||||
|
AQL,
|
||||||
|
account::COLLECTION,
|
||||||
|
self::COLLECTION . '_edge_' . account::COLLECTION,
|
||||||
|
$session->getId()
|
||||||
|
))) {
|
||||||
|
// Найден аккаунт
|
||||||
|
|
||||||
|
return $account;
|
||||||
|
} else throw new exception('Не удалось найти аккаунт');
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в журнал ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\models;
|
namespace mirzaev\site\ff\models;
|
||||||
|
|
||||||
// Файлы проекта
|
// Файлы проекта
|
||||||
use mirzaev\site\virus\models\account_model as account;
|
use mirzaev\site\ff\models\account_model as account;
|
||||||
|
|
||||||
// Фреймворк ArangoDB
|
// Фреймворк ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -27,7 +27,7 @@ use stdClass;
|
||||||
/**
|
/**
|
||||||
* Модель аккаунта ВКонтакте
|
* Модель аккаунта ВКонтакте
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\models
|
* @package mirzaev\site\ff\models
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class vk_model extends core
|
final class vk_model extends core
|
|
@ -0,0 +1,37 @@
|
||||||
|
section.hotline {
|
||||||
|
display: inline-flex;
|
||||||
|
height: 100%;
|
||||||
|
transition: unset;
|
||||||
|
/* gap нельзя */
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline * {
|
||||||
|
transition: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
section.hotline:last-child {
|
||||||
|
margin-bottom: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 18px;
|
||||||
|
width: calc(140px - var(--padding, 0px) * 2);
|
||||||
|
height: calc(190px - var(--padding, 0px) * 2);
|
||||||
|
padding: var(--padding, 0px);
|
||||||
|
display: flex;
|
||||||
|
align-self: flex-end;
|
||||||
|
overflow: clip;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--background-light-1);
|
||||||
|
box-shadow: 0px -6px 6px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline>article:last-child {
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article > * {
|
||||||
|
margin: auto;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
section.hotline > article.trash {
|
||||||
|
--padding: 12px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article.trash > h1 {
|
||||||
|
z-index: 10;
|
||||||
|
margin-top: 10px;
|
||||||
|
flex-grow: 4;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article.trash :is(p, b) {
|
||||||
|
z-index: 10;
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article.trash small {
|
||||||
|
z-index: 10;
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article.trash > img {
|
||||||
|
z-index: 5;
|
||||||
|
position: absolute;
|
||||||
|
left: -5%;
|
||||||
|
top: -5%;
|
||||||
|
width: 110%;
|
||||||
|
height: 110%;
|
||||||
|
object-position: center;
|
||||||
|
object-fit: cover;
|
||||||
|
filter: blur(2px) saturate(30%) brightness(40%);
|
||||||
|
transition: 0.2s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hotline > article.trash:is(:hover, :active) > img {
|
||||||
|
left: -20%;
|
||||||
|
top: -20%;
|
||||||
|
width: 140%;
|
||||||
|
height: 140%;
|
||||||
|
filter: blur(3px) saturate(0) brightness(60%) contrast(150%);
|
||||||
|
transition: 0.1s ease-out;
|
||||||
|
}
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 825 B |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 397 KiB After Width: | Height: | Size: 397 KiB |
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 295 KiB |
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus;
|
namespace mirzaev\site\ff;
|
||||||
|
|
||||||
use mirzaev\minimal\core;
|
use mirzaev\minimal\core;
|
||||||
use mirzaev\minimal\router;
|
use mirzaev\minimal\router;
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{# {{ block('hotline_css') }} #}
|
{# {{ block('hotline_css') }} #}
|
||||||
|
<link type="text/css" rel="stylesheet" href="/css/trash.css">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
|
@ -8,7 +8,7 @@
|
||||||
%} data-hotline-{{ name }}="{{value}}" {% endfor %} {% for name, value in hotline.attributes %} {{ name
|
%} data-hotline-{{ name }}="{{value}}" {% endfor %} {% for name, value in hotline.attributes %} {{ name
|
||||||
}}="{{value}}" {% endfor %}>
|
}}="{{value}}" {% endfor %}>
|
||||||
{% for element in hotline.elements %}
|
{% for element in hotline.elements %}
|
||||||
<{{element.tag??'article'}}>{{ element.content }}</{{element.tag??'article'}}>
|
<{{element.tag??'article'}} {% for attribute, value in element.attributes %}{{ attribute }}="{{ value }}"{% endfor %}>{{ element.html|raw }}</{{element.tag??'article'}}>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
{{ main|raw }}
|
||||||
|
{% if image is not null %}<img src="{{ image.src }}" alt="{{ image.alt }}">{% endif %}
|
||||||
|
<script>
|
||||||
|
// Initialization of the element
|
||||||
|
const element = document.getElementById('{{ id }}');
|
||||||
|
|
||||||
|
if (element instanceof HTMLElement) {
|
||||||
|
// Found the element
|
||||||
|
|
||||||
|
element.addEventListener('mouseenter', () => {
|
||||||
|
console.log('{{ id }}');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -19,7 +19,7 @@
|
||||||
{{ block('header_body') }}
|
{{ block('header_body') }}
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<noscript>К сожалению мой сайт ещё пока не готов для работы без javascript</noscript>
|
<noscript>Весь код на JavaScript выполняется из соглашения по твоему выбору через popup-окно (cookies), смело включай</noscript>
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</main>
|
</main>
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\site\virus\views;
|
namespace mirzaev\site\ff\views;
|
||||||
|
|
||||||
use mirzaev\minimal\controller;
|
use mirzaev\minimal\controller;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use Twig\Environment as view;
|
||||||
/**
|
/**
|
||||||
* Менеджер представлений
|
* Менеджер представлений
|
||||||
*
|
*
|
||||||
* @package mirzaev\site\virus\controllers
|
* @package mirzaev\site\ff\controllers
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
final class manager extends controller
|
final class manager extends controller
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace mirzaev\site\virus\controllers;
|
|
||||||
|
|
||||||
// Файлы проекта
|
|
||||||
use mirzaev\site\virus\controllers\core;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Контроллер основной страницы
|
|
||||||
*
|
|
||||||
* @package mirzaev\site\virus\controllers
|
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
|
||||||
*/
|
|
||||||
final class index_controller extends core
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Главная страница
|
|
||||||
*
|
|
||||||
* @param array $parameters Параметры запроса
|
|
||||||
*/
|
|
||||||
public function index(array $parameters = []): ?string
|
|
||||||
{
|
|
||||||
// Инициализация загружаемых категорий
|
|
||||||
$this->variables['include'] = [
|
|
||||||
'head' => ['self'],
|
|
||||||
'body' => ['self']
|
|
||||||
];
|
|
||||||
|
|
||||||
// Инициализация бегущей строки
|
|
||||||
$this->variables['hotline'] = [
|
|
||||||
'id' => $this->variables['request']['id'] ?? 'hotline'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Инициализация параметров бегущей строки
|
|
||||||
$this->variables['hotline']['parameters'] = [
|
|
||||||
// 'step' => 2
|
|
||||||
];
|
|
||||||
|
|
||||||
// Инициализация аттрибутов бегущей строки
|
|
||||||
$this->variables['hotline']['attributes'] = [
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
// Инициализация элементов бегущей строки
|
|
||||||
$this->variables['hotline']['elements'] = [
|
|
||||||
['content' => '1'],
|
|
||||||
[
|
|
||||||
'tag' => 'article',
|
|
||||||
'content' => '2'
|
|
||||||
],
|
|
||||||
['content' => '3'],
|
|
||||||
['content' => '4'],
|
|
||||||
['content' => '5'],
|
|
||||||
['content' => '6'],
|
|
||||||
['content' => '7'],
|
|
||||||
['content' => '8'],
|
|
||||||
['content' => '9'],
|
|
||||||
['content' => '10'],
|
|
||||||
['content' => '11'],
|
|
||||||
['content' => '12'],
|
|
||||||
['content' => '13'],
|
|
||||||
['content' => '14'],
|
|
||||||
['content' => '15']
|
|
||||||
];
|
|
||||||
|
|
||||||
// Генерация представления
|
|
||||||
return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', $this->variables);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace mirzaev\site\virus\models;
|
|
||||||
|
|
||||||
// Файлы проекта
|
|
||||||
use mirzaev\site\virus\models\account_model as account;
|
|
||||||
|
|
||||||
// Фреймворк ArangoDB
|
|
||||||
use mirzaev\arangodb\collection,
|
|
||||||
mirzaev\arangodb\document;
|
|
||||||
|
|
||||||
// Библиотека для ArangoDB
|
|
||||||
use ArangoDBClient\Document as _document;
|
|
||||||
|
|
||||||
// Встроенные библиотеки
|
|
||||||
use exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Модель сессий
|
|
||||||
*
|
|
||||||
* @package mirzaev\site\virus\models
|
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
|
||||||
*/
|
|
||||||
final class session_model extends core
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Коллекция
|
|
||||||
*/
|
|
||||||
public const COLLECTION = 'session';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Инициализация
|
|
||||||
*
|
|
||||||
* @param ?string $hash Хеш сессии в базе данных
|
|
||||||
* @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии)
|
|
||||||
* @param array &$errors Журнал ошибок
|
|
||||||
*
|
|
||||||
* @return ?_document Инстанция сессии, если удалось найти или создать
|
|
||||||
*/
|
|
||||||
public static function initialization(?string $hash = null, ?int $expires = null, array &$errors = []): ?_document
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (collection::init(static::$db->session, self::COLLECTION)) {
|
|
||||||
// Инициализирована коллекция
|
|
||||||
|
|
||||||
if (isset($hash) && $session = collection::search(static::$db->session, sprintf(
|
|
||||||
<<<AQL
|
|
||||||
FOR d IN %s
|
|
||||||
FILTER d.hash == '$hash' && d.expires > %d
|
|
||||||
RETURN d
|
|
||||||
AQL,
|
|
||||||
self::COLLECTION,
|
|
||||||
time()
|
|
||||||
))) {
|
|
||||||
// Найдена сессия по хешу
|
|
||||||
|
|
||||||
// Возврат сессии
|
|
||||||
return $session;
|
|
||||||
} else if ($session = collection::search(static::$db->session, sprintf(
|
|
||||||
<<<AQL
|
|
||||||
FOR d IN %s
|
|
||||||
FILTER d.ip == '%s' && d.expires > %d
|
|
||||||
RETURN d
|
|
||||||
AQL,
|
|
||||||
self::COLLECTION,
|
|
||||||
$_SERVER['REMOTE_ADDR'],
|
|
||||||
time()
|
|
||||||
))) {
|
|
||||||
// Найдена сессия по данным пользователя
|
|
||||||
|
|
||||||
// Возврат сессии
|
|
||||||
return $session;
|
|
||||||
} else {
|
|
||||||
// Не найдена сессия
|
|
||||||
|
|
||||||
// Запись сессии в базу данных
|
|
||||||
$_id = document::write(static::$db->session, self::COLLECTION, [
|
|
||||||
'ip' => $_SERVER['REMOTE_ADDR'],
|
|
||||||
'expires' => $expires ?? time() + 604800
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($session = collection::search(static::$db->session, sprintf(
|
|
||||||
<<<AQL
|
|
||||||
FOR d IN %s
|
|
||||||
FILTER d._id == '$_id' && d.expires > %d
|
|
||||||
RETURN d
|
|
||||||
AQL,
|
|
||||||
self::COLLECTION,
|
|
||||||
time()
|
|
||||||
))) {
|
|
||||||
// Найдена созданная сессия
|
|
||||||
|
|
||||||
// Запись хеша
|
|
||||||
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
|
|
||||||
|
|
||||||
if (document::update(static::$db->session, $session)) {
|
|
||||||
// Записано обновление
|
|
||||||
|
|
||||||
return $session;
|
|
||||||
} else throw new exception('Не удалось записать данные сессии');
|
|
||||||
} else throw new exception('Не удалось создать или найти созданную сессию');
|
|
||||||
}
|
|
||||||
} else throw new exception('Не удалось инициализировать коллекцию');
|
|
||||||
} catch (exception $e) {
|
|
||||||
// Запись в журнал ошибок
|
|
||||||
$errors[] = [
|
|
||||||
'text' => $e->getMessage(),
|
|
||||||
'file' => $e->getFile(),
|
|
||||||
'line' => $e->getLine(),
|
|
||||||
'stack' => $e->getTrace()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Связь сессии с аккаунтом
|
|
||||||
*
|
|
||||||
* @param _document $session Инстанция сессии
|
|
||||||
* @param _document $account Инстанция аккаунта
|
|
||||||
* @param array &$errors Журнал ошибок
|
|
||||||
*
|
|
||||||
* @return bool Статус выполнения
|
|
||||||
*/
|
|
||||||
public static function connect(_document $session, _document $account, array &$errors = []): bool
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
collection::init(static::$db->session, self::COLLECTION)
|
|
||||||
&& collection::init(static::$db->session, account::COLLECTION)
|
|
||||||
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
|
|
||||||
) {
|
|
||||||
// Инициализирована коллекция
|
|
||||||
|
|
||||||
if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [
|
|
||||||
'_from' => $session->getId(),
|
|
||||||
'_to' => $account->getId()
|
|
||||||
])) {
|
|
||||||
// Создано ребро: session -> account
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else throw new exception('Не удалось создать ребро: session -> account');
|
|
||||||
} else throw new exception('Не удалось инициализировать коллекцию');
|
|
||||||
} catch (exception $e) {
|
|
||||||
// Запись в журнал ошибок
|
|
||||||
$errors[] = [
|
|
||||||
'text' => $e->getMessage(),
|
|
||||||
'file' => $e->getFile(),
|
|
||||||
'line' => $e->getLine(),
|
|
||||||
'stack' => $e->getTrace()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Поиск связанного аккаунта
|
|
||||||
*
|
|
||||||
* @param _document $session Инстанция сессии
|
|
||||||
* @param array &$errors Журнал ошибок
|
|
||||||
*
|
|
||||||
* @return ?_document Инстанция аккаунта, если удалось найти
|
|
||||||
*/
|
|
||||||
public static function account(_document $session, array &$errors = []): ?_document
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
collection::init(static::$db->session, self::COLLECTION)
|
|
||||||
&& collection::init(static::$db->session, account::COLLECTION)
|
|
||||||
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
|
|
||||||
) {
|
|
||||||
// Инициализированы коллекции
|
|
||||||
|
|
||||||
if ($account = collection::search(static::$db->session, sprintf(
|
|
||||||
<<<AQL
|
|
||||||
FOR document IN %s
|
|
||||||
LET edge = (
|
|
||||||
FOR edge IN %s
|
|
||||||
FILTER edge._from == '%s'
|
|
||||||
SORT edge._key DESC
|
|
||||||
LIMIT 1
|
|
||||||
RETURN edge
|
|
||||||
)
|
|
||||||
FILTER document._id == edge[0]._to
|
|
||||||
LIMIT 1
|
|
||||||
RETURN document
|
|
||||||
AQL,
|
|
||||||
account::COLLECTION,
|
|
||||||
self::COLLECTION . '_edge_' . account::COLLECTION,
|
|
||||||
$session->getId()
|
|
||||||
))) {
|
|
||||||
// Найден аккаунт
|
|
||||||
|
|
||||||
return $account;
|
|
||||||
} else throw new exception('Не удалось найти аккаунт');
|
|
||||||
} else throw new exception('Не удалось инициализировать коллекцию');
|
|
||||||
} catch (exception $e) {
|
|
||||||
// Запись в журнал ошибок
|
|
||||||
$errors[] = [
|
|
||||||
'text' => $e->getMessage(),
|
|
||||||
'file' => $e->getFile(),
|
|
||||||
'line' => $e->getLine(),
|
|
||||||
'stack' => $e->getTrace()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
section.hotline {
|
|
||||||
display: inline-flex;
|
|
||||||
height : 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.hotline * {
|
|
||||||
transition: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.hotline:last-child {
|
|
||||||
margin-bottom: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.hotline>article {
|
|
||||||
margin-right : 18px;
|
|
||||||
width : 140px;
|
|
||||||
height : 190px;
|
|
||||||
display : flex;
|
|
||||||
align-self : flex-end;
|
|
||||||
border-radius : 3px;
|
|
||||||
background-color: var(--background-light-1);
|
|
||||||
box-shadow : 0px -6px 6px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
section.hotline>article:last-child {
|
|
||||||
margin-right: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.hotline>article>* {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|