Compare commits

..

No commits in common. "0d6ceef0f9846d992face380fc3f7a1fcab3443c" and "1d498f9791e83e85b8f0a4a08342db1285f555e8" have entirely different histories.

153 changed files with 3274 additions and 5658 deletions

View File

@ -5,7 +5,8 @@
"keywords": [
"site",
"api",
"authentication"
"authentication",
"auth"
],
"type": "site",
"homepage": "https://git.mirzaev.sexy/mirzaev/site-account",
@ -30,16 +31,15 @@
}
],
"require": {
"php": "~8.2",
"ext-sodium": "~8.2",
"php": "~8.1",
"ext-sodium": "~8.1",
"mirzaev/minimal": "^2.0.x-dev",
"mirzaev/accounts": "~1.2.x-dev",
"mirzaev/arangodb": "^1.0.0",
"mirzaev/vk": "^5.0",
"triagens/arangodb": "~3.9.x-dev",
"twig/twig": "^3.4",
"guzzlehttp/guzzle": "^7.5",
"scripturadesign/markov": "^2.0"
"guzzlehttp/guzzle": "^7.5"
},
"require-dev": {
"phpunit/phpunit": "~9.5"

203
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c0621deb07173185d918fd02e7551ca4",
"content-hash": "b562666ddbd12600f288a2949acc97d4",
"packages": [
{
"name": "guzzlehttp/guzzle",
@ -396,13 +396,13 @@
"source": {
"type": "git",
"url": "https://git.mirzaev.sexy/mirzaev/accounts",
"reference": "aa93c4d26395025fa16bb65e8a40332ac352a742"
"reference": "4d6df00dc9538c99d8eced6deebab55f1af2bf8a"
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"guzzlehttp/guzzle": "^7.5",
"php": "~8.2"
"ext-dom": "20031129",
"ext-libxml": "~8.1.1",
"guzzlehttp/guzzle": "^7.2",
"php": "~8.1"
},
"require-dev": {
"phpdocumentor/phpdocumentor": ">=2.9",
@ -442,7 +442,7 @@
"type": "funding"
}
],
"time": "2023-02-17T08:36:36+00:00"
"time": "2022-11-05T23:48:10+00:00"
},
{
"name": "mirzaev/arangodb",
@ -931,69 +931,18 @@
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "scripturadesign/markov",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/scripturadesign/markov.git",
"reference": "ca6d51a5d8ce1e115708c2d38c49c397e515cae9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/scripturadesign/markov/zipball/ca6d51a5d8ce1e115708c2d38c49c397e515cae9",
"reference": "ca6d51a5d8ce1e115708c2d38c49c397e515cae9",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.4.0",
"phpunit/phpunit": "^9.5",
"vimeo/psalm": "^4.15.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Scriptura\\Markov\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Martin Dilling-Hansen",
"email": "martindilling@gmail.com",
"homepage": "http://martindilling.com",
"role": "Developer"
}
],
"description": "Markov Chain",
"homepage": "https://github.com/scripturadesign/markov",
"keywords": [
"markov chain"
],
"support": {
"issues": "https://github.com/scripturadesign/markov/issues",
"source": "https://github.com/scripturadesign/markov/tree/v2.0.0"
},
"time": "2021-12-15T00:09:01+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version": "v3.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0121954c80fd17c18cf050fe73360e63bb43d4fb"
"reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0121954c80fd17c18cf050fe73360e63bb43d4fb",
"reference": "0121954c80fd17c18cf050fe73360e63bb43d4fb",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
"reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
"shasum": ""
},
"require": {
@ -1002,7 +951,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.1-dev"
},
"thanks": {
"name": "symfony/contracts",
@ -1031,7 +980,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1"
},
"funding": [
{
@ -1047,7 +996,7 @@
"type": "tidelift"
}
],
"time": "2023-02-02T07:48:03+00:00"
"time": "2022-02-25T11:15:52+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -1277,16 +1226,16 @@
},
{
"name": "twig/twig",
"version": "v3.5.1",
"version": "v3.4.3",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a6e0510cc793912b451fd40ab983a1d28f611c15"
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15",
"reference": "a6e0510cc793912b451fd40ab983a1d28f611c15",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
"shasum": ""
},
"require": {
@ -1301,7 +1250,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.5-dev"
"dev-master": "3.4-dev"
}
},
"autoload": {
@ -1337,7 +1286,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.5.1"
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
},
"funding": [
{
@ -1349,36 +1298,36 @@
"type": "tidelift"
}
],
"time": "2023-02-08T07:49:20+00:00"
"time": "2022-09-28T08:42:51+00:00"
}
],
"packages-dev": [
{
"name": "doctrine/instantiator",
"version": "2.0.0",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
"reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
"shasum": ""
},
"require": {
"php": "^8.1"
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^11",
"doctrine/coding-standard": "^9",
"ext-pdo": "*",
"ext-phar": "*",
"phpbench/phpbench": "^1.2",
"phpstan/phpstan": "^1.9.4",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5.27",
"vimeo/psalm": "^5.4"
"phpbench/phpbench": "^0.16 || ^1",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22"
},
"type": "library",
"autoload": {
@ -1405,7 +1354,7 @@
],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/2.0.0"
"source": "https://github.com/doctrine/instantiator/tree/1.4.1"
},
"funding": [
{
@ -1421,7 +1370,7 @@
"type": "tidelift"
}
],
"time": "2022-12-30T00:23:10+00:00"
"time": "2022-03-03T08:28:38+00:00"
},
{
"name": "myclabs/deep-copy",
@ -1484,16 +1433,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.15.3",
"version": "v4.15.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039"
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"shasum": ""
},
"require": {
@ -1534,9 +1483,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2"
},
"time": "2023-01-16T22:05:37+00:00"
"time": "2022-11-12T15:38:23+00:00"
},
{
"name": "phar-io/manifest",
@ -1651,23 +1600,23 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.25",
"version": "9.2.19",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954"
"reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954",
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559",
"reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.15",
"nikic/php-parser": "^4.14",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@ -1716,7 +1665,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19"
},
"funding": [
{
@ -1724,7 +1673,7 @@
"type": "github"
}
],
"time": "2023-02-25T05:32:00+00:00"
"time": "2022-11-18T07:47:47+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1969,20 +1918,20 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.4",
"version": "9.5.26",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d"
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d",
"reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2",
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.3.1 || ^2",
"doctrine/instantiator": "^1.3.1",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
@ -2020,7 +1969,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.6-dev"
"dev-master": "9.5-dev"
}
},
"autoload": {
@ -2051,7 +2000,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26"
},
"funding": [
{
@ -2067,7 +2016,7 @@
"type": "tidelift"
}
],
"time": "2023-02-27T13:06:37+00:00"
"time": "2022-10-28T06:00:21+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2435,16 +2384,16 @@
},
{
"name": "sebastian/environment",
"version": "5.1.5",
"version": "5.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
"reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"shasum": ""
},
"require": {
@ -2486,7 +2435,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
},
"funding": [
{
@ -2494,7 +2443,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:03:51+00:00"
"time": "2022-04-03T09:37:03+00:00"
},
{
"name": "sebastian/exporter",
@ -2808,16 +2757,16 @@
},
{
"name": "sebastian/recursion-context",
"version": "4.0.5",
"version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1"
"reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
"reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
"reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
"shasum": ""
},
"require": {
@ -2856,10 +2805,10 @@
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "https://github.com/sebastianbergmann/recursion-context",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5"
"source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
},
"funding": [
{
@ -2867,7 +2816,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:07:39+00:00"
"time": "2020-10-26T13:17:30+00:00"
},
{
"name": "sebastian/resource-operations",
@ -2926,16 +2875,16 @@
},
{
"name": "sebastian/type",
"version": "3.2.1",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
"reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
"reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
"reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
"shasum": ""
},
"require": {
@ -2970,7 +2919,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
"source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
"source": "https://github.com/sebastianbergmann/type/tree/3.2.0"
},
"funding": [
{
@ -2978,7 +2927,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:13:03+00:00"
"time": "2022-09-12T14:47:03+00:00"
},
{
"name": "sebastian/version",
@ -3094,8 +3043,8 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "~8.2",
"ext-sodium": "~8.2"
"php": "~8.1",
"ext-sodium": "~8.1"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"

View File

@ -1,74 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core,
mirzaev\site\account\models\generators\password,
mirzaev\site\account\models\invite;
/**
* Контроллер API
*
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class api extends core
{
/**
* Проверить существование
*
* @param array $parameters Параметры запроса
*
* @return string JSON с параметром exist
*/
public function invite_verify(array $parameters = []): string
{
// Инициализация буфера ответа
$return = ['errors' => &$this->errors];
// Запрос проверки на существование приглашения
$invite = invite::read($parameters['key'], $this->errors['account']);
$return['exist'] = isset($invite);
if ($parameters['from'] == 1) $return['from'] = ['login' => 'mirzaev'] ?? $invite->from();
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode($return);
}
/**
* Сгенерировать классический пароль
*
* @param array $parameters Параметры запроса
*
* @return string JSON с параметром password
*/
public function password_classic(array $parameters = []): string
{
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode(['password' => password::classic((int) $parameters['length'], $this->errors), 'errors' => $this->errors]);
}
/**
* Сгенерировать мнемонический пароль
*
* @param array $parameters Параметры запроса
*
* @return string JSON с параметром password
*/
public function password_mnemonic(array $parameters = []): string
{
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode(['password' => password::mnemonic((int) $parameters['length'], $this->errors), 'errors' => $this->errors]);
}
}

View File

@ -1,149 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core,
mirzaev\site\account\models\account as model,
mirzaev\site\account\models\session,
mirzaev\site\account\models\vk;
// Фреймворк для ВКонтакте
use mirzaev\vk\core as api;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use stdClass;
/**
* Контроллер аккаунта
*
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class account extends core
{
/**
* Страница профиля
*
* @param array $parameters Параметры запроса
*/
public function index(array $parameters = []): ?string
{
return null;
}
/**
* Инициализация
*
* @param array $parameters Параметры запроса
*/
public function initialization(array $parameters = []): ?string
{
if ($this->variables['account'] instanceof _document) {
// Найден аккаунт
if ($this->variables['vk'] instanceof _document) {
// Найден аккаунт ВКонтакте
// Инициализация данных аккаунта ВКонтакте
vk::parse($this->variables['vk'], $this->variables['errors']['vk']);
}
// Запись кода ответа
http_response_code(200);
return null;
} else {
// Не найден аккаунт
// Запись кода ответа
http_response_code(401);
// Запись заголовка ответа с ключом аккаунта
header('session: ' . $this->variables['session']->hash);
return null;
}
// Запись кода ответа
http_response_code(500);
return null;
}
/**
* Связь аккаунта с аккаунтом ВКонтакте
*
* @param array $parameters Параметры запроса
*/
public function connect(array $parameters = []): ?string
{
if ($this->variables['session']->hash === $parameters['state']) {
// Совпадает хеш сессии с полученным хешем из ответа ВКонтакте
if (!empty($response = vk::key($parameters['code'], $this->variables['errors']['vk']))) {
// Получены данные аккаунта ВКонтакте
if (($this->variables['vk'] = vk::initialization($response, $this->variables['errors']['vk'])) instanceof _document) {
// Инициализирован аккаунт ВКонтакте
if (($this->variables['account'] = vk::account($this->variables['vk'])) instanceof _document) {
// Найден аккаунт (существующий)
if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) {
// Связана сессия с аккаунтом
}
} else if (($this->variables['account'] = model::create($this->variables['errors']['account'])) instanceof _document) {
// Найден аккаунт (создан новый)
if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) {
// Связана сессия с аккаунтом
if (account::connect($this->variables['account'], $this->variables['vk'], $this->variables['errors']['account'])) {
// Связан аккаунт с аккаунтом ВКонтакте
}
}
}
// Инициализация робота для аккаунта ВКонтакте
$this->vk = api::init()->user(key: $this->variables['vk']->access['key']);
if ($this->variables['vk'] instanceof _document) {
// Инициализирован робот для аккаунта ВКонтакте
// Инициализация данных аккаунта ВКонтакте
$data = vk::parse($this->vk, $this->variables['errors']['vk']);
var_dump($data);
die;
if ($data instanceof stdClass) {
// Получены данные ВКонтакте
// Запись в базу данных
vk::update($this->variables['vk'], $data, $this->variables['errors']['vk']);
}
}
}
}
}
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'vk.html', $this->variables);
}
/**
* Генерация панели аккаунта
*
* @param array $parameters Параметры запроса
*/
public function panel(array $parameters = []): ?string
{
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'panel.html', $this->variables);
}
}

View File

@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core;
use mirzaev\site\account\models\account_model as account;
use mirzaev\site\account\models\session_model as session;
use mirzaev\site\account\models\vk_model as vk;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
use stdClass;
// Фреймворк для ВКонтакте
use mirzaev\vk\core as api;
/**
* Контроллер аккаунтов
*
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class account_controller extends core
{
/**
* Страница профиля
*
* @param array $parameters Параметры запроса
*/
public function index(array $parameters = []): ?string
{
return null;
}
/**
* Инициализация
*
* @param array $parameters Параметры запроса
*/
public function initialization(array $parameters = []): ?string
{
if ($this->variables['account'] instanceof _document) {
// Найден аккаунт
if ($this->variables['vk'] instanceof _document) {
// Найден аккаунт ВКонтакте
// Инициализация данных аккаунта ВКонтакте
vk::parse($this->variables['vk'], $this->variables['errors']['vk']);
}
// Запись кода ответа
http_response_code(200);
return null;
} else {
// Не найден аккаунт
// Запись кода ответа
http_response_code(401);
// Запись заголовка ответа с ключом аккаунта
header('session: ' . $this->variables['session']->hash);
return null;
}
// Запись кода ответа
http_response_code(500);
return null;
}
/**
* Связь аккаунта с аккаунтом ВКонтакте
*
* @param array $parameters Параметры запроса
*/
public function connect(array $parameters = []): ?string
{
if ($this->variables['session']->hash === $parameters['state']) {
// Совпадает хеш сессии с полученным хешем из ответа ВКонтакте
if (!empty($response = vk::key($parameters['code'], $this->variables['errors']['vk']))) {
// Получены данные аккаунта ВКонтакте
if (($this->variables['vk'] = vk::initialization($response, $this->variables['errors']['vk'])) instanceof _document) {
// Инициализирован аккаунт ВКонтакте
if (($this->variables['account'] = vk::account($this->variables['vk'])) instanceof _document) {
// Найден аккаунт (существующий)
if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) {
// Связана сессия с аккаунтом
}
} else if (($this->variables['account'] = account::create($this->variables['errors']['account'])) instanceof _document) {
// Найден аккаунт (создан новый)
if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) {
// Связана сессия с аккаунтом
if (account::connect($this->variables['account'], $this->variables['vk'], $this->variables['errors']['account'])) {
// Связан аккаунт с аккаунтом ВКонтакте
}
}
}
// Инициализация робота для аккаунта ВКонтакте
$this->vk = api::init()->user(key: $this->variables['vk']->access['key']);
if ($this->variables['vk'] instanceof _document) {
// Инициализирован робот для аккаунта ВКонтакте
// Инициализация данных аккаунта ВКонтакте
$data = vk::parse($this->vk, $this->variables['errors']['vk']);
var_dump($data); die;
if ($data instanceof stdClass) {
// Получены данные ВКонтакте
// Запись в базу данных
vk::update($this->variables['vk'], $data, $this->variables['errors']['vk']);
}
}
}
}
}
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'vk.html', $this->variables);
}
/**
* Генерация панели аккаунта
*
* @param array $parameters Параметры запроса
*/
public function panel(array $parameters = []): ?string
{
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'panel.html', $this->variables);
}
}

View File

@ -1,73 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core,
mirzaev\site\account\controllers\traits\errors,
mirzaev\site\account\models\generators\password;
// Встроенные библиотеки
use exception;
/**
* Контроллер API
*
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class api extends core
{
use errors;
/**
* Сгенерировать пароль
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
*/
public function password(array $parameters = []): string
{
// Инициализация буфера ответа
$buffer = [];
// Инициализация реестра возвращаемых параметров
$return = explode(',', $parameters['return'], 50);
// Инициализация значений по умолчению
$parameters['length'] ??= 6;
$parameters['type'] ??= 'classic';
try {
// Проверка параметров на соответствие требованиям
if (($parameters['length'] = (int) $parameters['length']) === 0) throw new exception('Минимальная длина генерируемого пароля: 1 символ');
if ($parameters['type'] !== 'classic' && $parameters['type'] !== 'mnemonic') throw new exception('Допустимые типы пароля: "mnemonic", "classic"');
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'password' => $buffer['password'] = password::{$parameters['type'] ?? 'classic'}($parameters['length'], $this->errors),
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
} catch (exception $e) {
// Запись в журнал ошибок
$this->errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode($buffer);
}
}

View File

@ -5,10 +5,10 @@ declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\views\templater;
use mirzaev\site\account\views\manager;
use mirzaev\site\account\models\core as models;
use mirzaev\site\account\models\account;
use mirzaev\site\account\models\session;
use mirzaev\site\account\models\account_model as account;
use mirzaev\site\account\models\session_model as session;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
@ -34,27 +34,9 @@ class core extends controller
protected robot $vk;
/**
* Инстанция сессии
* Переменные окружения
*/
public session $session;
/**
* Инстанция аккаунта
*/
public ?account $account;
/**
* Постфикс
*/
public string $postfix = '';
/**
* Реестр ошибок
*/
public array $errors = [
'session' => [],
'account' => []
];
protected array $variables = [];
/**
* Конструктор
@ -68,21 +50,24 @@ class core extends controller
// Инициализация ядра моделей (соединение с базой данных...)
new models();
// Инициализация шаблонизатора представлений
$this->view = new templater;
// Инициализация журнала ошибок
$this->variables['errors'] = [
'session' => [],
'account' => [],
'vk' => []
];
// Инициализация даты до которой будет активна сессия
$expires = time() + 604800;
// Инициализация сессии (без журналирования)
$this->session = new session($_COOKIE["session"] ?? null, $expires) ??
header('Location: https://mirzaev.sexy/error?code=500&text=Не+удалось+инициализировать+сессию');
$this->variables['session'] = session::initialization($_COOKIE["session"] ?? null, $expires) ?? header('Location: https://mirzaev.sexy/error?code=500&text=Не+удалось+инициализировать+сессию');
if ($_COOKIE["session"] ?? null !== $this->session->hash) {
if ($_COOKIE["session"] ?? null !== $this->variables['session']->hash) {
// Изменился хеш сессии (подразумевается, что сессия устарела)
// Запись хеша новой сессии
setcookie('session', $this->session->hash, [
setcookie('session', $this->variables['session']->hash, [
'expires' => $expires,
'domain' => 'mirzaev.sexy',
'path' => '/',
@ -93,11 +78,23 @@ class core extends controller
}
// Инициализация аккаунта (без журналирования)
$this->account = $this->session->account();
$this->variables['account'] = session::account($this->variables['session']);
if ($this->account instanceof _document) {
if ($this->variables['account'] instanceof _document) {
// Инициализирован аккаунт
// Инициализация аккаунта ВКонтакте (без журналирования)
$this->variables['vk'] = account::vk($this->variables['account']);
if ($this->variables['vk'] instanceof _document) {
// Инициализирован аккаунт ВКонтакте
// Инициализация робота для аккаунта ВКонтакте
$this->vk = vk::init()->user(key: $this->variables['vk']->access['key']);
} else unset($this->variables['account'], $this->variables['vk']);
}
// Инициализация препроцессора представления
$this->view = new manager;
}
}

View File

@ -13,7 +13,7 @@ use mirzaev\site\account\controllers\core;
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class error extends core
final class error_controller extends core
{
/**
* Страница с ошибкой
@ -23,22 +23,22 @@ final class error extends core
public function index(array $parameters = []): ?string
{
// Запись текста ошибки в переменную окружения
$this->view->text = $parameters['text'] ?? null;
$this->variables['text'] = $parameters['text'] ?? null;
if (isset($parameters['code'])) {
// Получен код ошибки
// Запись кода ошибки в переменную окружения
$this->view->code = $parameters['code'];
$this->variables['code'] = $parameters['code'];
// Запись кода ответа
http_response_code($parameters['code']);
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . 'index.html');
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');
return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . ($parameters['code'] ?? 'index') . '.html', $this->variables);
}
}

View File

@ -13,7 +13,7 @@ use mirzaev\site\account\controllers\core;
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class graph extends core
final class graph_controller extends core
{
/**
* Страница с графиком

View File

@ -13,7 +13,7 @@ use mirzaev\site\account\controllers\core;
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class hotline extends core
final class hotline_controller extends core
{
/**
* Страница с бегущей строкой

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core;
/**
* Контроллер основной страницы
*
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class index extends core
{
/**
* Главная страница
*
* @param array $parameters Параметры запроса
*/
public function index(array $parameters = []): ?string
{
// Инициализация узлов
$this->view->nodes = [
'account' => $this->view->render(DIRECTORY_SEPARATOR . 'nodes' . DIRECTORY_SEPARATOR . (isset($this->account) ? 'profile.html' : 'authentication.html'))
/* 'account' => $this->view->render(DIRECTORY_SEPARATOR . 'nodes' . DIRECTORY_SEPARATOR . (isset($this->account) ? 'profile.html' : 'connect.html')) */
];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'index.html');
}
}

View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core;
/**
* Контроллер основной страницы
*
* @package mirzaev\site\account\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']
];
// Инициализация бегущей строки
$this->variables['graph'] = [
'id' => $this->variables['request']['id'] ?? 'graph'
];
// Инициализация аттрибутов бегущей строки
$this->variables['graph']['attributes'] = [
];
// Инициализация элементов бегущей строки
$this->variables['graph']['elements'] = [
];
// Генерация представления
return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', $this->variables);
}
}

View File

@ -1,203 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers;
// Файлы проекта
use mirzaev\site\account\controllers\core,
mirzaev\site\account\controllers\traits\errors,
mirzaev\site\account\models\invite,
mirzaev\site\account\models\account;
// Встроенные библиотеки
use exception;
/**
* Контроллер сессии
*
* @package mirzaev\site\account\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class session extends core
{
use errors;
/**
* Записать входной псевдоним
*
* Проверяет существование аккаунта с этим входным псевдонимом
* и запоминает для использования в процессе аутентификации
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
*/
public function login(array $parameters = []): string
{
// Инициализация буфера ответа
$buffer = [];
// Инициализация реестра возвращаемых параметров
$return = explode(',', $parameters['return'], 50);
try {
// Проверка наличия обязательных параметров
if (empty($parameters['login'])) throw new exception('Необходимо передать входной псевдоним');
// Вычисление длины
$length = strlen($parameters['login']);
// Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Входной псевдоним не может быть пустым');
if ($length > 100) throw new exception('Входной псевдоним не может быть длиннее 100 символов');
// Поиск аккаунта
$account = account::read($parameters['login'], $this->errors['account']);
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'exist' => $buffer['exist'] = isset($account->instance),
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
if ($parameters['remember'] === '1') $this->session->remember('account.identification.login', $parameters['login']);
} catch (exception $e) {
// Запись в журнал ошибок
$this->errors['session'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode($buffer);
}
/**
* Записать пароль
*
* Проверяет на соответствие требованиям
* и запоминает для использования в процессе аутентификации
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
*/
public function password(array $parameters = []): string
{
// Инициализация буфера ответа
$buffer = [];
// Инициализация реестра возвращаемых параметров
$return = explode(',', $parameters['return'], 50);
try {
// Проверка наличия обязательных параметров
if (empty($parameters['password'])) throw new exception('Необходимо передать пароль');
// Вычисление длины
$length = strlen($parameters['password']);
// Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Пароль не может быть пустым');
if ($length > 300) throw new exception('Пароль не может быть длиннее 300 символов');
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'verify' => $buffer['verify'] = true,
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
if ($parameters['remember'] === '1') throw new exception('Запоминать пароль не безопасно');
} catch (exception $e) {
// Запись в журнал ошибок
$this->errors['session'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись реестра ошибок в буфер ответа
if (in_array('verify', $return, true)) $buffer['verify'] = false;
}
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode($buffer);
}
/**
* Записать код приглашения
*
* Проверяет существование приглашения с этим кодом
* и запоминает для использования в процессе регистрации
*
* @param array $parameters Параметры запроса
*
* @return string JSON-документ с запрашиваемыми параметрами
*/
public function invite(array $parameters = []): string
{
// Инициализация буфера ответа
$buffer = [];
// Инициализация реестра возвращаемых параметров
$return = explode(',', $parameters['return'], 50);
try {
// Проверка наличия обязательных параметров
if (empty($parameters['invite'])) throw new exception('Необходимо передать ключ приглашения');
// Вычисление длины
$length = strlen($parameters['invite']);
// Проверка параметров на соответствование требованиям
if ($length === 0) throw new exception('Получен пустой ключ приглашения');
// Поиск приглашения
$invite = invite::read($parameters['invite'], $this->errors['session']);
// Генерация ответа по запрашиваемым параметрам
foreach ($return as $parameter) match ($parameter) {
'exist' => $buffer['exist'] = isset($invite->instance),
// from временное решение пока не будет разработана система сессий
'from' => $return['from'] = ['login' => 'mirzaev'] ?? $invite->from(),
'errors' => null,
default => throw new exception("Параметр не найден: $parameter")
};
if ($parameters['remember'] === '1') $this->session->remember('account.registration.invite', $parameters['invite']);
} catch (exception $e) {
// Запись в журнал ошибок
$this->errors['session'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Запись реестра ошибок в буфер ответа
if (in_array('errors', $return, true)) $buffer['errors'] = self::parse_only_text($this->errors);
// Запись заголовка ответа
header('Content-Type: application/json');
return json_encode($buffer);
}
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\controllers\traits;
/**
* Заготовка для обработки ошибок
*
* @package mirzaev\site\account\controllers\traits
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
trait errors
{
private static function parse_only_text(array $errors): array
{
// Инициализация буфера вывода
$buffer = [];
foreach ($errors as $offset => $error) {
// Перебор ошибок
// Проверка на вложенность и запись в буфер вывода (вход в рекурсию)
if (isset($error['text'])) $buffer[] = $error['text'];
else if (is_array($error)) $buffer[$offset] = static::parse_only_text($error);
}
return $buffer;
}
}

View File

@ -1,222 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\models;
// Файлы проекта
use mirzaev\site\account\models\vk;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use exception;
/**
* Модель аккаунта
*
* @package mirzaev\site\account\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class account extends core
{
/**
* Коллекция
*/
public const COLLECTION = 'account';
/**
* Инстанция в базе данных
*/
public ?_document $instance;
/**
* Прочитать
*
* @param string $login Входной псевдоним
* @param array &$errors Журнал ошибок
*
* @return ?self Инстанция аккаунта, если найден
*/
public static function read(string $login, array &$errors = []): ?self
{
try {
if (collection::init(static::$db->session, self::COLLECTION)) {
// Инициализирована коллекция
// Инициализация инстанции аккаунта
$instance = new self;
// Поиск аккаунта
$instance->instance = collection::search(
static::$db->session,
sprintf(
<<<AQL
FOR d IN %s
FILTER d.login == '%s'
RETURN d
AQL,
self::COLLECTION,
$login
)
);
return $instance;
}
throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
/**
* Создать
*
* @param array &$errors Журнал ошибок
*
* @return ?_document Инстанция аккаунта, если удалось создать
*/
public static function create(array &$errors = []): ?_document
{
try {
if (collection::init(static::$db->session, self::COLLECTION)) {
// Инициализирована коллекция
// Запись аккаунта в базу данных
$_id = document::write(static::$db->session, self::COLLECTION);
if ($account = collection::search(static::$db->session, sprintf(
<<<AQL
FOR d IN %s
FILTER d._id == '$_id'
RETURN d
AQL,
self::COLLECTION
))) {
// Найден созданный аккаунт
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;
}
/**
* Связь аккаунта с аккаунтом ВКонтакте
*
* @param _document $account Инстанция аккаунта
* @param _document $vk Инстанция аккаунта ВКонтакте
* @param array &$errors Журнал ошибок
*
* @return bool Статус выполнения
*/
public static function connect(_document $account, _document $vk, array &$errors = []): bool
{
try {
if (
collection::init(static::$db->session, self::COLLECTION)
&& collection::init(static::$db->session, vk::COLLECTION)
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true)
) {
// Инициализированы коллекции
if (document::write(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, [
'_from' => $account->getId(),
'_to' => $vk->getId()
])) {
// Создано ребро: account -> vk
return true;
} else throw new exception('Не удалось создать ребро: account -> vk');
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return false;
}
/**
* Поиск связанного аккаунта ВКонтакте
*
* @param _document $account Инстанция аккаунта
* @param array &$errors Журнал ошибок
*
* @return ?_document Инстанция аккаунта, если удалось найти
*/
public static function vk(_document $account, array &$errors = []): ?_document
{
try {
if (
collection::init(static::$db->session, self::COLLECTION)
&& collection::init(static::$db->session, vk::COLLECTION)
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true)
) {
// Инициализирована коллекция
if ($vk = 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,
vk::COLLECTION,
self::COLLECTION . '_edge_' . vk::COLLECTION,
$account->getId()
))) {
// Найден аккаунт ВКонтакте
return $vk;
} 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;
}
}

View File

@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\models;
// Файлы проекта
use mirzaev\site\account\models\vk_model as vk;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use exception;
/**
* Модель регистрации, аутентификации и авторизации
*
* @package mirzaev\site\account\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class account_model extends core
{
/**
* Коллекция
*/
public const COLLECTION = 'account';
/**
* Создать
*
* @param array &$errors Журнал ошибок
*
* @return ?_document Инстанция аккаунта, если удалось создать
*/
public static function create(array &$errors = []): ?_document
{
try {
if (collection::init(static::$db->session, self::COLLECTION)) {
// Инициализирована коллекция
// Запись аккаунта в базу данных
$_id = document::write(static::$db->session, self::COLLECTION);
if ($account = collection::search(static::$db->session, sprintf(
<<<AQL
FOR d IN %s
FILTER d._id == '$_id'
RETURN d
AQL,
self::COLLECTION
))) {
// Найден созданный аккаунт
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;
}
/**
* Связь аккаунта с аккаунтом ВКонтакте
*
* @param _document $account Инстанция аккаунта
* @param _document $vk Инстанция аккаунта ВКонтакте
* @param array &$errors Журнал ошибок
*
* @return bool Статус выполнения
*/
public static function connect(_document $account, _document $vk, array &$errors = []): bool
{
try {
if (
collection::init(static::$db->session, self::COLLECTION)
&& collection::init(static::$db->session, vk::COLLECTION)
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true)
) {
// Инициализированы коллекции
if (document::write(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, [
'_from' => $account->getId(),
'_to' => $vk->getId()
])) {
// Создано ребро: account -> vk
return true;
} else throw new exception('Не удалось создать ребро: account -> vk');
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return false;
}
/**
* Поиск связанного аккаунта ВКонтакте
*
* @param _document $account Инстанция аккаунта
* @param array &$errors Журнал ошибок
*
* @return ?_document Инстанция аккаунта, если удалось найти
*/
public static function vk(_document $account, array &$errors = []): ?_document
{
try {
if (
collection::init(static::$db->session, self::COLLECTION)
&& collection::init(static::$db->session, vk::COLLECTION)
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true)
) {
// Инициализирована коллекция
if ($vk = 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,
vk::COLLECTION,
self::COLLECTION . '_edge_' . vk::COLLECTION,
$account->getId()
))) {
// Найден аккаунт ВКонтакте
return $vk;
} 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;
}
}

View File

@ -23,11 +23,6 @@ class core extends model
*/
public const SETTINGS = '../settings/arangodb.php';
/**
* Постфикс
*/
public string $postfix = '';
/**
* Соединение с базой данных
*/

View File

@ -1,159 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\models\generators;
// Файлы проекта
use mirzaev\site\account\models\core;
// Генератор по модели цепи Маркова
use Scriptura\Markov\Chain as chain,
Scriptura\Markov\Generator as generator,
Scriptura\Markov\RNG\RandomIntRNG as rng;
// Встроенные библиотеки
use exception;
/**
* Модель генератора паролей
*
* @package mirzaev\site\account\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class password extends core
{
/**
* Сгенерировать мнемонический пароль
*
* @param int $length Длина (количество слов)
* @param array &$errors Журнал ошибок
*
* @return ?string Пароль
*/
public static function mnemonic(int $length = 4, array &$errors = []): ?string
{
try {
preg_match_all(
'/\w+/um',
mb_convert_encoding(
<<<TEXT
Коридор был пуст. Мгновение я постоял перед закрытой дверью, прислушиваясь. Стены, наверно, были тонкими, снаружи сквозь них проникал плач ветра. На двери, немного наискось, висел небрежно прикрепленный прямоугольный кусок пластыря с карандашной надписью «Человек». Неразборчиво нацарапанное слово вызвало у меня желание вернуться к Снауту, но я понял, что это невозможно.
Нелепое предостережение все еще звучало в ушах. Тихо, как будто бессознательно скрываясь от невидимого наблюдателя, я вернулся в круглую камеру с пятью дверьми. На трех из них висели таблички: «Д-р Гибарян», «Д-р Снаут», «Д-р Сарториус». На четвертой таблички не было. Поколебавшись, я нажал ручку. Пока дверь медленно открывалась, у меня появилось граничащее с уверенностью ощущение, что в комнате кто-то есть. Я вошел внутрь.
В комнате никого не было. Выпуклое окно глядело на океан, который жирно блестел под солнцем, как будто с волн стекало красное масло. Пурпурный отблеск заливал комнату, похожую на корабельную каюту. С одной стороны ее находились полки с книгами и прикрепленная вертикально к стене кровать в карданной подвеске. С другой было очень много шкафчиков. Между ними в никелированных рамках висели фотоснимки планеты. В металлических захватах торчали колбы и пробирки, заткнутые ватой. Под окном в два ряда громоздились белые эмалированные ящики с инструментами. В углах комнаты краны, вытяжной шкаф, холодильные установки, на полу стоял микроскоп, для него уже не было места на большом столе у окна.
Я обернулся и около входной двери увидел шкаф с открытыми дверцами до самого потолка. В нем висели комбинезоны, рабочие и защитные халаты, на полках белье, между голенищами противорадиационных сапог поблескивали алюминиевые баллоны для переносных кислородных аппаратов. Два аппарата с масками болтались на поручне поднятой кровати. Везде был тот же кое-как упорядоченный хаос.
Я втянул воздух и почувствовал слабый запах химических реактивов. Машинально поискал глазами вентиляционные решетки. Прикрепленные к ним полоски бумаги легонько колебались, показывая, что компрессоры работают, поддерживая нормальный обмен воздуха. Я перенес книги, аппараты и инструменты с двух кресел в углы, распихал все это как попало, и вокруг постели, между шкафом и полками, образовалось относительно пустое пространство. Потом подтянул вешалку, чтобы повесить на нее скафандр, и уже взялся за замки-молнии, но тут же их отпустил. Я никак не мог решиться снять скафандр, как будто от этого стал бы беззащитным. Еще раз я окинул взглядом комнату. Дверь была плотно закрыта, но замка в ней не было, и после недолгого колебания я припер ее двумя самыми тяжелыми ящиками.
Забаррикадировавшись так, я освободился от своей скрипящей оболочки. Узкое зеркало на внутренней поверхности шкафа отражало часть комнаты. Углом глаза я заметил какое-то движение, вскочил, но тут же понял, что это мое собственное отражение. Комбинезон под скафандром пропотел. Я сбросил его и толкнул шкаф. Он отъехал в сторону, и в нише за ним заблестели стены миниатюрной ванной комнаты. На полу, под душем, лежал довольно большой плоский ящик, который я с трудом втащил в комнату. Когда я опускал его на пол, крышка отскочила как на пружине, и я увидел отделения, набитые странными предметами. Ящик был полон страшно изуродованных инструментов из темного металла, немного похожих на те, которые лежали в шкафах. Все они никуда не годились, бесформенные, скрученные, оплавленные, словно вынесенные из пожара. Самым удивительным было то, что повреждения такого же характера были даже на керамитовых, то есть практически не плавящихся, рукоятках. Ни в одной лабораторной печи нельзя было получить температуру, при которой они бы плавились, разве что внутри атомного котла. Из кармана моего скафандра я достал портативный дозиметр, но черный цилиндрик молчал, когда я поднес его к обломкам.
На мне были только трусы и рубашка-сетка. Я скинул их на пол и пошел под душ. Вода принесла облегчение. Я изгибался под потоком твердых горячих струй, массировал тело, фыркал и делал все это как-то преувеличенно, как будто хотел вытравить из себя эту жуткую, внушающую подозрения неуверенность, охватившую станцию.
В шкафу я нашел легкий тренировочный костюм, который можно было носить под скафандром, переложил в карман все свое скромное имущество. Между листами блокнота я нащупал что-то твердое это был каким-то чудом попавший сюда ключ от моего земного жилья. Я повертел его в руках, не зная, что с ним делать, потом положил на стол. Мне пришло в голову, что неплохо бы иметь какое-нибудь оружие. Универсальный перочинный нож тут явно не годился, но ничего другого у меня не было, а я еще не дошел до такого состояния, чтобы искать ядерный излучатель или что-нибудь в этом роде. Я уселся на металлический стульчик, который стоял посредине пустого пространства, в отдалении от всех вещей. Мне хотелось побыть одному. С удовольствием я отметил, что у меня есть еще полчаса времени. Стрелки на двадцатичетырехчасовом циферблате показывали семь. Солнце заходило. Семь часов местного времени значит двадцать часов на борту «Прометея». На экранах Моддарда Солярис, наверно, уже уменьшился до размеров искорки и ничем не отличался от звезд. Но какое я имею отношение к «Прометею»? Я закрыл глаза. Стояла полная тишина, только в ванной капли воды глухо стучали по кафелю.
Гибарян мертв. Если я правильно понял Снаута, с момента его смерти прошло всего несколько часов.
Что сделали с его телом? Похоронили? Правда, здесь, на Солярисе, этого сделать нельзя. Некоторое время я обдумывал это, будто судьба мертвого была так уж важна. Поняв бессмысленность подобных размышлений, я встал и начал ходить по комнате, поддавая носком беспорядочно разбросанные книги. Потом поднял с пола фляжку из темного стекла, такую легкую, будто она была сделана из бумаги. Посмотрел сквозь нее в окно, в мрачно пламенеющие, затянутые грозным туманом последние лучи заката. Что со мной? Почему я занимаюсь какими-то глупостями, какой-то ненужной ерундой?
Я вздрогнул зажегся свет. Очевидно, фотоэлементы среагировали на наступающие сумерки. Я был полон ожидания, напряжение нарастало до такой степени, что мне уже действовало на нервы пустое пространство за спиной. С этим пора было кончать.
Я придвинул кресло к полкам, взял хорошо известный мне второй том старой монографии Хьюджеса и Эгла «История Соляриса» и начал его перелистывать, подперев толстый жесткий переплет коленом.
Солярис был открыт почти за сто лет до того, как я родился. Планета обращается вокруг двух солнц красного и голубого. В течение сорока с лишним лет к ней не приближался ни один космический корабль. В то время теория Гамова Шепли о невозможности зарождения жизни на планетах двойных звезд не вызывала сомнений. Орбиты таких планет непрерывно изменяются из-за непостоянства сил притяжения, вызванного взаимным обращением двух солнц.
Возникающие изменения гравитационного поля сокращают или растягивают орбиту планеты, и зародыши жизни, если они возникнут, будут уничтожены испепеляющим жаром или космическим холодом. Эти изменения происходят регулярно через каждые несколько миллионов лет, то есть в астрономическом или биологическом масштабе за очень короткий промежуток времени, так как эволюция требует сотен миллионов, если не миллиардов лет.
Солярис, по предварительным подсчетам, должен был за пятьсот тысяч лет приблизиться на расстояние половины астрономической единицы к своему красному солнцу,[1] а еще через миллион лет упасть в его раскаленную бездну. Но уже через несколько лет выяснилось, что орбита планеты не подвергается ожидаемым изменениям, вроде бы она постоянная, такая же постоянная, как орбиты планет нашей Солнечной системы.
Повторенные на этот раз с максимальной точностью наблюдения и вычисления лишь подтвердили то, что уже было известно: орбита Соляриса нестабильна. И если до этого Солярис был всего-навсего одной из нескольких сотен ежегодно открываемых планет, которым в статистических сборниках уделяют десяток строчек, где описываются элементы их движения, то теперь он немедленно перешел в ранг небесного тела, достойного самого пристального внимания.
Через четыре года после этого открытия планету облетела экспедиция Оттеншельда, который изучал Солярис с «Лаокоона» и двух вспомогательных космолетов.[2] Эта экспедиция носила характер предварительной разведки, тем более что высадиться на планету она не могла. Ученые запустили на экваториальные и полярные орбиты большое количество автоматических спутников-наблюдателей. Спутники должны были главным образом измерять гравитационные потенциалы. Кроме того, изучался океан, почти целиком покрывающий планету, и немногочисленные возвышающиеся над его поверхностью плоскогорья. Их общая площадь оказалась меньше, чем территория Европы, хотя Солярис имел диаметр на двадцать процентов больше земного. Эти лоскутки скалистой пустынной суши, разбросанные как попало, скопились главным образом в Южном полушарии. Был также определен состав атмосферы, лишенной кислорода, и произведены чрезвычайно точные измерения плотности планеты, альбедо и других астрономических показателей.[3] Как и ожидалось, ни на жалких клочках суши, ни в океане не удалось обнаружить никаких следов жизни.
В течение дальнейших десяти лет Солярис, теперь уже находящийся в центре внимания всех наблюдателей этого района, демонстрировал поразительную тенденцию к сохранению своей, вне всякого сомнения, гравитационно-нестабильной орбиты. Запахло было скандалом, так как вину за такие результаты наблюдений пытались возложить (заботясь о благе науки) то на определенных людей, то на вычислительные машины, которыми они пользовались.
Отсутствие средств задержало отправку специальной соляристической экспедиции еще на три года, вплоть до того момента, когда Шеннон, укомплектовавший команду, получил от института три космических корабля тоннажа «С» космодромного класса. За полтора года до прибытия экспедиции, которая вылетела с альфы Водолея, другая исследовательская группа по поручению института вывела на околосоляристическую орбиту автоматический сателлоид Луну-247. Этот сателлоид после трех последовательных реконструкций, отделенных друг от друга десятками лет, работает до сегодняшнего дня. Данные, которые он собрал, окончательно подтвердили выводы экспедиции Оттеншельда об активном характере движения океана.
Один корабль Шеннона остался на дальней орбите, два других после предварительных приготовлений сели у Южного полюса планеты на скалистом клочке суши, который занимает около тысячи квадратных километров. Работа экспедиции закончилась через восемнадцать месяцев и прошла очень успешно, за исключением одного несчастного случая, вызванного неисправностью аппаратуры. Однако ученые экспедиции раскололись на два враждующих лагеря. Предметом спора стал океан. На основании анализов он был признан органическим образованием (назвать его живым никто еще не решался). Но если биологи видели в нем организм весьма примитивный, что-то вроде одной чудовищно разросшейся жидкой клетки (они называли ее «добиологическая формация»), которая окружила всю планету студенистой оболочкой, местами глубиной в несколько километров, то астрономы и физики утверждали, что это должна быть чрезвычайно высокоорганизованная структура, сложностью своего строения превосходящая земные организмы, коль скоро она в состоянии активно влиять на форму планетной орбиты. Никакой иной причины, объясняющей стабилизацию Соляриса, открыто не было. Кроме того, планетофизики установили связь между определенными процессами, происходящими в плазменном океане, и локальными колебаниями гравитационного потенциала, которые зависели от океанического «обмена веществ».
Таким образом, физики, а не биологи выдвинули парадоксальную формулировку «плазматическая машина», имея в виду образование, в нашем понимании, возможно, и неодушевленное, но способное к целенаправленным действиям в астрономическом масштабе.
В этом споре, который за несколько недель втянул в свою орбиту все выдающиеся авторитеты, доктрина Гамова Шепли пошатнулась впервые за восемьдесят лет.
Некоторое время ее еще пытались защищать, утверждая, что океан ничего общего с жизнью не имеет, что он является даже не образованием пара или добиологическим, а всего лишь геологической формацией, по всей вероятности необычной, но способной лишь к стабилизации орбиты Соляриса посредством изменения силы тяжести; при этом ссылались на закон Ле Шателье.
TEXT,
'UTF-8'
),
$matches
);
// Инициализация цепочки
$chain = new chain(1);
// Обучение
$chain->learn($matches[0]);
// Инициализация генератора
$generator = new generator(new rng(222, 666), $chain);
// Генерация
$result = $generator->generate();
// Обрезка результата
for ($password = [], $choose = rand(0, count($result) - 20); isset($result[$choose]) && count($password) < $length; $choose++) $password[] = $result[$choose];
return implode(' ', $password);
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
/**
* Сгенерировать классический пароль
*
* @param int $length Длина (количество символов)
* @param array &$errors Журнал ошибок
*
* @return ?string Пароль
*/
public static function classic(int $length = 12, array &$errors = []): ?string
{
try {
// Инициализация реестра символов
$symbols = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
// Инициализация буфера пароля
$password = '';
// Определение максимальной длины
$max = mb_strlen($symbols, '8bit') - 1;
if ($max < 1) throw new Exception('Длина пароля должна быть не менее двух символов');
// Генерация пароля
for ($i = 0; $i < $length; ++$i) $password .= $symbols[random_int(0, $max)];
return $password;
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
}

View File

@ -1,90 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\models;
// Файлы проекта
use mirzaev\site\account\models\account;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use exception;
/**
* Модель приглашения
*
* @package mirzaev\site\account\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class invite extends core
{
/**
* Коллекция
*/
public const COLLECTION = 'invite';
/**
* Инстанция в базе данных
*/
public ?_document $instance;
/**
* Прочитать
*
* @param string $invite Ключ приглашения
* @param array &$errors Журнал ошибок
*
* @return ?self Инстанция приглашения, если оно найдено
*/
public static function read(string $invite, array &$errors = []): ?self
{
try {
if (collection::init(static::$db->session, self::COLLECTION)) {
// Инициализирована коллекция
// Инициализация инстанции приглашения
$instance = new self;
// Поиск приглашения
$instance->instance = collection::search(
static::$db->session,
sprintf(
<<<AQL
FOR d IN %s
FILTER d.key == '%s' && d.active == true
RETURN d
AQL,
self::COLLECTION,
$invite
)
);
return $instance;
}
throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в журнал ошибок
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
return null;
}
public function from(): ?account
{
return new account();
}
}

View File

@ -1,292 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\models;
// Файлы проекта
use mirzaev\site\account\models\account;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use exception;
/**
* Модель сессий
*
* @package mirzaev\site\account\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class session extends core
{
/**
* Коллекция
*/
public const COLLECTION = 'session';
/**
* Данные сессии из базы данных
*/
public _document $document;
/**
* Конструктор
*
* Инициализация сессии и запись в свойство $this->document
*
* @param ?string $hash Хеш сессии в базе данных
* @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии)
* @param array &$errors Журнал ошибок
*
* @return static Инстанция сессии
*/
public function __construct(?string $hash = null, ?int $expires = null, array &$errors = [])
{
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()
))) {
// Найдена сессия по хешу
// Запись в свойство
$this->document = $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()
))) {
// Найдена сессия по данным пользователя
// Запись в свойство
$this->document = $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)) {
// Записано обновление
// Запись в свойство
$this->document = $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()
];
}
}
public function __destruct()
{
// Закрыть сессию
}
/**
* Связь сессии с аккаунтом
*
* @param _document $account Инстанция аккаунта
* @param array &$errors Журнал ошибок
*
* @return bool Статус выполнения
*/
public function connect(_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' => $this->document->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 array &$errors Журнал ошибок
*
* @return ?_document Инстанция аккаунта, если удалось найти
*/
public function account(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,
$this->document->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;
}
/**
* Записать
*
* Записывает свойство в инстанцию документа сессии из базы данных
*
* @param string $name Название
* @param mixed $value Содержимое
*
* @return void
*/
public function __set(string $name, mixed $value = null): void
{
$this->document->{$name} = $value;
}
/**
* Прочитать
*
* Читает свойство из инстанции документа сессии из базы данных
*
* @param string $name Название
*
* @return mixed Данные свойства инстанции сессии или инстанции документа сессии из базы данных
*/
public function __get(string $name): mixed
{
return $this->document->{$name};
}
/**
* Проверить инициализированность
*
* Проверяет инициализированность свойства в инстанции документа сессии из базы данных
*
* @param string $name Название
*
* @return bool Свойство инициализировано?
*/
public function __isset(string $name): bool
{
return isset($this->document->{$name});
}
/**
* Удалить
*
* Деинициализировать свойство в инстанции документа сессии из базы данных
*
* @param string $name Название
*
* @return void
*/
public function __unset(string $name): void
{
unset($this->document->{$name});
}
/**
* Выполнить метод
*
* Выполнить метод в инстанции документа сессии из базы данных
*
* @param string $name Название
* @param array $arguments Аргументы
*/
public function __call(string $name, array $arguments = [])
{
if (method_exists($this, $name)) return $this->document->{$name}($arguments);
}
}

View File

@ -0,0 +1,213 @@
<?php
declare(strict_types=1);
namespace mirzaev\site\account\models;
// Файлы проекта
use mirzaev\site\account\models\account_model as account;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use exception;
/**
* Модель сессий
*
* @package mirzaev\site\account\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;
}
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace mirzaev\site\account\models;
// Файлы проекта
use mirzaev\site\account\models\account;
use mirzaev\site\account\models\account_model as account;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
@ -30,7 +30,7 @@ use stdClass;
* @package mirzaev\site\account\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class vk extends core
final class vk_model extends core
{
/**
* Коллекция

View File

@ -1,230 +1,54 @@
@keyframes glare {
2%,
100% {
left : 130%;
bottom : -200%;
width : 120px;
opacity: 0.7;
}
}
main {
z-index: 1000;
top: 20%;
position: relative;
height: unset;
display: flex;
flex-direction: unset;
justify-content: center;
align-items: unset;
}
section.panel {
z-index : 1000;
width : 400px;
position : absolute;
#authentication {
width : 25vw;
margin : 25vh auto;
padding : 70px 40px;
display : flex;
flex-direction: column;
flex-direction : column;
background-color: var(--background-light-1);
}
section.panel.medium {
width: 300px;
}
section.panel.small {
width: 220px;
}
section.panel#mnemonic {
margin-left: -570px;
}
section.panel#classic {
margin-left: 570px;
}
section.panel>section.body>ul {
margin: 0 5%;
padding: 0;
display: flex;
flex-direction: column;
gap: 4px;
list-style: square;
}
section.panel>section.body>ul>li {
font-size: 0.8rem;
word-break: break-word;
animation-duration : .35s;
animation-name : uprise;
animation-fill-mode : forwards;
animation-timing-function: cubic-bezier(.47,0,.74,.71);
}
section.panel>section.header {
z-index : 1000;
height : 50px;
display : flex;
justify-content: center;
align-items: end;
animation-duration: 120s;
border-radius : 3px 3px 0 0;
background-color : var(--background-above);
}
section#profile>section.header {
margin-left : -50px;
#authentication>section.header {
width : 100px;
height : 100px;
padding : 30px 0;
clip-path : url(#profile-header-mask);
margin : auto;
display : flex;
border-radius: 100%;
overflow : hidden;
}
section#profile>section.header>img.avatar {
z-index : 1500;
left : 6px;
top : 36px;
width : 88px;
height : 88px;
position : absolute;
#authentication>section {
margin: unset;
color : #fff;
}
#authentication>section.header>img {
width : 90%;
height : 90%;
margin : auto;
object-fit : cover;
border-radius : 100%;
cursor : pointer;
image-rendering : smooth;
box-shadow : 0px 0px 12px 0px rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.5);
-moz-box-shadow : 0px 0px 12px 0px rgba(0, 0, 0, 0.5);
box-shadow : 1px 0px 12px 0px rgba(0, 0, 0, 1);
-webkit-box-shadow: 1px 0px 12px 0px rgba(0, 0, 0, 1);
-moz-box-shadow : 1px 0px 12px 0px rgba(0, 0, 0, 1);
}
section#profile>section.header>img.avatar:hover {
left : 0;
top : 30px;
width : 100px;
height : 100px;
box-shadow : 0px 0px 8px 0px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.3);
-moz-box-shadow : 0px 0px 8px 0px rgba(0, 0, 0, 0.3);
#authentication>:is(b, a) {
margin-left: 15%;
}
section#profile>section.header>img.cover {
z-index : -5000;
left : -50px;
top : 0;
position : absolute;
width : calc(100% + 100px);
height : 100%;
object-position: 0px 30%;
object-fit : cover;
clip-path : polygon(50px 0, calc(100% - 50px) 0, calc(100% - 50px) 100%, 50px 100%);
border-radius : 0 0 3px 3px;
background : var(--background-above);
#authentication>b {
margin-top: 30px;
font-size : 1.2em;
}
section#profile>section.header>div.glare {
z-index : 3000;
left : -30px;
top : -300px;
width : 30px;
height : 400%;
position : absolute;
rotate : 25deg;
opacity : 0.2;
filter : unset;
pointer-events : none;
animation-name : glare;
animation-duration : 32s;
animation-delay : 2s;
animation-fill-mode : forwards;
animation-timing-function: linear;
background-color : #fff;
#authentication>ul {
margin-left: 25%;
}
section#profile>section.header>div {
animation-duration: 80s;
}
section#profile>section.header>a {
margin : auto;
width : 100%;
margin-left : 110px;
padding-bottom: 0.5ex;
white-space : nowrap;
overflow-x : hidden;
text-overflow : ellipsis;
font-size : 1.3em;
font-weight : bold;
color : var(--text-inverse);
text-shadow : 0 0 8px #00000080;
}
section.panel>section.header>:is(h1, h2, h3) {
margin-bottom: unset;
}
section.panel>section.body {
padding : 20px 30px;
gap : 10px;
display : flex;
flex-direction : column;
border-radius : 0 0 3px 3px;
background-color : var(--background-above);
}
section#profile>section.body>ul {
margin : unset;
margin-left : 10%;
}
section#profile>section.body ul ul {
#authentication ul ul {
padding-top: 1ex;
}
section#profile>section.body ul li:not(:last-child) {
#authentication ul li:not(:last-child) {
margin-bottom: 1ex;
}
section#profile>section.body div.buttons {
margin-top: 10px;
display: flex;
}
section#profile>section.body div.buttons>button {
padding : 1ex 2ex;
cursor : pointer;
border-radius : 3px;
font-size : 0.9em;
background-color: unset;
}
section#profile>section.body div.buttons>button:hover {
color: var(--text-hover);
}
section#profile>section.body div.buttons>button:active {
color : var(--text-active);
transition: unset;
}
section#profile>section.body div.buttons>button:first-of-type {
margin-left : auto;
margin-right: 5%;
}
section#profile>section.body div.buttons>button:last-of-type {
margin-right: auto;
}
section#profile>section.body div.buttons>button.accept {
padding : 1ex 5ex;
color : var(--text-inverse);
background-color: #63954d;
}
section#profile>section.body div.buttons>button.accept:hover {
color : var(--text-inverse-above);
background-color: #6fa259;
}
section#profile>section.body div.buttons>button.accept:active {
background-color: #63954d;
}

View File

@ -1,23 +0,0 @@
@keyframes input-error {
0%,
20% {
background-color: var(--input-error);
}
50%,
100% {
background-color: var(--background-above-1);
}
}
@keyframes uprise {
0% {
opacity: 0;
filter: blur(2px);
}
100% {
opacity: 1;
filter: blur(0px);
}
}

View File

@ -1,139 +0,0 @@
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-Hair.woff2') format('woff2'), url('/fonts/fira/FiraSans-Hair.woff') format('woff');
font-weight: 100;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-HairItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-HairItalic.woff') format('woff');
font-weight: 100;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-UltraLight.woff2') format('woff2'), url('/fonts/fira/FiraSans-UltraLight.woff') format('woff');
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-UltraLightItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-UltraLightItalic.woff') format('woff');
font-weight: 200;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-Light.woff2') format('woff2'), url('/fonts/fira/FiraSans-Light.woff') format('woff');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-LightItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-LightItalic.woff') format('woff');
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-Regular.woff2') format('woff2'), url('/fonts/fira/FiraSans-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-Italic.woff2') format('woff2'), url('/fonts/fira/FiraSans-Italic.woff') format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraMono-Medium.woff2') format('woff2'), url('/fonts/fira/FiraMono-Medium.woff') format('woff');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-MediumItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-MediumItalic.woff') format('woff');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-SemiBold.woff2') format('woff2'), url('/fonts/fira/FiraSans-SemiBold.woff') format('woff');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-SemiBoldItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-SemiBoldItalic.woff') format('woff');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-Bold.woff2') format('woff2'), url('/fonts/fira/FiraSans-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-BoldItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-BoldItalic.woff') format('woff');
font-weight: 700;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-ExtraBold.woff2') format('woff2'), url('/fonts/fira/FiraSans-ExtraBold.woff') format('woff');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-ExtraBoldItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-ExtraBoldItalic.woff') format('woff');
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-Heavy.woff2') format('woff2'), url('/fonts/fira/FiraSans-Heavy.woff') format('woff');
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: Fira;
src: url('/fonts/fira/FiraSans-HeavyItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-HeavyItalic.woff') format('woff');
font-weight: 900;
font-style: italic;
}
@font-face {
font-family: Fira Mono;
src: url('/fonts/fira/FiraMono-Regular.woff2') format('woff2'), url('/fonts/fira/FiraMono-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: Fira Mono;
src: url('/fonts/fira/FiraMono-Bold.woff2') format('woff2'), url('/fonts/fira/FiraMono-Bold.woff') format('woff');
font-weight: 600;
font-style: normal;
}

View File

@ -1,63 +0,0 @@
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-regular.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-regular.woff?sha=3114f1256') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-bold.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-bold.woff?sha=3114f1256') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-italic.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-italic.woff?sha=3114f1256') format('woff');
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-bolditalic.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-bolditalic.woff?sha=3114f1256') format('woff');
font-weight: 700;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-regular-subset.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-regular-subset.woff?sha=3114f1256') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-bold-subset.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-bold-subset.woff?sha=3114f1256') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-italic-subset.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-italic-subset.woff?sha=3114f1256') format('woff');
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-bolditalic-subset.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-bolditalic-subset.woff?sha=3114f1256') format('woff');
font-weight: 700;
font-style: italic;
font-display: swap;
}

View File

@ -1,4 +1,9 @@
@keyframes red {
from {
left: -250%;
top : -300%;
}
25% {
left: -240%;
top : -130%;
@ -14,13 +19,18 @@
top : -230%;
}
100% {
to {
left: -250%;
top : -300%;
}
}
@keyframes blue {
from {
left: -280%;
top : -80%;
}
25% {
left: -180%;
top : -100%;
@ -36,13 +46,18 @@
top : -300%;
}
100% {
to {
left: -280%;
top : -80%;
}
}
@keyframes green {
from {
left: -180%;
top : -100%;
}
25% {
left: -120%;
top : -250%;
@ -58,13 +73,17 @@
top : -80%;
}
100% {
to {
left: -180%;
top : -100%;
}
}
@keyframes wrap-background {
@keyframes background {
from {
background-color: #ff5c5c;
}
25% {
background-color: #9395ff;
}
@ -77,56 +96,47 @@
background-color: #534eff;
}
100% {
to {
background-color: #ff5c5c;
}
}
:is(div, section).gradient {
#authentication>section.header.gradient {
position : relative;
overflow : hidden;
animation-duration : 30s;
animation-name : wrap-background;
animation-name : background;
animation-iteration-count: infinite;
background-repeat : no-repeat;
animation-timing-function: ease-in-out;
background-color : #ff5c5c;
}
:is(div, section).gradient>div:not(.gradient) {
#authentication>section.header.gradient>div {
z-index : -1000;
width : 500%;
height : 500%;
position : absolute;
pointer-events : none;
filter : blur(200px);
animation-duration : 12s;
background-repeat : no-repeat;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
:is(div, section).gradient>div.red {
left : -250%;
top : -300%;
#authentication>section.header.gradient>div.red {
animation-name: red;
background-image: radial-gradient(circle,
rgba(255, 25, 25, 1) 0%,
rgba(0, 0, 0, 0) 35%);
}
:is(div, section).gradient>div.blue {
left : -280%;
top : -80%;
#authentication>section.header.gradient>div.blue {
animation-name: blue;
background-image: radial-gradient(circle,
rgba(25, 25, 255, 0.6) 0%,
rgba(0, 0, 0, 0) 35%);
}
:is(div, section).gradient>div.green {
left : -180%;
top : -100%;
#authentication>section.header.gradient>div.green {
animation-name: green;
background-image: radial-gradient(circle,
rgba(25, 255, 25, 1) 0%,

View File

@ -1,245 +0,0 @@
@keyframes node-select {
from {
outline-offset: 0px;
}
to {
outline : 2px solid var(--grey-light);
outline-offset: 10px;
}
}
@keyframes node-select-revert {
from {
outline : 2px solid var(--grey-light);
outline-offset: 10px;
}
to {
outline-offset: 0px;
}
}
@keyframes description-select {
from {
outline-offset: 0px;
}
to {
outline : 2px solid var(--grey);
outline-offset: 10px;
}
}
@keyframes description-select-revert {
from {
outline : 2px solid var(--grey);
outline-offset: 10px;
}
to {
outline-offset: 0px;
}
}
section.graph {
z-index : 0;
width : 100vw;
height : 100vh;
position : absolute;
transition: unset;
}
section.graph * {
transition: unset;
}
section.graph:active {
cursor: move;
}
section.graph article.node {
z-index : 500;
position : absolute;
display : flex;
cursor : grab;
border-radius : 100%;
background-color : var(--node-background);
}
section.graph article.node.animated {
animation-duration : 0.1s;
animation-name : node-select-revert;
animation-fill-mode : forwards;
}
section.graph article.node.animated:hover {
animation-duration : 0.1s;
animation-name : node-select;
animation-fill-mode : forwards;
}
section.graph article.node:active {
cursor : grabbing;
background-color: var(--node-background-important);
}
section.graph article.node>h4.title {
margin : auto;
text-align: center;
cursor : pointer;
}
section.graph article.node>div.description {
z-index : 1000;
position : absolute;
text-align : justify;
text-align-last : center;
border-radius : 100%;
overflow : hidden;
background-color: var(--node-background-completed);
}
/* section.graph article.node>div.description.animated {
animation-duration : 0.1s;
animation-name : description-select-revert;
animation-fill-mode : forwards;
}
section.graph article.node>div.description.animated:hover {
animation-duration : 0.1s;
animation-name : description-select;
animation-fill-mode : forwards;
} */
section.graph article.node * {
transition: 0.1s ease-in;
}
section.graph article.node>div.description>span.wrapper {
width : 50%;
height : 100%;
shape-margin: 15px;
}
section.graph article.node>div.description>span.left.wrapper {
float : left;
shape-outside: polygon(100% 0%, 0% 0%, 0% 100%, 100% 100%, 68% 98%, 38% 90%, 10% 72%, 0% 50%, 10% 28%, 20% 20%, 100% 20%);
}
section.graph article.node>div.description>span.right.wrapper {
float : right;
shape-outside: polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%, 0% 20%, 82% 20%, 90% 28%, 100% 50%, 90% 72%, 60% 90%, 32% 98%);
}
section.graph article.node>div.description>p {
z-index : 1500;
position : relative;
margin : 0;
opacity : 0;
word-break: break-all;
color : var(--text-inverse);
}
section.graph article.node>div.description:hover>p {
opacity: 1;
}
section.graph article.node>div.description>a.source {
z-index : 2000;
top : calc(20% - 1.3em + (1em - 1.3ex));
left : 0;
position : absolute;
font-weight: bold;
font-size : 1.3em;
opacity : 0;
}
section.graph article.node.white>div.description>a.source {
color: var(--text-inverse);
}
section.graph article.node.white>div.description>a.source:active {
color: var(--text-inverse-active);
}
section.graph article.node.white>div.description>a.source:hover {
color: var(--text-inverse-hover);
}
section.graph article.node.red>div.description>a.source {
color: var(--text-red);
}
section.graph article.node.red>div.description>a.source:active {
color: var(--text-red-active);
}
section.graph article.node.red>div.description>a.source:hover {
color: var(--text-red-hover);
}
section.graph article.node>div.description>a.source.red:active {
color: var(--text-red-active);
}
section.graph article.node>div.description:hover>a.source {
opacity: 1;
}
section.graph article.node>div.description>a.source:visited ::after {
content : '';
width : 100%;
height : 100%;
background-color: var(--node-background-completed);
}
section.graph article.node>div.description>img.cover {
left : 0;
top : 0;
position : absolute;
width : 100%;
height : 100%;
object-fit : cover;
pointer-events: none;
filter : unset;
}
section.graph article.node>div.description:hover>img.cover {
filter: blur(5px) brightness(0.5);
}
section.graph article.node>i.close {
z-index : -2000;
top : -10%;
right : -10%;
position : absolute;
scale : 0;
opacity : 0;
cursor : pointer;
color : var(--text);
transition: 0.2s ease-out;
}
section.graph article.node>i.close:hover {
scale : 1.4 !important;
color : var(--text-hover);
transition: 0.1s ease-in;
}
section.graph article.node>i.close:active {
scale : 1.2 !important;
color : var(--text-active);
transition: unset;
}
section.graph svg.connection {
z-index : -500;
position: absolute;
width : 100%;
height : 100%;
}
section.graph svg.connection>line {
stroke: var(--connection);
}

View File

@ -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;
}

View File

@ -1,29 +0,0 @@
.icon.close {
box-sizing : border-box;
position : relative;
display : block;
transform : scale(var(--ggs, 1));
width : 22px;
height : 22px;
border : 2px solid transparent;
border-radius: 40px
}
.icon.close::after,
.icon.close::before {
content : "";
display : block;
box-sizing : border-box;
position : absolute;
width : 16px;
height : 2px;
background : currentColor;
transform : rotate(45deg);
border-radius: 5px;
top : 8px;
left : 1px
}
.icon.close::after {
transform: rotate(-45deg)
}

View File

@ -1,33 +0,0 @@
i.arrow.right {
box-sizing: border-box;
position: relative;
display: block;
width: 22px;
--height: 22px;
height: var(--height);
}
i.arrow.right::after,
i.arrow.right::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
right: 3px;
}
i.arrow.right::after {
width: 8px;
height: 8px;
border-top: 2px solid;
border-right: 2px solid;
transform: rotate(45deg);
bottom: 7px;
}
i.arrow.right::before {
width: 16px;
height: 2px;
bottom: 10px;
background: currentColor;
}

View File

@ -1,48 +0,0 @@
i.keyhole,
i.keyhole::after,
i.keyhole::before {
display: block;
box-sizing: border-box;
border-radius: 20px;
}
i.keyhole {
--width: 20px;
--height: 20px;
position: relative;
width: 20px;
height: 20px;
border: 2px solid;
}
i.keyhole::after,
i.keyhole::before {
position: absolute;
content: '';
}
i.keyhole::before {
left: 5px;
top: 3px;
width: 6px;
height: 6px;
border: 2px solid;
}
i.keyhole::after {
left: 7px;
bottom: 3px;
width: 2px;
height: 5px;
background: currentColor;
}
label>i.keyhole:first-child {
left: 7px;
scale: .9;
border: 2.1px solid;
}
i.keyhole+input {
padding-left: 34px;
}

View File

@ -1,28 +0,0 @@
i.lock {
--width: 12px;
--height: 11px;
position: relative;
margin-top: -12px;
width: 12px;
height: 11px;
display: block;
box-sizing: border-box;
border: 2px solid;
border-top-right-radius: 50%;
border-top-left-radius: 50%;
border-bottom: transparent;
}
i.lock::after {
left: -4px;
top: 9px;
position: absolute;
width: 16px;
height: 10px;
display: block;
box-sizing: border-box;
content: '';
box-shadow: 0 0 0 2px;
border-radius: 2px;
border: 2px solid transparent;
}

View File

@ -1,30 +0,0 @@
i.mail,
i.mail::after {
--width: 18px;
--height: 14px;
height: 14px;
display: block;
box-sizing: border-box;
border: 2px solid;
}
i.mail {
position: relative;
width: 18px;
overflow: hidden;
border-radius: 2px;
}
i.mail::after {
position: absolute;
left: 0;
bottom: 3px;
width: 14px;
transform: rotate(-45deg);
content: "";
border-radius: 3px;
}
i.mail+input {
padding-left: 36px;
}

View File

@ -1,34 +0,0 @@
i.nametag {
--width: 6px;
--height: 6px;
box-sizing: border-box;
position: relative;
width: 6px;
height: 6px;
display: block;
border: 2px solid;
}
i.nametag::before {
left: -5px;
top: -5px;
position: absolute;
width: 12px;
height: 12px;
display: block;
box-sizing: border-box;
content: '';
box-shadow:
-5px -5px 0 -3px,
5px 5px 0 -3px,
5px -5px 0 -3px,
-5px 5px 0 -3px;
}
label>i.nametag:first-child {
left: 13px;
}
i.nametag+input {
padding-left: 32px;
}

View File

@ -1,53 +0,0 @@
i.user.add {
--width: 20px;
--height: 18px;
width: 20px;
height: 18px;
display: block;
box-sizing: border-box;
background:
linear-gradient(
to left,
currentColor 8px,
transparent 0)
no-repeat 14px 6px/6px 2px,
linear-gradient(
to left,
currentColor 8px,
transparent 0)
no-repeat 16px 4px/2px 6px;
}
i.user.add::after,
i.user.add::before {
content: '';
position: absolute;
display: block;
box-sizing: border-box;
border: 2px solid
}
i.user.add::before {
left: 2px;
top: 0;
width: 8px;
height: 8px;
border-radius: 30px;
}
i.user.add::after {
top: 9px;
width: 12px;
height: 9px;
border-bottom: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
label>i.user.add:first-child {
left: 9px;
}
i.user.add+input {
padding-left: 37px;
}

View File

@ -1,11 +1,8 @@
@import url('/fonts/commissioner.ttf');
@import url('/fonts/comissioner.ttf');
@media (prefers-color-scheme: light) {
:root {
--background-above-1 : #fff;
--background-above : #fff6f6;
--background : #e8dada;
--background-below : #d7c5c5;
--background : #eee;
--background-inverse : #221e1e;
--background-inverse-dark : #120f0f;
--node-background-important: #c3eac3;
@ -13,63 +10,54 @@
--node-background : #bdb;
--connection : #b2b7b2;
--connection-completed : #d1d1d1;
--text : #151313;
--text-hover : #463e3e;
--text-active : #0e0e0e;
--text-inverse-above : #fff;
--text-inverse : #efefef;
--text-inverse-below : #d0d0d0;
--text : #131313;
--text-hover : #292929;
--text-active : #0c0c0c;
--text-inverse : #e6e6e6;
--text-inverse-hover : #fff;
--text-inverse-active : #d0d0d0;
--text-red : #f8a2a2;
--text-red-hover : #ffbcbc;
--text-red-active : #e69191;
--link : #3c76ff;
--link-hover : #6594ff;
--link-active : #3064dd;
}
}
@media (prefers-color-scheme: dark) {
:root {
--background-above-1: #322d2d;
--background-above : #2b2525;
--background-light-3: #403939;
--background-light-2: #322d2d;
--background-light-1: #2b2525;
--background-light : #252020;
--background : #221e1e;
--background-below : #121010;
--block-background : ;
--node-background : #221e1e;
--text : #e6e6e6;
--text-hover : #fff;
--text-active : #d0d0d0;
--text-inverse : #020202;
--text-inverse : 'dark';
--red-light-1 : #dc4343;
--red-light : #bf3737;
--red : #a43333;
--red-dark : #8d2a2a;
--input-error : #6c2424;
}
}
@keyframes page-background-gradient {
25% {
left: -350%;
top : 0%;
}
50% {
left: 0%;
top : 0%;
}
75% {
left: 0%;
top : -350%;
}
to {
left: -350%;
top : -350%;
}
}
:root {
--link : #3c76ff;
--link-hover : #6594ff;
--link-active: #3064dd;
--grey-light: #c0c0c0;
--grey : #858585;
--grey-dark : #565656;
}
* {
text-decoration: none;
outline : none;
border : none;
color : var(--text);
font-family : 'Commissioner', sans-serif;
transition : 0.1s ease-out;
}
.unselectable {
@ -81,28 +69,6 @@
user-select : none;
}
.hidden {
display: none !important;
opacity: 0;
}
* {
text-decoration: none;
outline : none;
border : none;
color : var(--text);
font-family : Fira, sans-serif;
transition : 0.1s ease-out;
}
pre, code {
font-family: Hack, monospace;
}
button {
cursor: pointer;
}
a {
color: var(--link);
}
@ -112,83 +78,16 @@ a:hover {
}
a:active {
color : var(--link-active);
transition: unset;
}
label {
position: relative;
height: 26px;
display: flex;
overflow: hidden;
border-radius: 2px;
}
label>i:first-child {
left: 8px;
top: calc((26px - var(--height)) / 2);
position: absolute !important;
margin: auto;
color: #8c7d7d;
}
label * {
/* color: var(--text-inverse); */
}
label>input {
width: 100%;
padding: 0 8px;
background-color: var(--background-above-1);
}
label>input+button {
background-color: var(--red);
}
i+input {
padding-left: 30px;
}
input.error {
animation-duration : 1s;
animation-name : input-error;
animation-fill-mode : forwards;
animation-timing-function: ease-in;
}
section.header>h1 {
font-size: 1.3rem;
line-height: 1.3rem;
}
section.header>:is(h2, h3) {
font-size: 1.1rem;
line-height: 1.1rem;
color: var(--link-active);
}
body {
height : 100vh;
margin : 0;
overflow : hidden;
background-color: var(--background);
}
body>div.background {
z-index : -50000;
left : -350%;
top : -350%;
width : 500%;
height : 500%;
position : absolute;
filter : blur(200px);
animation-duration : 15s;
animation-name : page-background-gradient;
animation-iteration-count: infinite;
background-repeat : no-repeat;
animation-timing-function: linear;
background-image : radial-gradient(circle, var(--background-above) 0%, rgba(0, 0, 0, 0) 100%);
}
aside {
z-index : 500;
grid-column: 1/ 4;
@ -277,12 +176,10 @@ header>nav {
}
main {
z-index : 1000;
height : 100%;
display : flex;
z-index: 1000;
height: 100%;
display: flex;
flex-direction: column;
justify-content : center;
align-items : center;
}
footer {

Some files were not shown because too many files have changed in this diff Show More