pizdets optimized
|
@ -1,4 +1,2 @@
|
|||
!.gitignore
|
||||
composer.phar
|
||||
composer.lock
|
||||
vendor
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
{
|
||||
"name": "mirzaev/site-repression",
|
||||
"description": "",
|
||||
"description": "An article about how i was kidnapped by PMC Wagner operatives",
|
||||
"readme": "README.md",
|
||||
"keywords": [
|
||||
"repression",
|
||||
"site"
|
||||
"site",
|
||||
"article"
|
||||
],
|
||||
"type": "site",
|
||||
"homepage": "https://git.mirzaev.sexy/mirzaev/site-repression",
|
||||
|
@ -14,7 +15,7 @@
|
|||
"name": "Arsen Mirzaev Tatyano-Muradovich",
|
||||
"email": "arsen@mirzaev.sexy",
|
||||
"homepage": "https://mirzaev.sexy",
|
||||
"role": "Programmer"
|
||||
"role": "Victim"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
|
@ -22,28 +23,16 @@
|
|||
"issues": "https://git.mirzaev.sexy/mirzaev/site-repression/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": "~8.3",
|
||||
"ext-sodium": "~8.3",
|
||||
"mirzaev/minimal": "^2.2.0",
|
||||
"mirzaev/accounts": "~1.2.x-dev",
|
||||
"mirzaev/arangodb": "^1.0.0",
|
||||
"mirzaev/vk": "^4.0",
|
||||
"triagens/arangodb": "~3.9.x-dev",
|
||||
"php": "~8.4",
|
||||
"ext-sodium": "~8.4",
|
||||
"mirzaev/minimal": "^3.2.0",
|
||||
"twig/twig": "^3.4",
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"ipinfo/ipinfo": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~9.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"mirzaev\\site\\repression\\": "mirzaev/site/repression/system"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"mirzaev\\site\\repression\\tests\\": "mirzaev/site/repression/tests"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name project;
|
||||
|
||||
# 301 302
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen 443 quic;
|
||||
listen [::]:443 ssl;
|
||||
listen [::]:443 quic;
|
||||
|
||||
server_name project;
|
||||
|
||||
http2 on;
|
||||
http3 on;
|
||||
quic_gso on;
|
||||
quic_retry on;
|
||||
|
||||
add_header Alt-Svc 'h3=":$server_port"; ma=86400';
|
||||
add_header x-quic 'h3';
|
||||
|
||||
root /var/www/project/mirzaev/site/repression/system/public;
|
||||
|
||||
index index.php;
|
||||
|
||||
keepalive_timeout 60;
|
||||
|
||||
include snippets/ssl-params.conf;
|
||||
include snippets/ssl-mirzaev.conf;
|
||||
include snippets/php8_4.conf;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|mp3|ogg|ogv|webm|htc|woff2|woff)$ {
|
||||
expires 1M;
|
||||
access_log off;
|
||||
add_header Cache-Control "max-age=2629746, public";
|
||||
}
|
||||
|
||||
location ~* \.(?:css|js)$ {
|
||||
expires 1y;
|
||||
access_log off;
|
||||
add_header Cache-Control "max-age=31556952, public";
|
||||
}
|
||||
}
|
||||
|
|
@ -4,57 +4,78 @@ declare(strict_types=1);
|
|||
|
||||
namespace mirzaev\site\repression\controllers;
|
||||
|
||||
// Файлы проекта
|
||||
use mirzaev\site\repression\views\manager;
|
||||
use mirzaev\site\repression\models\core as models;
|
||||
use mirzaev\site\repression\models\account_model as account;
|
||||
use mirzaev\site\repression\models\session_model as session;
|
||||
// Filtes of the project
|
||||
use mirzaev\site\repression\models\core as models,
|
||||
mirzaev\site\repression\views\templater,
|
||||
mirzaev\site\repression\models\enumerations\language;
|
||||
|
||||
// Библиотека для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Фреймворк PHP
|
||||
use mirzaev\minimal\controller;
|
||||
|
||||
// Фреймворк ВКонтакте
|
||||
use mirzaev\vk\core as vk;
|
||||
use mirzaev\vk\robots\user as robot;
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\core as minimal,
|
||||
mirzaev\minimal\http\response,
|
||||
mirzaev\minimal\controller;
|
||||
|
||||
/**
|
||||
* Ядро контроллеров
|
||||
* Core of controllers
|
||||
*
|
||||
* @package mirzaev\site\repression\controllers
|
||||
*
|
||||
* @param language $language Language
|
||||
* @param response $response Response
|
||||
* @param array $errors Registry of errors
|
||||
*
|
||||
* @method void __construct(minimal $core) Constructor
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
class core extends controller
|
||||
{
|
||||
/**
|
||||
* Переменные окружения
|
||||
*/
|
||||
protected robot $vk;
|
||||
/**
|
||||
* Language
|
||||
*
|
||||
* @var language $language Language
|
||||
*/
|
||||
protected language $language = language::en;
|
||||
|
||||
/**
|
||||
* Response
|
||||
*
|
||||
* @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks)
|
||||
*
|
||||
* @var response $response Response
|
||||
*/
|
||||
protected response $response {
|
||||
// Read
|
||||
get => $this->response ??= $this->request->response();
|
||||
}
|
||||
|
||||
/**
|
||||
* Errors
|
||||
*
|
||||
* @var array $errors Registry of errors
|
||||
*/
|
||||
protected array $errors = [
|
||||
'session' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Переменные окружения
|
||||
* Constructor
|
||||
*
|
||||
* @param minimal $core Initialize a controller?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected array $variables = [];
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
public function __construct(minimal $core) {
|
||||
// For the extends system
|
||||
parent::__construct(core: $core);
|
||||
|
||||
// Инициализация ядра моделей (соединение с базой данных...)
|
||||
new models();
|
||||
new models();
|
||||
|
||||
// Инициализация журнала ошибок
|
||||
$this->variables['errors'] = [
|
||||
'vk' => []
|
||||
];
|
||||
// Initializing of the language
|
||||
$this->language = language::{$_COOKIE["language"] ?? 'en'} ?? language::en;
|
||||
|
||||
// Инициализация препроцессора представления
|
||||
$this->view = new manager;
|
||||
// Initializing of preprocessor of views
|
||||
$this->view = new templater;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\controllers;
|
||||
|
||||
// Файлы проекта
|
||||
use mirzaev\site\repression\controllers\core;
|
||||
|
||||
/**
|
||||
* Контроллер ошибок
|
||||
*
|
||||
* @package mirzaev\site\repression\controllers
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class error_controller extends core
|
||||
{
|
||||
/**
|
||||
* Страница с ошибкой
|
||||
*
|
||||
* @param array $parameters
|
||||
*/
|
||||
public function index(array $parameters = []): ?string
|
||||
{
|
||||
// Запись текста ошибки в переменную окружения
|
||||
$this->variables['text'] = $parameters['text'] ?? null;
|
||||
|
||||
if (isset($parameters['code'])) {
|
||||
// Получен код ошибки
|
||||
|
||||
// Запись кода ошибки в переменную окружения
|
||||
$this->variables['code'] = $parameters['code'];
|
||||
|
||||
// Запись кода ответа
|
||||
http_response_code($parameters['code']);
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . 'index.html', $this->variables);
|
||||
}
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . ($parameters['code'] ?? 'index') . '.html', $this->variables);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\controllers;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\site\repression\controllers\core,
|
||||
mirzaev\site\repression\models\views;
|
||||
|
||||
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\http\enumerations\content;
|
||||
|
||||
// Framework for API of ipinfo.io
|
||||
use ipinfo\ipinfo\IPinfo as ipinfo;
|
||||
|
||||
// Built-in libraries
|
||||
use Exception as exception;
|
||||
|
||||
/**
|
||||
* Index controller
|
||||
*
|
||||
* @package mirzaev\site\repression\controllers
|
||||
*
|
||||
* @method null index() Main page
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class index extends core
|
||||
{
|
||||
/**
|
||||
* Main page
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function index(...$parameters): null
|
||||
{
|
||||
if (str_contains($this->request->headers['accept'], content::any->value)) {
|
||||
// Request for any response
|
||||
|
||||
// Initializing joker
|
||||
$this->view->joker = [
|
||||
/* 'instasamka' => rand(1, 11), */
|
||||
'southern' => rand(1, 3),
|
||||
'vk' => (bool) rand(0, 1),
|
||||
'whatsapp' => (bool) rand(0, 1),
|
||||
'iphone' => (bool) rand(0, 1),
|
||||
];
|
||||
|
||||
try {
|
||||
// Initializing data for the creepy line that will appear on the screen when user click "I did not understand" in the site rules window
|
||||
$data = (array) (new ipinfo(require '../settings/ipinfo.php'))->getDetails($_SERVER['cf-connecting-ip'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
// Initializing the creepy line that will appear on the screen when user click "I did not understand" in the site rules window
|
||||
$this->view->creepy = sprintf(
|
||||
"%s, %s, %s, %s, %s, %s, %s, seen in extremist chats, seen in LGBTQ+ chats, anti-state activities, problems with studies, was in contact with a criminal group (2017), wears strange pants, suspect in the case of January 15, 2024 No. 2360501",
|
||||
$data['continent']['name'],
|
||||
$data['country_name'],
|
||||
$data['city'],
|
||||
$data['latitude'],
|
||||
$data['longitude'],
|
||||
$data['org'],
|
||||
$data['ip']
|
||||
);
|
||||
} catch (exception $e) {
|
||||
// Initializing data for the creepy line that will appear on the screen when user click "I did not understand" in the site rules window
|
||||
$data = [];
|
||||
|
||||
// Initializing the creepy line that will appear on the screen when user click "I did not understand" in the site rules window
|
||||
$this->view->creepy = 'check your DM';
|
||||
}
|
||||
|
||||
// Validating values
|
||||
$address = filter_var($this->request->headers['remote-addr'] ?? '', FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE);
|
||||
$connecting = filter_var($this->request->headers['cf-connecting-ip'] ?? '', FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE);
|
||||
$forwarded = filter_var($this->request->headers['x-forwarded-for'] ?? $this->request->headers['http-x-forwarded-for'] ?? '', FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE);
|
||||
|
||||
// Initializing last 10 unique views less than 5 minutes ago
|
||||
$last = views::last(rows: 10, address: true, time: 300);
|
||||
|
||||
if (
|
||||
($forwarded && array_search($forwarded, array_column($last, 'forwarded'), true) !== false)
|
||||
or ($address && array_search($address, array_column($last, 'address'), true) !== false)
|
||||
or ($connecting && array_search($connecting, array_column($last, 'connecting'), true) !== false)
|
||||
) {
|
||||
// Found a dublicate
|
||||
} else {
|
||||
// Not found a dublicate
|
||||
|
||||
// Validating other values
|
||||
$useragent = $this->request->headers['user-agent'] ?? $this->request->headers['http-user-agent'] ?? null;
|
||||
$referer = $this->request->headers['referer'] ?? $this->request->headers['http-referer'] ?? null;
|
||||
$continent = $data['continent']['code'];
|
||||
$country = $data['country'];
|
||||
$country_name = $data['country_name'];
|
||||
$region = $data['region'];
|
||||
$city = $data['city'];
|
||||
$latitude = $data['latitude'];
|
||||
$longitude = $data['longitude'];
|
||||
$organisation = $data['org'];
|
||||
$flag = $data['country_flag']['emoji'];
|
||||
$currency = $data['country_currency']['code'];
|
||||
$timezone = $data['timezone'];
|
||||
|
||||
// Writing a view
|
||||
views::write(
|
||||
address: empty($address) ? null : "\"$address\"",
|
||||
connecting: empty($connecting) ? null : "\"$connecting\"",
|
||||
forwarded: empty($forwarded) ? null : "\"$forwarded\"",
|
||||
useragent: !empty($useragent) && mb_strlen($useragent) < 512 ? "\"$useragent\"" : null,
|
||||
referer: !empty($referer) && mb_strlen($referer) < 256 ? "\"$referer\"" : null,
|
||||
continent: !empty($continent) && mb_strlen($continent) === 2 ? "\"$continent\"" : null,
|
||||
country: !empty($country) && mb_strlen($country) < 64 ? "\"$country\"" : null,
|
||||
country_name: !empty($country_name) && mb_strlen($country_name) < 32 ? "\"$country_name\"" : null,
|
||||
region: !empty($region) && mb_strlen($region) < 32 ? "\"$region\"" : null,
|
||||
city: !empty($city) && mb_strlen($city) < 32 ? "\"$city\"" : null,
|
||||
latitute: filter_var((string) $latitude, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_NULL_ON_FAILURE),
|
||||
longitude: filter_var((string) $longitude, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_NULL_ON_FAILURE),
|
||||
organisation: !empty($organisation) && mb_strlen($organisation) < 128 ? "\"$organisation\"" : null,
|
||||
flag: !empty($flag) && mb_strlen($flag) === 2 ? "\"$flag\"" : null,
|
||||
currency: !empty($currency) && mb_strlen($currency) <= 3 ? "\"$currency\"" : null,
|
||||
timezone: !empty($timezone) && mb_strlen($timezone) < 64 ? "\"$timezone\"" : null
|
||||
);
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset(
|
||||
$last,
|
||||
$useragent,
|
||||
$referer,
|
||||
$continent,
|
||||
$country,
|
||||
$country_name,
|
||||
$region,
|
||||
$city,
|
||||
$latitude,
|
||||
$longitude,
|
||||
$organisation,
|
||||
$flag,
|
||||
$currency,
|
||||
$timezone
|
||||
);
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($address, $connecting, $forwarded);
|
||||
|
||||
// Initializing the counter of views
|
||||
$this->view->views = views::statistics(rows: 1000000) + ['last' => views::last(rows: 10, address: true)];
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($last);
|
||||
|
||||
// Sending response
|
||||
$this->response
|
||||
->start()
|
||||
->clean()
|
||||
->sse()
|
||||
->write($this->view->render(DIRECTORY_SEPARATOR . 'index.html'))
|
||||
->validate($this->request)
|
||||
?->body()
|
||||
->end();
|
||||
}
|
||||
|
||||
// Exit (success/fail)
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\controllers;
|
||||
|
||||
// Файлы проекта
|
||||
use mirzaev\site\repression\controllers\core,
|
||||
mirzaev\site\repression\models\views;
|
||||
|
||||
// Фреймворк для сайта ipinfo.io
|
||||
use ipinfo\ipinfo\IPinfo;
|
||||
|
||||
// Встроенные библиотеки
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Контроллер основной страницы
|
||||
*
|
||||
* @package mirzaev\site\repression\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['troller'] = [
|
||||
'instasamka' => rand(1, 11),
|
||||
'southern' => rand(1, 3),
|
||||
'vk' => (bool) rand(0, 1),
|
||||
'whatsapp' => (bool) rand(0, 1),
|
||||
'iphone' => (bool) rand(0, 1),
|
||||
];
|
||||
|
||||
try {
|
||||
// Запрос дополнительных данных
|
||||
$data = (array) (new IPinfo(require '../settings/ipinfo.php'))->getDetails($_SERVER['cf-connecting-ip'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
// Генерация строки для запугивания пользователя
|
||||
$this->variables['creepy'] = "{$data['continent']['name']}, {$data['country_name']}, {$data['city']}, {$data['latitude']}, {$data['longitude']}, {$data['org']}, {$data['ip']}, seen in extremist chats, seen in LGBTQ+ chats, anti-state activities, problems with studies, was in contact with a criminal group (2017), wears strange pants, suspect in the case of January 15, 2024 No. 2360501";
|
||||
} catch (exception $e) {
|
||||
// Инициализация заглушки дополнительных данных
|
||||
$data = [];
|
||||
|
||||
// Инициализация заглушки строки для запугивания пользователя
|
||||
$this->variables['creepy'] = 'check your DM';
|
||||
}
|
||||
|
||||
// Запись просмотра
|
||||
views::increase($data);
|
||||
|
||||
// Инициализация счётчика просмотров
|
||||
$this->variables['views'] = [
|
||||
'day' => views::day(),
|
||||
'week' => views::week(),
|
||||
'month' => views::month(),
|
||||
'all' => views::all(),
|
||||
'last' => views::last(10)
|
||||
];
|
||||
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', $this->variables);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
,,,"Mozilla/5.0 (compatible; Google-InspectionTool/1.0;)",,"NA","US","United States","Oklahoma","Oklahoma City",354676,-975164,"AS15169 Google LLC","🇺🇸","USD","America/Chicago",1732246927
|
||||
,"194.164.180.23","194.164.180.23","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Safari/537.36",,"EU","DE","Germany","Hesse","Frankfurt am Main",501038,86522,"AS215174 ProNow Tech CO. L.L.C","🇩🇪","EUR","Europe/Berlin",1732248193
|
||||
,"66.249.81.162","66.249.81.162","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Safari/537.36 Chrome-Lighthouse",,"EU","NL","Netherlands","Groningen","Delfzijl",533300,69181,"AS15169 Google LLC","🇳🇱","EUR","Europe/Amsterdam",1732248832
|
||||
,"66.249.81.167","66.249.81.167","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Safari/537.36 Chrome-Lighthouse",,"EU","NL","Netherlands","Groningen","Delfzijl",533300,69181,"AS15169 Google LLC","🇳🇱","EUR","Europe/Amsterdam",1732248839
|
||||
,"66.102.9.76","66.102.9.76","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"EU","FI","Finland","Kymenlaakso","Hamina",605697,271979,"AS15169 Google LLC","🇫🇮","EUR","Europe/Helsinki",1732248851
|
||||
,"66.102.9.75","66.102.9.75","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"EU","FI","Finland","Kymenlaakso","Hamina",605697,271979,"AS15169 Google LLC","🇫🇮","EUR","Europe/Helsinki",1732248859
|
||||
,"74.125.212.107","74.125.212.107","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"NA","US","United States","Iowa","Council Bluffs",412619,-958608,"AS15169 Google LLC","🇺🇸","USD","America/Chicago",1732248860
|
||||
,"74.125.212.108","74.125.212.108","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"NA","US","United States","Iowa","Council Bluffs",412619,-958608,"AS15169 Google LLC","🇺🇸","USD","America/Chicago",1732248869
|
||||
,"74.125.212.109","74.125.212.109","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"NA","US","United States","Iowa","Council Bluffs",412619,-958608,"AS15169 Google LLC","🇺🇸","USD","America/Chicago",1732248883
|
||||
,"66.249.81.168","66.249.81.168","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"EU","NL","Netherlands","Groningen","Delfzijl",533300,69181,"AS15169 Google LLC","🇳🇱","EUR","Europe/Amsterdam",1732248889
|
||||
,"74.125.212.106","74.125.212.106","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Safari/537.36 Chrome-Lighthouse",,"NA","US","United States","Iowa","Council Bluffs",412619,-958608,"AS15169 Google LLC","🇺🇸","USD","America/Chicago",1732248892
|
||||
,"66.249.81.166","66.249.81.166","Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse",,"EU","NL","Netherlands","Groningen","Delfzijl",533300,69181,"AS15169 Google LLC","🇳🇱","EUR","Europe/Amsterdam",1732248895
|
||||
,"195.234.62.12","195.234.62.12","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Safari/537.36",,"EU","DE","Germany","Hesse","Frankfurt am Main",500837,86440,"AS202422 G-Core Labs S.A.","🇩🇪","EUR","Europe/Berlin",1732249663
|
||||
,"194.164.180.23","194.164.180.23","Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",,"EU","DE","Germany","Hesse","Frankfurt am Main",501038,86522,"AS215174 ProNow Tech CO. L.L.C","🇩🇪","EUR","Europe/Berlin",1732249896
|
||||
,"194.164.180.15","194.164.180.15","Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",,"EU","DE","Germany","Hesse","Frankfurt am Main",501038,86522,"AS215174 ProNow Tech CO. L.L.C","🇩🇪","EUR","Europe/Berlin",1732250185
|
||||
,"194.164.180.15","194.164.180.15","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Safari/537.36",,"EU","DE","Germany","Hesse","Frankfurt am Main",501038,86522,"AS215174 ProNow Tech CO. L.L.C","🇩🇪","EUR","Europe/Berlin",1732250492
|
||||
,"185.253.23.85","185.253.23.85","Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",,"EU","RU","Russia","Moscow","Moscow",557522,376156,"AS21030 Docker LTD","🇷🇺","RUB","Europe/Moscow",1732250743
|
||||
,"185.253.23.85","185.253.23.85","Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",,"EU","RU","Russia","Moscow","Moscow",557522,376156,"AS21030 Docker LTD","🇷🇺","RUB","Europe/Moscow",1732251093
|
||||
,"109.248.213.45","109.248.213.45","Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML\, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",,"AS","KZ","Kazakhstan","Karaganda","Karagandy",498019,731021,"AS203087 PE Fedinyak Sergey Vyacheslavovich","🇰🇿","KZT","Asia/Almaty",1732251404
|
|
|
@ -4,151 +4,28 @@ declare(strict_types=1);
|
|||
|
||||
namespace mirzaev\site\repression\models;
|
||||
|
||||
// Фреймворк PHP
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\model;
|
||||
|
||||
// Фреймворк ArangoDB
|
||||
use mirzaev\arangodb\connection as arangodb;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Ядро моделей
|
||||
* Core of models
|
||||
*
|
||||
* @package mirzaev\site\repression\models
|
||||
*
|
||||
* @method void __construct() Constructor
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
class core extends model
|
||||
{
|
||||
/**
|
||||
* Соединение с базой данных ArangoDB
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static arangodb $arangodb;
|
||||
|
||||
/**
|
||||
* Путь до файла с настройками подключения к базе данных ArangoDB
|
||||
*/
|
||||
final public const ARANGODB = '../settings/arangodb.php';
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param bool $initialize Инициализировать контроллер?
|
||||
* @param ?arangodb $arangodb Инстанция соединения с базой данных ArangoDB
|
||||
*/
|
||||
public function __construct(bool $initialize = true, ?arangodb $arangodb = null)
|
||||
{
|
||||
if ($initialize) {
|
||||
// Запрошена инициализация
|
||||
|
||||
if (isset($arangodb)) {
|
||||
// Получена инстанция соединения с базой данных
|
||||
|
||||
// Запись и инициализация соединения с базой данных
|
||||
$this->__set('arangodb', $arangodb);
|
||||
} else {
|
||||
// Не получена инстанция соединения с базой данных
|
||||
|
||||
// Инициализация соединения с базой данных по умолчанию
|
||||
$this->__get('arangodb');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать свойство
|
||||
*
|
||||
* @param string $name Название
|
||||
* @param mixed $value Значение
|
||||
*/
|
||||
public function __set(string $name, mixed $value = null): void
|
||||
{
|
||||
match ($name) {
|
||||
'arangodb' => (function () use ($value) {
|
||||
if ($this->__isset('arangodb')) {
|
||||
// Свойство уже было инициализировано
|
||||
|
||||
// Выброс исключения (неудача)
|
||||
throw new exception('Запрещено реинициализировать соединение с базой данных ($this->arangodb)', 500);
|
||||
} else {
|
||||
// Свойство ещё не было инициализировано
|
||||
|
||||
if ($value instanceof arangodb) {
|
||||
// Передано подходящее значение
|
||||
|
||||
// Запись свойства (успех)
|
||||
self::$arangodb = $value;
|
||||
} else {
|
||||
// Передано неподходящее значение
|
||||
|
||||
// Выброс исключения (неудача)
|
||||
throw new exception('Соединение с базой данных ($this->arangodb) должен быть инстанцией mirzaev\arangodb\connection', 500);
|
||||
}
|
||||
}
|
||||
})(),
|
||||
default => parent::__set($name, $value)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать свойство
|
||||
*
|
||||
* @param string $name Название
|
||||
*
|
||||
* @return mixed Содержимое
|
||||
*/
|
||||
public function __get(string $name): mixed
|
||||
{
|
||||
return match ($name) {
|
||||
'arangodb' => (function () {
|
||||
if (!$this->__isset('db')) {
|
||||
// Свойство не инициализировано
|
||||
|
||||
// Инициализация значения по умолчанию исходя из настроек
|
||||
$this->__set('arangodb', new arangodb(require static::ARANGODB));
|
||||
}
|
||||
|
||||
return self::$arangodb;
|
||||
})(),
|
||||
default => parent::__get($name)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить свойство на инициализированность
|
||||
*
|
||||
* @param string $name Название
|
||||
*/
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return match ($name) {
|
||||
default => parent::__isset($name)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить свойство
|
||||
*
|
||||
* @param string $name Название
|
||||
*/
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
match ($name) {
|
||||
default => parent::__isset($name)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Статический вызов
|
||||
*
|
||||
* @param string $name Название
|
||||
* @param array $arguments Параметры
|
||||
*/
|
||||
public static function __callStatic(string $name, array $arguments): mixed
|
||||
{
|
||||
match ($name) {
|
||||
default => throw new exception("Не найдено свойство или функция: $name", 500)
|
||||
};
|
||||
}
|
||||
public function __construct() {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\models\enumerations;
|
||||
|
||||
/**
|
||||
* Types of languages by ISO 639-1 standart
|
||||
*
|
||||
* @package mirzaev\site\repression\models\enumerations
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
enum language
|
||||
{
|
||||
case en;
|
||||
case ru;
|
||||
|
||||
/**
|
||||
* Label
|
||||
*
|
||||
* Initialize label of the language
|
||||
*
|
||||
* @param language|null $language Language into which to translate
|
||||
*
|
||||
* @return string Translated label of the language
|
||||
*
|
||||
* @todo
|
||||
* 1. More languages
|
||||
* 2. Cases???
|
||||
*/
|
||||
public function label(?language $language = language::en): string
|
||||
{
|
||||
// Exit (success)
|
||||
return match ($this) {
|
||||
language::en => match ($language) {
|
||||
language::en => 'English',
|
||||
language::ru => 'Английский'
|
||||
},
|
||||
language::ru => match ($language) {
|
||||
language::en => 'Russian',
|
||||
language::ru => 'Русский'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\models\interfaces;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\site\repression\models\traits\csv as csv_trait;
|
||||
|
||||
/**
|
||||
* CSV
|
||||
*
|
||||
* Comma-Separated Values by RFC 4180
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4180
|
||||
*
|
||||
* @used-by csv_trait
|
||||
* @package mirzaev\site\repression\models\interfaces
|
||||
*
|
||||
* @var string FILE Path to the database file
|
||||
*
|
||||
* @method void static write() Write to the database file
|
||||
* @method array|null static read() Read from the database file
|
||||
* @method string|false static serialize() Preparing data for writing to the database
|
||||
* @method array|false static deserialize() Preparing data from the database to processing
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
interface csv
|
||||
{
|
||||
/**
|
||||
* File
|
||||
*
|
||||
* @var string FILE Path to the database file
|
||||
*/
|
||||
public const string FILE = DATABASE . DIRECTORY_SEPARATOR . 'database.csv';
|
||||
|
||||
/**
|
||||
* Write
|
||||
*
|
||||
* Write to the database file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function write(): void;
|
||||
|
||||
/**
|
||||
* Read
|
||||
*
|
||||
* Read from the start of the database file
|
||||
*
|
||||
* @param int $rows Amount of rows for reading
|
||||
*
|
||||
* @return array|null Readed records
|
||||
*/
|
||||
public static function read(int $rows = 1): ?array;
|
||||
|
||||
/**
|
||||
* Last
|
||||
*
|
||||
* Read from the end of the database file
|
||||
*
|
||||
* @param int $rows Amount of rows for reading
|
||||
*
|
||||
* @return array|null Readed records
|
||||
*/
|
||||
public static function last(int $rows = 1): ?array;
|
||||
|
||||
/**
|
||||
* Serialize
|
||||
*
|
||||
* Preparing data for writing to the database
|
||||
*
|
||||
* @param array $parameters Values for serializing
|
||||
*
|
||||
* @return string|false Serialized data
|
||||
*/
|
||||
public static function serialize(array $parameters): string|false;
|
||||
|
||||
/**
|
||||
* Deserialize
|
||||
*
|
||||
* Preparing data from the database to processing
|
||||
*
|
||||
* @param string $row Record for deserializing
|
||||
*
|
||||
* @return array|false Serialized data
|
||||
*/
|
||||
public static function deserialize(string $row): array|false;
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\models\traits;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\site\repression\models\interfaces\csv as csv_interface;
|
||||
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* CSV
|
||||
*
|
||||
* Comma-Separated Values by RFC 4180
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4180
|
||||
*
|
||||
* @uses csv_interface
|
||||
* @package mirzaev\arming_bot\models\traits
|
||||
*
|
||||
* @var protected readonly _document|null $document An instance of the ArangoDB document
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
trait csv
|
||||
{
|
||||
/**
|
||||
* Read
|
||||
*
|
||||
* Read from the start of the database file
|
||||
*
|
||||
* @param int $rows Amount of rows for reading
|
||||
* @param array &$errors Buffer of errors
|
||||
*
|
||||
* @return array|null Readed records
|
||||
*/
|
||||
public static function read(int $rows = 0, &$errors = []): ?array
|
||||
{
|
||||
try {
|
||||
// Initializing the buffer of readed records
|
||||
$records = [];
|
||||
|
||||
// Opening the file with views records
|
||||
$file = fopen(static::FILE, 'c+');
|
||||
|
||||
while (--$rows >= 0 && ($row = fgets($file, 4096)) !== false) {
|
||||
// Iterating over rows (records)
|
||||
|
||||
// Deserealizing record
|
||||
$deserialized = static::deserialize($row);
|
||||
|
||||
if ($deserialized) {
|
||||
// Deserialized record
|
||||
|
||||
// Writing to the buffer of readed records
|
||||
$records[] = $deserialized;
|
||||
}
|
||||
}
|
||||
|
||||
// Closing file with views records
|
||||
fclose($file);
|
||||
|
||||
// Exit (success)
|
||||
return $records;
|
||||
} catch (exception $e) {
|
||||
// Write to the buffer of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
|
||||
// Writing to the log of errors
|
||||
/* log::write(type::ERRORS, "[{$_SERVER['REMOTE_ADDR']}] " . (empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : "[{$_SERVER['HTTP_X_FORWARDED_FOR']}] ") . $e->getMessage()); */
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize
|
||||
*
|
||||
* Preparing data for writing to the database
|
||||
*
|
||||
* @param array $parameters Values for serializing
|
||||
* @param bool $created Add date of creating at the end?
|
||||
*
|
||||
* @return string|false Serialized data
|
||||
*/
|
||||
public static function serialize(array $parameters, bool $created = true): string|false
|
||||
{
|
||||
// Declaring the buffer of serialized values
|
||||
$serialized = '';
|
||||
|
||||
// Sanitizing values
|
||||
foreach ($parameters as $value) $serialized .= ',' . preg_replace('/(?<=[^^])"(?=[^$])/', '""', preg_replace('/(?<=[^^]),(?=[^$])/', '\,', $value ?? ''));
|
||||
|
||||
// Writing date of creating to the buffer of serialized values
|
||||
if ($created) $serialized .= ',' . time();
|
||||
|
||||
// Trimming excess first comma in the buffer of serialized values
|
||||
$serialized = mb_substr($serialized, 1, mb_strlen($serialized));
|
||||
|
||||
// Exit (success/fail)
|
||||
return empty($serialized) ? false : $serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize
|
||||
*
|
||||
* Preparing data from the database to processing
|
||||
*
|
||||
* @param string $row Record for deserializing
|
||||
*
|
||||
* @return array|false Serialized data
|
||||
*/
|
||||
public static function deserialize(string $row): array|false
|
||||
{
|
||||
// Separating row by commas
|
||||
preg_match_all('/(?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)/', $row, $matches);
|
||||
|
||||
// Converting double quotes to single quotes
|
||||
foreach ($matches[2] as &$match)
|
||||
if (empty($match = preg_replace('/[\n\r]/', '', preg_replace('/""/', '"', preg_replace('/\\\,/', ',', trim((string) $match, '"'))))))
|
||||
$match = null;
|
||||
|
||||
// Exit (success/fail)
|
||||
return empty($matches[2]) ? false : $matches[2];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\site\repression\models\traits;
|
||||
|
||||
// Built-in libraries
|
||||
use Exception as exception,
|
||||
Generator as generator;
|
||||
|
||||
/**
|
||||
* File
|
||||
*
|
||||
* @package mirzaev\site\repression\models\traits
|
||||
*
|
||||
* @method generator|null
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
trait file
|
||||
{
|
||||
/**
|
||||
* Read
|
||||
*
|
||||
* @param resource $file Pointer to the file (fopen())
|
||||
* @param int $offset Offset of rows for start reading
|
||||
* @param int $rows Amount of rows for reading
|
||||
* @param int $position Initial cursor position on a row
|
||||
* @param int $step Reading step
|
||||
* @param array &$errors Buffer of errors
|
||||
*
|
||||
* @return generator|null|false
|
||||
*/
|
||||
private static function read($file, int $offset = 0, int $rows = 500, int $position = 0, int $step = 1, &$errors = []): generator|null|false
|
||||
{
|
||||
try {
|
||||
while ($offset-- > 0) {
|
||||
do {
|
||||
// Iterate over symbols of the row
|
||||
|
||||
// The end (or the beginning) of the file reached (success)
|
||||
if (feof($file)) break;
|
||||
|
||||
// Moving the cursor to next position on the row
|
||||
fseek($file, $position += $step, SEEK_END);
|
||||
|
||||
// Reading a character of the row
|
||||
$character = fgetc($file);
|
||||
|
||||
// Is the character a carriage return? (end or start of the row)
|
||||
} while ($character !== PHP_EOL);
|
||||
}
|
||||
|
||||
while ($rows-- > 0) {
|
||||
// Reading rows
|
||||
|
||||
// Initializing of the buffer of row
|
||||
$row = '';
|
||||
|
||||
// Initializing the character buffer to generate $row
|
||||
$character = '';
|
||||
|
||||
do {
|
||||
// Iterate over symbols of the row
|
||||
|
||||
// The end (or the beginning) of the file reached (success)
|
||||
if (feof($file)) break;
|
||||
|
||||
// Building the row
|
||||
$row = $step > 0 ? $row . $character : $character . $row;
|
||||
|
||||
// Moving the cursor to next position on the row
|
||||
fseek($file, $position += $step, SEEK_END);
|
||||
|
||||
// Reading a character of the row
|
||||
$character = fgetc($file);
|
||||
|
||||
// Is the character a carriage return? (end or start of the row)
|
||||
} while ($character !== PHP_EOL);
|
||||
|
||||
// Exit (success)
|
||||
yield empty($row) ? null : $row;
|
||||
}
|
||||
|
||||
// Exit (success)
|
||||
return null;
|
||||
} catch (exception $e) {
|
||||
// Write to the buffer of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
|
||||
// Write to the log of errors
|
||||
/* log::write(type::ERRORS, "[{$_SERVER['REMOTE_ADDR']}] " . (empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : "[{$_SERVER['HTTP_X_FORWARDED_FOR']}] ") . $e->getMessage()); */
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -4,225 +4,491 @@ declare(strict_types=1);
|
|||
|
||||
namespace mirzaev\site\repression\models;
|
||||
|
||||
// Фреймворк ArangoDB
|
||||
use mirzaev\arangodb\collection,
|
||||
mirzaev\arangodb\document;
|
||||
// Files of the project
|
||||
use mirzaev\site\repression\models\interfaces\csv as csv_interface,
|
||||
mirzaev\site\repression\models\traits\csv as csv_trait,
|
||||
mirzaev\site\repression\models\traits\file;
|
||||
|
||||
// Библиотека для ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
// Встроенные библиотеки
|
||||
// Built-in libraries
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Счётчик просмотров
|
||||
* Counter of views
|
||||
*
|
||||
* @package mirzaev\site\repression\models
|
||||
*
|
||||
* @var string FILE Path to the datavase file
|
||||
*
|
||||
* @method
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
class views extends core
|
||||
class views extends core implements csv_interface
|
||||
{
|
||||
/**
|
||||
* Коллекция
|
||||
*/
|
||||
final public const COLLECTION = 'views';
|
||||
use csv_trait, file {
|
||||
csv_trait::read insteadof file;
|
||||
file::read as protected file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Увеличить счётчик посещений используя данные пользователя из запроса
|
||||
* File
|
||||
*
|
||||
* @param ?array $data Дополнительные данные пользователя
|
||||
*
|
||||
* @return bool Записано в базу данных?
|
||||
* @var string FILE Path to the database file
|
||||
*/
|
||||
public static function increase(?array $data = null, array &$errors = []): bool
|
||||
{
|
||||
final public const string FILE = DATABASE . DIRECTORY_SEPARATOR . 'views.csv';
|
||||
|
||||
/**
|
||||
* Write
|
||||
*
|
||||
* Write request data to the views registry
|
||||
*
|
||||
* @param string|null $address
|
||||
* @param string|null $connecting
|
||||
* @param string|null $forwarded
|
||||
* @param string|null $useragent
|
||||
* @param string|null $referer
|
||||
* @param string|null $continent
|
||||
* @param string|null $country
|
||||
* @param string|null $country_name
|
||||
* @param string|null $region
|
||||
* @param string|null $city
|
||||
* @param string|float|null $latitute
|
||||
* @param string|float|null $longitude
|
||||
* @param string|null $organisation
|
||||
* @param string|null $flag
|
||||
* @param string|null $currency
|
||||
* @param string|null $timezone
|
||||
* @param array &$errors Buffer of errors
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function write(
|
||||
?string $address = null,
|
||||
?string $connecting = null,
|
||||
?string $forwarded = null,
|
||||
?string $useragent = null,
|
||||
?string $referer = null,
|
||||
?string $continent = null,
|
||||
?string $country = null,
|
||||
?string $country_name = null,
|
||||
?string $region = null,
|
||||
?string $city = null,
|
||||
string|float|null $latitute = null,
|
||||
string|float|null $longitude = null,
|
||||
?string $organisation = null,
|
||||
?string $flag = null,
|
||||
?string $currency = null,
|
||||
?string $timezone = null,
|
||||
&$errors = []
|
||||
): void {
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION))
|
||||
if ($_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return null;
|
||||
else if (document::write(static::$arangodb->session, self::COLLECTION, [
|
||||
'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
|
||||
'cf-connecting-ip' => $_SERVER['cf-connecting-ip'] ?? null,
|
||||
'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
|
||||
'referer' => $_SERVER['HTTP_REFERER'] ?? null,
|
||||
'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null
|
||||
] + ($data ?? []))) return true;
|
||||
else throw new exception('Не удалось создать аккаунт');
|
||||
else throw new exception('Не удалось инициализировать коллекцию');
|
||||
// Initializing registry of rows before
|
||||
$before = [];
|
||||
|
||||
if (file_exists(static::FILE) && filesize(static::FILE) > 0) {
|
||||
// File exists and not empty
|
||||
|
||||
// Opening the file with views records
|
||||
$file = fopen(static::FILE, 'c+');
|
||||
|
||||
while (($row = fgets($file, 4096)) !== false) {
|
||||
// Iterating over rows
|
||||
|
||||
// Writing the registry of rows before
|
||||
$before[] = $row;
|
||||
}
|
||||
|
||||
// Closing the file with views records
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($row);
|
||||
|
||||
// Opening the file with views records
|
||||
$file = fopen(static::FILE, 'c');
|
||||
|
||||
if (flock($file, LOCK_EX)) {
|
||||
// The file was locked
|
||||
|
||||
// Clearing the file
|
||||
ftruncate($file, 0);
|
||||
|
||||
// Serializing values
|
||||
$serialized = static::serialize([
|
||||
$address,
|
||||
$connecting,
|
||||
$forwarded,
|
||||
$useragent,
|
||||
$referer,
|
||||
$continent,
|
||||
$country,
|
||||
$country_name,
|
||||
$region,
|
||||
$city,
|
||||
$latitute,
|
||||
$longitude,
|
||||
$organisation,
|
||||
$flag,
|
||||
$currency,
|
||||
$timezone
|
||||
]);
|
||||
|
||||
// Writing to the buffer for insertion to the views registry
|
||||
if (count($before)) $serialized = trim(implode("", $before)) . "\n" . $serialized;
|
||||
|
||||
// Writing a new record to the views registry
|
||||
fwrite($file, $serialized);
|
||||
|
||||
// Applying changes
|
||||
fflush($file);
|
||||
|
||||
// Unlocking the file
|
||||
flock($file, LOCK_UN);
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($serialized, $record, $before);
|
||||
|
||||
// Closing the file with views records
|
||||
fclose($file);
|
||||
|
||||
// Writing to the log
|
||||
/* log::write(type::FILE, "[CREATE] $domain $ip $port"); */
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
// Write to the buffer of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
|
||||
// Write to the log of errors
|
||||
/* log::write(type::ERRORS, "[{$_SERVER['REMOTE_ADDR']}] " . (empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : "[{$_SERVER['HTTP_X_FORWARDED_FOR']}] ") . $e->getMessage()); */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search
|
||||
*
|
||||
* Search for a record by IP-address
|
||||
*
|
||||
* @param string|null $address IP-address
|
||||
* @param int $rows Amount of rows for reading
|
||||
* @param array &$errors Buffer of errors
|
||||
*
|
||||
* @return array|null Found view record
|
||||
*/
|
||||
public static function search(?string $address, int $rows = 0, &$errors = []): ?array
|
||||
{
|
||||
try {
|
||||
// Opening the file with views records
|
||||
$file = fopen(static::FILE, 'c+');
|
||||
|
||||
while ($rows-- > 0 && ($row = fgets($file, 4096)) !== false) {
|
||||
// Iterating over rows (records)
|
||||
|
||||
// Deserealizing record
|
||||
$deserialized = static::deserialize($row);
|
||||
|
||||
if ($deserialized) {
|
||||
// Deserialized record
|
||||
|
||||
// Initializing values of the view data
|
||||
$record = array_combine([
|
||||
'address',
|
||||
'connecting',
|
||||
'forwarded',
|
||||
'useragent',
|
||||
'referer',
|
||||
'continent',
|
||||
'country',
|
||||
'country_name',
|
||||
'region',
|
||||
'city',
|
||||
'latitute',
|
||||
'longitude',
|
||||
'organisation',
|
||||
'flag',
|
||||
'currency',
|
||||
'timezone',
|
||||
'created',
|
||||
], $deserialized);
|
||||
|
||||
if (match ($address) {
|
||||
$record['address'], $record['connecting'], $record['forwarded'] => true,
|
||||
default => false
|
||||
}) {
|
||||
// View found by IP-address
|
||||
|
||||
// Closing the file with views records
|
||||
fclose($file);
|
||||
|
||||
// Exit (success)
|
||||
return $record;
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($row, $deserialized, $record);
|
||||
}
|
||||
}
|
||||
|
||||
// Closing file with views records
|
||||
fclose($file);
|
||||
} catch (exception $e) {
|
||||
// Write to the buffer of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
|
||||
// Writing to the log of errors
|
||||
/* log::write(type::ERRORS, "[{$_SERVER['REMOTE_ADDR']}] " . (empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : "[{$_SERVER['HTTP_X_FORWARDED_FOR']}] ") . $e->getMessage()); */
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statistics
|
||||
*
|
||||
* Generate statistics on views (day, week, month, total)
|
||||
*
|
||||
* @param bool $day Statistics on views per day
|
||||
* @param bool $week Statistics on views per week
|
||||
* @param bool $month Statistics on views per month
|
||||
* @param bool $total Total statistics on views
|
||||
* @param int $rows Amount of rows for reading
|
||||
* @param array &$errors Buffer of errors
|
||||
*
|
||||
* @return array|false Counters of views (only those that were requested)
|
||||
*/
|
||||
public static function statistics(
|
||||
bool $day = true,
|
||||
bool $week = true,
|
||||
bool $month = true,
|
||||
bool $total = true,
|
||||
int $rows = 1,
|
||||
&$errors = []
|
||||
): array|false {
|
||||
try {
|
||||
// Initializing counters
|
||||
$counters = [];
|
||||
switch (true) {
|
||||
case $day:
|
||||
$counters['day'] = 0;
|
||||
$counters['week'] = 0;
|
||||
$counters['month'] = 0;
|
||||
$counters['total'] = 0;
|
||||
}
|
||||
|
||||
// Opening the file with views records
|
||||
$file = fopen(static::FILE, 'c+');
|
||||
|
||||
while ($rows-- > 0 && ($row = fgets($file, 4096)) !== false) {
|
||||
// Iterating over rows (records)
|
||||
|
||||
// Deserealizing record
|
||||
$deserialized = static::deserialize($row);
|
||||
|
||||
if ($deserialized) {
|
||||
// Deserialized record
|
||||
|
||||
// Initializing value of the view data
|
||||
$created = $deserialized[16];
|
||||
|
||||
// Increasing counters
|
||||
switch (true) {
|
||||
case $day && $created >= time() - 86400:
|
||||
++$counters['day'];
|
||||
case $week && $created >= time() - 604800:
|
||||
++$counters['week'];
|
||||
case $month && $created >= time() - 2592000:
|
||||
++$counters['month'];
|
||||
case $total:
|
||||
++$counters['total'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($row, $deserialized, $created);
|
||||
|
||||
// Closing file with views records
|
||||
fclose($file);
|
||||
|
||||
// Exit (success)
|
||||
return $counters;
|
||||
} catch (exception $e) {
|
||||
// Write to the buffer of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
|
||||
// Writing to the log of errors
|
||||
/* log::write(type::ERRORS, "[{$_SERVER['REMOTE_ADDR']}] " . (empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : "[{$_SERVER['HTTP_X_FORWARDED_FOR']}] ") . $e->getMessage()); */
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function day(array &$errors = []): ?int
|
||||
/**
|
||||
* Last
|
||||
*
|
||||
* Recursively reads unique records by IP-address
|
||||
*
|
||||
* @param int $rows Amount of rows for reading (pool)
|
||||
* @param bool $address Filter by uniqueness by IP-address? (forwarded ?? address ?? connecting)
|
||||
* @oaram int|false $time Filter by elapsed time
|
||||
* @param array &$errors Buffer of errors
|
||||
*
|
||||
* @return array|null Readed records
|
||||
*/
|
||||
public static function last(int $rows = 1, bool $address = false, int|false $time = false, &$errors = []): ?array
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION))
|
||||
return collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
return COUNT_DISTINCT(
|
||||
FOR d IN %s
|
||||
FILTER d.created >= %d
|
||||
RETURN d['x-forwarded-for']
|
||||
)
|
||||
AQL,
|
||||
views::COLLECTION,
|
||||
time() - 86400
|
||||
));
|
||||
else throw new exception('Не удалось инициализировать коллекцию');
|
||||
// Initializing the buffer of readed records
|
||||
$records = [];
|
||||
|
||||
// Initializing the file with views records
|
||||
if (!file_exists(static::FILE)) touch(static::FILE);
|
||||
|
||||
// Opening the file with views records
|
||||
$file = fopen(static::FILE, 'r');
|
||||
|
||||
// Initializing offset of rows for readint
|
||||
$offset = 0;
|
||||
|
||||
// Continuing reading
|
||||
offset:
|
||||
|
||||
foreach (static::file(file: $file, offset: $offset, rows: $rows, position: 0, step: -1) as $row) {
|
||||
// Iterating over rows backwards (rows from the end)
|
||||
|
||||
if ($row === null) {
|
||||
// The end of the file reached
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($row, $deserialized, $offset);
|
||||
|
||||
// Closing file with views records
|
||||
fclose($file);
|
||||
|
||||
// Exit (success)
|
||||
return $records;
|
||||
}
|
||||
|
||||
// Deserealizing record
|
||||
$deserialized = static::deserialize($row);
|
||||
|
||||
if ($deserialized) {
|
||||
// Deserialized record
|
||||
|
||||
$record = array_combine([
|
||||
'address',
|
||||
'connecting',
|
||||
'forwarded',
|
||||
'useragent',
|
||||
'referer',
|
||||
'continent',
|
||||
'country',
|
||||
'country_name',
|
||||
'region',
|
||||
'city',
|
||||
'latitute',
|
||||
'longitude',
|
||||
'organisation',
|
||||
'flag',
|
||||
'currency',
|
||||
'timezone',
|
||||
'created',
|
||||
], $deserialized);
|
||||
|
||||
if ($time) {
|
||||
// Requested filtering by elapsed time
|
||||
|
||||
if (time() - $record['created'] > $time) {
|
||||
// Not enough time has passed yet
|
||||
|
||||
// Skipping iteration
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($address) {
|
||||
// Requested filtering by unique IP-address
|
||||
|
||||
// Declaring buffer of dublicate index
|
||||
$dublicate = null;
|
||||
|
||||
if (
|
||||
($record['forwarded'] && ($dublicate = array_search($record['forwarded'], array_column($records, 'forwarded'), true)) !== false)
|
||||
or ($record['address'] && ($dublicate = array_search($record['address'], array_column($records, 'address'), true)) !== false)
|
||||
or ($record['connecting'] && ($dublicate = array_search($record['connecting'], array_column($records, 'connecting'), true)) !== false)
|
||||
) {
|
||||
// Found a dublicate
|
||||
|
||||
if ($record['created'] > $records[$dublicate]['created']) {
|
||||
// The record is newer than the dublicate (probably useless because the file is read from the end)
|
||||
|
||||
// Replacing the dublicate with the record
|
||||
$records[$dublicate] = $record;
|
||||
}
|
||||
|
||||
// Skipping iteration
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Writing to the buffer of readed records
|
||||
$records[] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($row, $deserialized);
|
||||
|
||||
if (count($records) < $rows) {
|
||||
// Fewer unique rows were read than requested
|
||||
|
||||
// Writing offset for reading
|
||||
$offset += $rows;
|
||||
|
||||
// Continuing reading (enter to the recursion)
|
||||
goto offset;
|
||||
}
|
||||
|
||||
// Deinitializing unnecessary variables
|
||||
unset($offset);
|
||||
|
||||
// Closing file with views records
|
||||
fclose($file);
|
||||
|
||||
// Exit (success)
|
||||
return $records;
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function week(array &$errors = []): ?int
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION))
|
||||
return collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
return COUNT_DISTINCT(
|
||||
FOR d IN %s
|
||||
FILTER d.created >= %d
|
||||
RETURN d['x-forwarded-for']
|
||||
)
|
||||
AQL,
|
||||
views::COLLECTION,
|
||||
time() - 604800
|
||||
));
|
||||
else throw new exception('Не удалось инициализировать коллекцию');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function month(array &$errors = []): ?int
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION))
|
||||
return collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
return COUNT_DISTINCT(
|
||||
FOR d IN %s
|
||||
FILTER d.created >= %d
|
||||
RETURN d['x-forwarded-for']
|
||||
)
|
||||
AQL,
|
||||
views::COLLECTION,
|
||||
time() - 2592000
|
||||
));
|
||||
else throw new exception('Не удалось инициализировать коллекцию');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function all(array &$errors = []): ?int
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION))
|
||||
return collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
return COUNT_DISTINCT(
|
||||
FOR d IN %s
|
||||
RETURN d['x-forwarded-for']
|
||||
)
|
||||
AQL,
|
||||
views::COLLECTION
|
||||
));
|
||||
else throw new exception('Не удалось инициализировать коллекцию');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function last(int $amount = 10, array &$errors = []): ?array
|
||||
{
|
||||
try {
|
||||
if (collection::init(static::$arangodb->session, self::COLLECTION)) {
|
||||
// Инициализирована коллекция
|
||||
|
||||
// Поиск последних просмотров
|
||||
$response = @collection::search(static::$arangodb->session, sprintf(
|
||||
<<<AQL
|
||||
FOR ip in (
|
||||
FOR d IN views
|
||||
SORT d.created DESC
|
||||
RETURN DISTINCT d['x-forwarded-for'] == null ? d.ip : d['x-forwarded-for']
|
||||
)
|
||||
LIMIT 10
|
||||
RETURN (
|
||||
FOR d IN views
|
||||
SORT d.created DESC
|
||||
FILTER d['x-forwarded-for'] == ip || d.ip == ip
|
||||
LIMIT 1
|
||||
RETURN d
|
||||
)
|
||||
AQL,
|
||||
views::COLLECTION,
|
||||
$amount
|
||||
));
|
||||
|
||||
// Инициализация буфера обработанных последних просмотров
|
||||
$buffer = [];
|
||||
|
||||
// Универсализация
|
||||
if ($response instanceof _document) $response = [$response];
|
||||
|
||||
// Обработка последних просмотров
|
||||
foreach ($response ?? [] as $view) $buffer[] = $view->getAll()[0];
|
||||
|
||||
return $buffer;
|
||||
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||
} catch (exception $e) {
|
||||
// Запись в реестр ошибок
|
||||
// Write to the buffer of errors
|
||||
$errors[] = [
|
||||
'text' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'stack' => $e->getTrace()
|
||||
];
|
||||
|
||||
// Writing to the log of errors
|
||||
/* log::write(type::ERRORS, "[{$_SERVER['REMOTE_ADDR']}] " . (empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : "[{$_SERVER['HTTP_X_FORWARDED_FOR']}] ") . $e->getMessage()); */
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,28 +4,31 @@ declare(strict_types=1);
|
|||
|
||||
namespace mirzaev\site\repression;
|
||||
|
||||
use mirzaev\minimal\core;
|
||||
use mirzaev\minimal\router;
|
||||
// Framework for PHP
|
||||
use mirzaev\minimal\core,
|
||||
mirzaev\minimal\route;
|
||||
|
||||
// Enabling debugging
|
||||
ini_set('error_reporting', E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
|
||||
// Initializing system parameters of the project
|
||||
define('INDEX', __DIR__);
|
||||
define('ROOT', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
|
||||
define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
|
||||
define('STORAGE', realpath('..' . DIRECTORY_SEPARATOR . 'storage'));
|
||||
define('INDEX', __DIR__);
|
||||
define('DATABASE', realpath('..' . DIRECTORY_SEPARATOR . 'database'));
|
||||
|
||||
// Автозагрузка
|
||||
require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||
// Initializing dependencies
|
||||
require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||
|
||||
// Инициализация маршрутизатора
|
||||
$router = new router;
|
||||
// Initializing core
|
||||
$core = new core(namespace: __NAMESPACE__);
|
||||
|
||||
// Запись маршрутов
|
||||
$router->write('/', 'index', 'index');
|
||||
// Initializing routes
|
||||
$core->router
|
||||
->write('/', new route('index', 'index'), 'GET');
|
||||
|
||||
// Инициализация ядра
|
||||
$core = new core(namespace: __NAMESPACE__, router: $router);
|
||||
|
||||
// Обработка запроса
|
||||
echo $core->start();
|
||||
// Handling request
|
||||
$core->start();
|
||||
|
|
0
mirzaev/site/repression/system/public/css/account.css → mirzaev/site/repression/system/public/themes/default/css/account.css
Normal file → Executable file
0
mirzaev/site/repression/system/public/css/icon_eye.css → mirzaev/site/repression/system/public/themes/default/css/icon_eye.css
Normal file → Executable file
|
@ -31,6 +31,7 @@
|
|||
border: none;
|
||||
color: var(--text);
|
||||
font-family: commissioner, Roboto, sans-serif;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
transition: 0.1s ease-out;
|
||||
}
|
||||
|
||||
|
@ -87,46 +88,26 @@ body {
|
|||
margin: 0;
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
/* height: 100%; */
|
||||
padding-top: var(--padding-vertical);
|
||||
padding-bottom: var(--padding-vertical);
|
||||
overflow-x: clip;
|
||||
overflow-x: hidden;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
body> :is(article, section)[data-layer] {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: 0.2s ease-out;
|
||||
}
|
||||
|
||||
body>article[data-layer] {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
body>section[data-layer]>canvas.shell {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body>section[data-layer]>canvas.shell {
|
||||
box-shadow: -1px 1px 47px 20px rgba(0, 0, 0, 1);
|
||||
-webkit-box-shadow: -1px 1px 47px 20px rgba(0, 0, 0, 1);
|
||||
-moz-box-shadow: -1px 1px 47px 20px rgba(0, 0, 0, 1);
|
||||
/* outline: 10vw solid; */
|
||||
filter: url("#blob");
|
||||
}
|
||||
|
||||
body>aside {
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
body>article {
|
||||
body>main {
|
||||
position: relative !important;
|
||||
margin: 0 auto !important;
|
||||
width: 800px !important;
|
||||
width: min(800px, 90vw);
|
||||
border-radius: 10px;
|
||||
background-color: var(--background-dark);
|
||||
}
|
||||
|
@ -135,7 +116,7 @@ body> :last-child {
|
|||
margin-bottom: 20vh !important;
|
||||
}
|
||||
|
||||
body>article>div#cover {
|
||||
body>main>div#cover {
|
||||
z-index: 6000;
|
||||
height: 300px;
|
||||
display: flex;
|
||||
|
@ -143,17 +124,17 @@ body>article>div#cover {
|
|||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
body>article>div#cover>img {
|
||||
body>main>div#cover>img {
|
||||
width: 100%;
|
||||
/* object-fit: cover;
|
||||
object-position: top; */
|
||||
}
|
||||
|
||||
body>article>header {
|
||||
body>main>header {
|
||||
z-index: 5000;
|
||||
top: 0;
|
||||
position: sticky;
|
||||
height: 80px;
|
||||
min-height: 80px;
|
||||
overflow: hidden;
|
||||
clip-path: border-box;
|
||||
border-radius: 0px 0px 10px 10px;
|
||||
|
@ -161,31 +142,35 @@ body>article>header {
|
|||
|
||||
}
|
||||
|
||||
body>article>header>section {
|
||||
body>main>header>section {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
min-height: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
filter: blur(2.2px) contrast(30);
|
||||
background: #750000;
|
||||
}
|
||||
|
||||
body>article>header>section>h1 {
|
||||
font-size: 2.5rem;
|
||||
margin: auto;
|
||||
body>main>header>section>h1 {
|
||||
margin: 0.4rem 2rem;
|
||||
font-size: min(2.5rem, 8vw);
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
color: red;
|
||||
}
|
||||
|
||||
body>article>header>section>canvas#title {
|
||||
body>main>header>section>canvas#title {
|
||||
z-index: -5000;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body>article>main,
|
||||
body>main>article,
|
||||
body>section.block {
|
||||
z-index: 1000;
|
||||
position: relative;
|
||||
background-color: var(--background-light);
|
||||
}
|
||||
|
||||
|
@ -193,38 +178,38 @@ body>section.block {
|
|||
box-sizing: border-box;
|
||||
border-radius: 10px;
|
||||
margin: 20px auto 0;
|
||||
width: 800px;
|
||||
width: min(800px, 90vw);
|
||||
padding: 30px 50px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
body>article>main {
|
||||
body>main>article {
|
||||
padding-top: 10px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
body>article>main>section {
|
||||
body>main>article>section {
|
||||
position: relative;
|
||||
margin: 30px 50px;
|
||||
}
|
||||
|
||||
body>article>main>section>p.digression {
|
||||
body>main>article>section>p.digression {
|
||||
margin-left: 30px;
|
||||
color: var(--grey-dark);
|
||||
}
|
||||
|
||||
body>article>main>section>p.digression * {
|
||||
body>main>article>section>p.digression * {
|
||||
color: var(--grey-dark);
|
||||
}
|
||||
|
||||
body>article>main>section#assault>img#wet_bebra {
|
||||
body>main>article>section#assault>img#wet_bebra {
|
||||
width: 200px;
|
||||
float: right;
|
||||
shape-outside: polygon(68px 202px, 200px 197px, 158px 173px, 159px 50px, 192px 25px, 165px 0px, 56px 3px, 41px 27px, 56px 42px, 112px 47px, 103px 68px, 97px 116px, 58px 120px, 40px 169px, 6px 186px);
|
||||
clip-path: polygon(208px 199px, 162px 170px, 162px 54px, 194px 21px, 155px -4px, 48px 7px, 54px 43px, 103px 51px, 91px 114px, 56px 116px, 45px 165px, -1px 186px, 51px 201px, 153px 202px);
|
||||
}
|
||||
|
||||
body>article>main>section#assault>img#scary {
|
||||
body>main>article>section#assault>img#scary {
|
||||
margin-left: -20px;
|
||||
padding-right: 10px;
|
||||
width: 200px;
|
||||
|
@ -233,7 +218,7 @@ body>article>main>section#assault>img#scary {
|
|||
clip-path: polygon(208px 91px, 181px 75px, 203px 22px, 140px 1px, 58px 1px, -8px 112px, 55px 173px, 70px 201px, 136px 201px, 139px 176px, 175px 152px);
|
||||
}
|
||||
|
||||
body>article>main>section#car>img#crying_bebra {
|
||||
body>main>article>section#car>img#crying_bebra {
|
||||
margin-left: -20px;
|
||||
width: 200px;
|
||||
float: left;
|
||||
|
@ -241,7 +226,7 @@ body>article>main>section#car>img#crying_bebra {
|
|||
clip-path: polygon(22px 169px, 44px 200px, 122px 207px, 165px 192px, 173px 151px, 187px 141px, 188px 108px, 169px 103px, 178px 52px, 150px 12px, 86px -7px, 41px 33px, 19px 67px);
|
||||
}
|
||||
|
||||
body>article>main>section#car>img#chill_bebra {
|
||||
body>main>article>section#car>img#chill_bebra {
|
||||
margin-right: -20px;
|
||||
width: 200px;
|
||||
float: right;
|
||||
|
@ -249,7 +234,7 @@ body>article>main>section#car>img#chill_bebra {
|
|||
clip-path: polygon(11px 126px, -6px 194px, 71px 199px, 179px 181px, 204px 90px, 175px 32px, 118px -5px, 60px 27px, 80px 102px, 52px 124px, 54px 60px, 16px 58px, -1px 83px);
|
||||
}
|
||||
|
||||
body>article>main>section#car>img#just {
|
||||
body>main>article>section#car>img#just {
|
||||
margin-left: -20px;
|
||||
padding-right: 10px;
|
||||
width: 200px;
|
||||
|
@ -258,7 +243,7 @@ body>article>main>section#car>img#just {
|
|||
clip-path: polygon(-6px 171px, 76px 170px, 106px 201px, 165px 198px, 193px 159px, 206px 81px, 180px 26px, 129px -5px, 63px 15px, 32px 51px, 29px 124px);
|
||||
}
|
||||
|
||||
body>article>main>section#car>img#evil_bebra {
|
||||
body>main>article>section#car>img#evil_bebra {
|
||||
margin-top: 30px;
|
||||
margin-right: -25px;
|
||||
width: 200px;
|
||||
|
@ -267,7 +252,7 @@ body>article>main>section#car>img#evil_bebra {
|
|||
clip-path: polygon(193px 197px, 161px 10px, 92px -8px, 19px 46px, 23px 138px, 7px 168px, 21px 196px);
|
||||
}
|
||||
|
||||
body>article>main>section#deal>img#marta_with_broken_heart {
|
||||
body>main>article>section#deal>img#marta_with_broken_heart {
|
||||
margin-top: 30px;
|
||||
margin-right: -15px;
|
||||
width: 200px;
|
||||
|
@ -276,7 +261,7 @@ body>article>main>section#deal>img#marta_with_broken_heart {
|
|||
clip-path: polygon(199px 146px, 204px 26px, 125px -1px, 65px -7px, -7px 96px, 19px 169px, 62px 195px, 150px 199px);
|
||||
}
|
||||
|
||||
body>article>main>section#deal>img#two_bebras {
|
||||
body>main>article>section#deal>img#two_bebras {
|
||||
margin-top: 50px;
|
||||
margin-right: -10px;
|
||||
width: 200px;
|
||||
|
@ -285,7 +270,7 @@ body>article>main>section#deal>img#two_bebras {
|
|||
clip-path: polygon(215px 104px, 132px -2px, 108px 22px, 33px 40px, 20px 114px, -6px 127px, 20px 201px, 177px 179px);
|
||||
}
|
||||
|
||||
body>article>main>section#deal>img#fuck {
|
||||
body>main>article>section#deal>img#fuck {
|
||||
margin-left: -20px;
|
||||
width: 200px;
|
||||
float: left;
|
||||
|
@ -293,7 +278,7 @@ body>article>main>section#deal>img#fuck {
|
|||
clip-path: polygon(25px 194px, 100px 207px, 180px 190px, 171px 142px, 188px 90px, 179px 46px, 152px 5px, 75px -7px, 6px 56px, 28px 144px);
|
||||
}
|
||||
|
||||
body>article>main>section#friendship>img#no_comments {
|
||||
body>main>article>section#friendship>img#no_comments {
|
||||
margin-top: 50px;
|
||||
margin-right: 0px;
|
||||
width: 200px;
|
||||
|
@ -302,7 +287,7 @@ body>article>main>section#friendship>img#no_comments {
|
|||
clip-path: polygon(208px 115px, 200px 41px, 145px -7px, 69px 13px, 46px 44px, -10px 58px, 39px 78px, 48px 141px, 46px 187px, 121px 201px, 192px 191px);
|
||||
}
|
||||
|
||||
body>article>main>section#friendship>img#nu_tipa {
|
||||
body>main>article>section#friendship>img#nu_tipa {
|
||||
margin-top: 100px;
|
||||
margin-right: 0px;
|
||||
width: 200px;
|
||||
|
@ -311,22 +296,22 @@ body>article>main>section#friendship>img#nu_tipa {
|
|||
clip-path: polygon(179px 142px, 176px 117px, 195px 100px, 163px 87px, 153px 26px, 91px -12px, 11px 39px, 1px 197px, 181px 203px, 172px 157px);
|
||||
}
|
||||
|
||||
body>article>main>section>img {
|
||||
body>main>article>section>img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
body>article>main>section#conclusion {
|
||||
body>main>article>section#conclusion {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
body>article>main>section#conclusion>h2 {
|
||||
body>main>article>section#conclusion>h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
body>article>main>section#conclusion>a>img {
|
||||
body>main>article>section#conclusion>a>img {
|
||||
margin-top: 30px;
|
||||
margin-right: -4%;
|
||||
height: 300px;
|
||||
|
@ -335,71 +320,87 @@ body>article>main>section#conclusion>a>img {
|
|||
clip-path: polygon(136px 80px, 122px 10px, 92px 30px, 66px 36px, 36px 32px, 36px 91px, 55px 141px, 34px 145px, 32px 181px, 41px 189px, 81px 208px, 28px 264px, 47px 287px, 81px 295px, 155px 266px, 162px 232px, 180px 212px, 179px 180px, 196px 174px, 191px 152px, 146px 157px, 145px 135px, 161px 110px, 165px 92px);
|
||||
}
|
||||
|
||||
body>article>main>section>h2:before {
|
||||
body>article>section>h2:before {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
body>article>main>section#assault>h2:before {
|
||||
body>main>article>section#assault>h2:before {
|
||||
content: '🤕';
|
||||
}
|
||||
|
||||
body>article>main>section#reasons>h2:before {
|
||||
body>main>article>section#reasons>h2:before {
|
||||
content: '😼';
|
||||
}
|
||||
|
||||
body>article>main>section#car>h2:before {
|
||||
body>main>article>section#car>h2:before {
|
||||
content: '😵';
|
||||
}
|
||||
|
||||
body>article>main>section#deal>h2:before {
|
||||
body>main>article>section#deal>h2:before {
|
||||
content: '🤝';
|
||||
}
|
||||
|
||||
body>article>main>section#friendship>h2:before {
|
||||
body>main>article>section#friendship>h2:before {
|
||||
content: '🥳';
|
||||
}
|
||||
|
||||
body>article>main>section#court>h2:before {
|
||||
body>main>article>section#court>h2:before {
|
||||
content: '😥';
|
||||
}
|
||||
|
||||
body>article>main>section#return>h2:before {
|
||||
body>main>article>section#return>h2:before {
|
||||
content: '🥱';
|
||||
}
|
||||
|
||||
|
||||
body>article>main>section#conclusion>h2:before {
|
||||
body>main>article>section#conclusion>h2:before {
|
||||
content: '🤟';
|
||||
}
|
||||
|
||||
|
||||
body>article>main>section>p+p,
|
||||
body>article>main>section>p>small+small {
|
||||
body>main>article>section>p+p,
|
||||
body>main>article>section>p>small+small {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
body>article>main>section>h2 {
|
||||
body>main>article>section>h2 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
body>article>main>section>h2+h3 {
|
||||
body>main>article>section>h2+h3 {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
body>article>main>section> :is(h2, h3) {
|
||||
body>main>article>section> :is(h2, h3) {
|
||||
margin-bottom: 0px;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
body>article>main>section> :is(h2, h3)+p {
|
||||
body>main>article>section> :is(h2, h3)+p {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
body>article>main>section>p:last-of-type {
|
||||
body>main>article>section>p:last-of-type {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
body>article>footer {
|
||||
body>canvas#background {
|
||||
z-index: -1000;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
box-shadow: -1px 1px 47px 20px rgba(0, 0, 0, 1);
|
||||
-webkit-box-shadow: -1px 1px 47px 20px rgba(0, 0, 0, 1);
|
||||
-moz-box-shadow: -1px 1px 47px 20px rgba(0, 0, 0, 1);
|
||||
/* outline: 10vw solid; */
|
||||
filter: url("#blob");
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
body>main>footer {
|
||||
z-index: 3000;
|
||||
}
|
||||
|
||||
|
@ -410,11 +411,12 @@ body>section#contacts>section {
|
|||
body>section#contacts>section#author {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 35px;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
body>section#contacts>section#author>section#avatar {
|
||||
margin-left: 10px;
|
||||
margin-right: 25px;
|
||||
position: relative;
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
|
@ -579,6 +581,7 @@ body>section#license>p>span:last-of-type {
|
|||
}
|
||||
|
||||
body>section#license>img {
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
right: -65px;
|
||||
bottom: -50px;
|
||||
|
@ -593,8 +596,10 @@ body>svg#navalny2 {
|
|||
}
|
||||
|
||||
body>section#navalny {
|
||||
width: 800px;
|
||||
height: 190px;
|
||||
--height: min(200px, 30vw);
|
||||
z-index: 500;
|
||||
width: min(800px, 90vw);
|
||||
height: calc(var(--height));
|
||||
padding: unset;
|
||||
cursor: pointer;
|
||||
-webkit-transform: translateZ(0);
|
||||
|
@ -610,6 +615,7 @@ body>section#navalny {
|
|||
-webkit-backdrop-filter: saturate(100%) hue-rotate(230deg) blur(22px);
|
||||
backdrop-filter: saturate(100%) hue-rotate(230deg) blur(22px);
|
||||
background-color: rgba(0, 3, 210, 0.05);
|
||||
clip-path: polygon(0px 0px, 0 var(--height), 100% var(--height), 100% 0px);
|
||||
}
|
||||
|
||||
body>section#navalny:hover {
|
||||
|
@ -621,77 +627,42 @@ body>section#navalny:hover {
|
|||
|
||||
@media (max-width: 840px) {
|
||||
body {
|
||||
padding-top: unset;
|
||||
padding-bottom: 20px;
|
||||
padding-top: 5vw;
|
||||
padding-bottom: 5vw;
|
||||
}
|
||||
|
||||
body>article>header {
|
||||
body>main>header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body>article {
|
||||
width: 100% !important;
|
||||
body>main>header>section>h1 {
|
||||
padding: 0 15%;
|
||||
}
|
||||
|
||||
body>main {
|
||||
margin: unset;
|
||||
border-radius: unset;
|
||||
}
|
||||
|
||||
body>article>div#cover,
|
||||
body>article>main {
|
||||
border-radius: unset;
|
||||
}
|
||||
|
||||
body>article>main {
|
||||
body>main>article {
|
||||
box-shadow: 1px 6px 11px 3px rgba(0, 0, 0, 0.6);
|
||||
-webkit-box-shadow: 1px 6px 11px 3px rgba(0, 0, 0, 0.6);
|
||||
-moz-box-shadow: 1px 6px 11px 3px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
body>section.block {
|
||||
body>main>section.block {
|
||||
width: calc(100% - 40px) !important;
|
||||
margin-left: 20px !important;
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
|
||||
mask#NAVALNY>text {
|
||||
font-size: calc(5rem - (5rem - 17vw)) !important;
|
||||
transform: translate(0, 120px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 710px) {
|
||||
body>article>header {
|
||||
height: calc(140px + (110px - 20vw));
|
||||
}
|
||||
|
||||
body>article>header>section>h1 {
|
||||
padding: 0 10vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 690px) {
|
||||
body>section#contacts>section {
|
||||
body>section#contacts>section#author {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
body>section#contacts>section#author>section#avatar {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
max-width: 120px;
|
||||
max-height: 120px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
flex-shrink: 0;
|
||||
clip-path: circle(60px);
|
||||
}
|
||||
|
||||
body>section#contacts>section#author>section#name {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
body>section.block#views>h3 {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
@ -710,10 +681,9 @@ body>section#navalny:hover {
|
|||
body>section.block#views>section:last-of-type {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 430px) {
|
||||
body>article>header>section>h1 {
|
||||
padding: 0 5vw;
|
||||
mask#NAVALNY>text {
|
||||
font-size: 18vw !important;
|
||||
transform: translate(0, 60px) !important;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ section.popup>div.wrap {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
border-radius: 2px;
|
||||
border-radius: 0.75rem;
|
||||
background-color: var(--background-light);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 950 B After Width: | Height: | Size: 950 B |
0
mirzaev/site/repression/system/public/images/bebra.png → mirzaev/site/repression/system/public/themes/default/images/bebra.png
Normal file → Executable file
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 229 KiB |
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 995 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 944 B |
Before Width: | Height: | Size: 972 B After Width: | Height: | Size: 972 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 836 B After Width: | Height: | Size: 836 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |