resolved #129, resolved #124, resolved #123, resolved #119, resolved #118, resolved #114, resolved #111

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2024-05-31 10:50:05 +07:00
parent a5b771a99a
commit 19a5dbd9b7
178 changed files with 4261 additions and 1682 deletions

View File

@ -30,16 +30,17 @@
}
],
"require": {
"php": "~8.2",
"ext-sodium": "~8.2.4",
"php": "~8.3",
"ext-sodium": "~8.3",
"mirzaev/minimal": "^2.0.x-dev",
"mirzaev/accounts": "~1.2.x-dev",
"mirzaev/arangodb": "^1.0.0",
"triagens/arangodb": "~3.9.x-dev",
"twig/twig": "^3.4",
"twig/twig": "^3.10",
"twig/extra-bundle": "^3.7",
"twig/intl-extra": "^3.7",
"phpoffice/phpspreadsheet": "^1.29"
"twig/intl-extra": "^3.10",
"phpoffice/phpspreadsheet": "^2.1",
"openswoole/core": "^22.1"
},
"require-dev": {
"phpunit/phpunit": "~9.5"

400
composer.lock generated
View File

@ -4,69 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "588f1020cbf90d4c8ed02b057592c14f",
"content-hash": "c0be2095032e176b9fb16a24e7a4d1a1",
"packages": [
{
"name": "ezyang/htmlpurifier",
"version": "v4.16.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
"reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
"shasum": ""
},
"require": {
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
},
"require-dev": {
"cerdic/css-tidy": "^1.7 || ^2.0",
"simpletest/simpletest": "dev-master"
},
"suggest": {
"cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
"ext-bcmath": "Used for unit conversion and imagecrash protection",
"ext-iconv": "Converts text to and from non-UTF-8 encodings",
"ext-tidy": "Used for pretty-printing HTML"
},
"type": "library",
"autoload": {
"files": [
"library/HTMLPurifier.composer.php"
],
"psr-0": {
"HTMLPurifier": "library/"
},
"exclude-from-classmap": [
"/library/HTMLPurifier/Language/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/ezyang/htmlpurifier/issues",
"source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0"
},
"time": "2022-09-18T07:06:19+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.7.0",
@ -728,17 +667,88 @@
"time": "2023-03-20T11:46:41+00:00"
},
{
"name": "phpoffice/phpspreadsheet",
"version": "1.29.0",
"name": "openswoole/core",
"version": "22.1.5",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0"
"url": "https://github.com/openswoole/core.git",
"reference": "06dae68fdac73341ccf565ecef388434bd893141"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fde2ccf55eaef7e86021ff1acce26479160a0fa0",
"reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0",
"url": "https://api.github.com/repos/openswoole/core/zipball/06dae68fdac73341ccf565ecef388434bd893141",
"reference": "06dae68fdac73341ccf565ecef388434bd893141",
"shasum": ""
},
"require": {
"ext-openswoole": ">=22.0",
"php": ">=7.4",
"psr/http-message": "^1.0 || ^2.0",
"psr/http-server-middleware": "^1.0.0"
},
"require-dev": {
"ext-curl": "*",
"ext-sockets": "*",
"friendsofphp/php-cs-fixer": "^3.6",
"openswoole/ide-helper": "^22.0",
"php-http/psr7-integration-tests": "^1.1",
"phpunit/phpunit": "^9.5"
},
"suggest": {
"ext-mysqli": "*",
"ext-pdo": "*",
"ext-redis": "Required to use redis database, and the required version is greater than or equal to 3.1.3"
},
"type": "library",
"autoload": {
"files": [
"src/Coroutine/functions.php"
],
"psr-4": {
"OpenSwoole\\Core\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "OpenSwoole Group",
"email": "hello@openswoole.com"
}
],
"description": "Openswoole core library",
"homepage": "https://openswoole.com",
"keywords": [
"http",
"http2",
"mqtt",
"openswoole",
"php",
"tcp",
"websocket"
],
"support": {
"docs": "https://openswoole.com/docs",
"issues": "https://github.com/openswoole/openswoole/issues",
"pull-request": "https://github.com/openswoole/openswoole/pulls",
"source": "https://github.com/openswoole/openswoole"
},
"time": "2023-12-10T19:02:13+00:00"
},
{
"name": "phpoffice/phpspreadsheet",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e",
"reference": "dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e",
"shasum": ""
},
"require": {
@ -755,25 +765,24 @@
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
"ezyang/htmlpurifier": "^4.15",
"maennchen/zipstream-php": "^2.1 || ^3.0",
"markbaker/complex": "^3.0",
"markbaker/matrix": "^3.0",
"php": "^7.4 || ^8.0",
"php": "^8.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
"dompdf/dompdf": "^1.0 || ^2.0",
"dompdf/dompdf": "^2.0",
"friendsofphp/php-cs-fixer": "^3.2",
"mitoteam/jpgraph": "^10.3",
"mpdf/mpdf": "^8.1.1",
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.0 || ^10.0",
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.7",
"tecnickcom/tcpdf": "^6.5"
},
@ -828,9 +837,9 @@
],
"support": {
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.0"
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.1.0"
},
"time": "2023-06-14T22:48:31+00:00"
"time": "2024-05-11T04:17:56+00:00"
},
{
"name": "psr/cache",
@ -1144,6 +1153,119 @@
},
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "psr/http-server-handler",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-server-handler.git",
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
"shasum": ""
},
"require": {
"php": ">=7.0",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Server\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP server-side request handler",
"keywords": [
"handler",
"http",
"http-interop",
"psr",
"psr-15",
"psr-7",
"request",
"response",
"server"
],
"support": {
"source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
},
"time": "2023-04-10T20:06:20+00:00"
},
{
"name": "psr/http-server-middleware",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-server-middleware.git",
"reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
"reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
"shasum": ""
},
"require": {
"php": ">=7.0",
"psr/http-message": "^1.0 || ^2.0",
"psr/http-server-handler": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Server\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP server-side middleware",
"keywords": [
"http",
"http-interop",
"middleware",
"psr",
"psr-15",
"psr-7",
"request",
"response"
],
"support": {
"issues": "https://github.com/php-fig/http-server-middleware/issues",
"source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2"
},
"time": "2023-04-11T06:14:47+00:00"
},
{
"name": "psr/log",
"version": "3.0.0",
@ -1619,16 +1741,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.3.0",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
@ -1637,7 +1759,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.4-dev"
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@ -1666,7 +1788,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.3.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
@ -1682,7 +1804,7 @@
"type": "tidelift"
}
],
"time": "2023-05-23T14:45:45+00:00"
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/error-handler",
@ -2377,25 +2499,25 @@
},
{
"name": "symfony/intl",
"version": "v6.3.2",
"version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
"reference": "1f8cb145c869ed089a8531c51a6a4b31ed0b3c69"
"reference": "dd12042707110995e2e7d80103f8d9928bea8621"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/intl/zipball/1f8cb145c869ed089a8531c51a6a4b31ed0b3c69",
"reference": "1f8cb145c869ed089a8531c51a6a4b31ed0b3c69",
"url": "https://api.github.com/repos/symfony/intl/zipball/dd12042707110995e2e7d80103f8d9928bea8621",
"reference": "dd12042707110995e2e7d80103f8d9928bea8621",
"shasum": ""
},
"require": {
"php": ">=8.1"
"php": ">=8.2"
},
"require-dev": {
"symfony/filesystem": "^5.4|^6.0",
"symfony/finder": "^5.4|^6.0",
"symfony/var-exporter": "^5.4|^6.0"
"symfony/filesystem": "^6.4|^7.0",
"symfony/finder": "^6.4|^7.0",
"symfony/var-exporter": "^6.4|^7.0"
},
"type": "library",
"autoload": {
@ -2403,7 +2525,8 @@
"Symfony\\Component\\Intl\\": ""
},
"exclude-from-classmap": [
"/Tests/"
"/Tests/",
"/Resources/data/"
]
},
"notification-url": "https://packagist.org/downloads/",
@ -2439,7 +2562,7 @@
"localization"
],
"support": {
"source": "https://github.com/symfony/intl/tree/v6.3.2"
"source": "https://github.com/symfony/intl/tree/v7.0.7"
},
"funding": [
{
@ -2455,20 +2578,20 @@
"type": "tidelift"
}
],
"time": "2023-07-20T07:43:09+00:00"
"time": "2024-04-18T09:29:19+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"shasum": ""
},
"require": {
@ -2482,9 +2605,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -2521,7 +2641,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
},
"funding": [
{
@ -2537,20 +2657,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
@ -2564,9 +2684,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -2604,7 +2721,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@ -2620,20 +2737,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
@ -2641,9 +2758,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -2687,7 +2801,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
},
"funding": [
{
@ -2703,7 +2817,7 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-php83",
@ -3516,25 +3630,25 @@
},
{
"name": "twig/intl-extra",
"version": "v3.7.1",
"version": "v3.10.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/intl-extra.git",
"reference": "4f4fe572f635534649cc069e1dafe4a8ad63774d"
"reference": "693f6beb8ca91fc6323e01b3addf983812f65c93"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/4f4fe572f635534649cc069e1dafe4a8ad63774d",
"reference": "4f4fe572f635534649cc069e1dafe4a8ad63774d",
"url": "https://api.github.com/repos/twigphp/intl-extra/zipball/693f6beb8ca91fc6323e01b3addf983812f65c93",
"reference": "693f6beb8ca91fc6323e01b3addf983812f65c93",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"symfony/intl": "^5.4|^6.0",
"twig/twig": "^2.7|^3.0"
"php": ">=7.2.5",
"symfony/intl": "^5.4|^6.4|^7.0",
"twig/twig": "^3.10"
},
"require-dev": {
"symfony/phpunit-bridge": "^5.4|^6.3"
"symfony/phpunit-bridge": "^6.4|^7.0"
},
"type": "library",
"autoload": {
@ -3564,7 +3678,7 @@
"twig"
],
"support": {
"source": "https://github.com/twigphp/intl-extra/tree/v3.7.1"
"source": "https://github.com/twigphp/intl-extra/tree/v3.10.0"
},
"funding": [
{
@ -3576,33 +3690,41 @@
"type": "tidelift"
}
],
"time": "2023-07-29T15:34:56+00:00"
"time": "2024-05-11T07:35:57+00:00"
},
{
"name": "twig/twig",
"version": "v3.6.1",
"version": "v3.10.3",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd"
"reference": "67f29781ffafa520b0bbfbd8384674b42db04572"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572",
"reference": "67f29781ffafa520b0bbfbd8384674b42db04572",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
"type": "library",
"autoload": {
"files": [
"src/Resources/core.php",
"src/Resources/debug.php",
"src/Resources/escaper.php",
"src/Resources/string_loader.php"
],
"psr-4": {
"Twig\\": "src/"
}
@ -3635,7 +3757,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.6.1"
"source": "https://github.com/twigphp/Twig/tree/v3.10.3"
},
"funding": [
{
@ -3647,7 +3769,7 @@
"type": "tidelift"
}
],
"time": "2023-06-08T12:52:13+00:00"
"time": "2024-05-16T10:04:27+00:00"
}
],
"packages-dev": [
@ -5393,8 +5515,8 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "~8.2",
"ext-sodium": "~8.2.4"
"php": "~8.3",
"ext-sodium": "~8.3"
},
"platform-dev": [],
"plugin-api-version": "2.2.0"

17
ebala-socket.service Executable file
View File

@ -0,0 +1,17 @@
[Unit]
Description=Ebala-socket
Wants=network.target
After=syslog.target network-online.target
[Service]
ExecStart=sudo -u www-data /usr/bin/php /var/www/ebala/mirzaev/ebala/system/public/socket.php
PIDFile=/var/run/php/ebala-socket.pid
RemainAfterExit=no
RuntimeMaxSec=3600s
Restart=always
RestartSec=30s
[Install]
WantedBy=multi-user.target

View File

@ -106,6 +106,7 @@ final class account extends core
*/
public function update(array $parameters = []): ?string
{
try {
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
// Авторизован аккаунт администратора или оператора
@ -134,7 +135,7 @@ final class account extends core
);
else throw new exception('Пароль должен быть длиннее 6 символов');
if ($parameters['commentary'] !== $account->commentary) $account->commentary = $parameters['commentary'];
if ($parameters['transactions'] !== $account->transactions)
if (!empty($parameters['transactions']) && $parameters['transactions'] !== $account->transactions)
$account->transactions = match ($parameters['transactions']) {
'true' => true,
'false' => false,
@ -222,6 +223,40 @@ final class account extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти аккаунт');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'updated' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;
@ -234,6 +269,7 @@ final class account extends core
*/
public function delete(array $parameters = []): ?string
{
try {
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
// Авторизован аккаунт администратора или оператора
@ -302,6 +338,40 @@ final class account extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти аккаунт');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'deleted' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;
@ -314,6 +384,7 @@ final class account extends core
*/
public function ban(array $parameters = []): ?string
{
try {
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
// Авторизован аккаунт администратора или оператора
@ -353,6 +424,40 @@ final class account extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти аккаунт');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'banned' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;
@ -365,6 +470,7 @@ final class account extends core
*/
public function unban(array $parameters = []): ?string
{
try {
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
// Авторизован аккаунт администратора или оператора
@ -404,6 +510,40 @@ final class account extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти аккаунт');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'unbanned' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;

View File

@ -234,12 +234,35 @@ final class administrator extends core
if (empty($account)) throw new exception('Не удалось создать аккаунт');
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
$this->errors['administrator'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных)

View File

@ -56,7 +56,7 @@ class core extends controller
public function __construct(bool $initialize = true)
{
// Блокировка запросов от CloudFlare
if ($_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return;
if (!empty($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return;
parent::__construct($initialize);

View File

@ -341,7 +341,7 @@ final class market extends core
throw new exception('Не инициализирован аккаунт');
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
$this->errors['market'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
@ -398,6 +398,7 @@ final class market extends core
*/
public function ban(array $parameters = []): ?string
{
try {
if ($this->account->status() && $this->account->type === 'market') {
// Авторизован аккаунт магазина
@ -442,6 +443,39 @@ final class market extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти магазин');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['market'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'banned' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;
@ -454,6 +488,7 @@ final class market extends core
*/
public function unban(array $parameters = []): ?string
{
try {
if ($this->account->status() && $this->account->type === 'market') {
// Авторизован аккаунт магазина
@ -498,6 +533,39 @@ final class market extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти магазин');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['market'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'unbanned' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;
@ -510,6 +578,7 @@ final class market extends core
*/
public function banned(array $parameters = []): ?string
{
try {
if ($this->account->status() && $this->account->type === 'market') {
// Авторизован аккаунт магазина
@ -547,6 +616,38 @@ final class market extends core
flush();
} else throw new exception('Не удалось найти магазин');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['market'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
return null;
}
@ -558,6 +659,7 @@ final class market extends core
*/
public function update(array $parameters = []): ?string
{
try {
if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
// Авторизован аккаунт администратора или оператора
@ -629,6 +731,39 @@ final class market extends core
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти аккаунт');
}
} catch (exception $e) {
// Write to the errors registry
$this->errors['market'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'updated' => false,
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Возврат (провал)
return null;

View File

@ -233,12 +233,35 @@ final class operator extends core
if (empty($account)) throw new exception('Не удалось создать аккаунт');
} catch (exception $e) {
// Write to the errors registry
$this->errors['account'][] = [
$this->errors['operator'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Инициализация буфера ответа
$return = [
'errors' => self::parse_only_text($this->errors)
];
// Генерация ответа
echo json_encode($return);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
// Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных)

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace mirzaev\ebala\controllers;
// Файлы проекта
use mirzaev\ebala\controllers\core,
mirzaev\ebala\controllers\traits\errors,
mirzaev\ebala\models\socket as model,
mirzaev\ebala\models\task,
mirzaev\ebala\models\session,
mirzaev\ebala\models\registry,
mirzaev\ebala\models\core as _core;
// Фреймворк ArangoDB
use mirzaev\arangodb\document;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Встроенные библиотеки
use exception;
/**
* Контроллер сокета
*
* @package mirzaev\ebala\controllers
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class socket extends core
{
use errors;
/**
* Регистрация
*
* @param array $parameters Параметры запроса
*/
public function registration(array $parameters = []): ?string
{
try {
if (model::session($this->session->getId(), $parameters['key'], $this->errors)) {
// Инициализировано соединение
// @todo сделать например возврат того что соединение удалось и на клиенте что-то оптимизировать (или не удалось чтобы повторил)
return null;
} else throw new exception('Не удалось зарегистрировать сокет');
} catch (exception $e) {
// Write to the errors registry
$this->errors['socket'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Возврат (провал)
return null;
}
}

View File

@ -274,6 +274,7 @@ final class task extends core
),
after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }',
page: (int) $this->session->buffer['worker']['tasks']['page'],
sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC',
target: empty($search) ? model::COLLECTION : 'registry_tasks',
return: '{task: z, worker: x, market: y}',
binds: ['worker' => account::worker($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search])
@ -292,6 +293,7 @@ final class task extends core
),
after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }',
page: (int) $this->session->buffer['market']['tasks']['page'],
sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC',
target: empty($search) ? model::COLLECTION : 'registry_tasks',
return: '{task: z, worker: x, market: y}',
binds: ['market' => account::market($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search])
@ -310,6 +312,7 @@ final class task extends core
),
after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }',
page: (int) $this->session->buffer['operator']['tasks']['page'],
sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC',
target: empty($search) ? model::COLLECTION : 'registry_tasks',
return: '{task: z, worker: x, market: y}',
binds: empty($search) ? [] : [
@ -448,7 +451,6 @@ final class task extends core
// Перенос времени в дату
$start = $date->setTime((int) $start->format('H'), (int) $start->format('i'));
// Запись в буфер
$link->task = ['start' => $start] + $link->task;
}
@ -469,6 +471,11 @@ final class task extends core
$link->task = ['end' => $end] + $link->task;
}
// Инициализация данных заблокировавшего аккаунта
if (isset($link->task['block']) && $link->task['block']['expires'] > time() && $link->task['block']['account'] !== (int) $account->getKey())
$link->task = ['block' => ['_account' => account::read('d._key == "' . $link->task['block']['account'] . '"')] + $link->task['block']] + $link->task;
else $link->task = ['block' => null] + $link->task;
// Инициализация буфера сгенерированных данных работы для шаблонизатора
$generated = [];
@ -642,6 +649,14 @@ final class task extends core
// Найден сотрудник (запрашиваемый для записи сотрудник существует в базе данных)
if (!$worker->fired) {
// Не уволен сотрудник
if (in_array($task->work, $worker->works, true)) {
// Работа соответствует подходящим сотруднику
if ((($age = (new DateTime)->diff(DateTime::createFromFormat('d.m.Y', $worker->birth))->y) > 15 && ($task->work === 'Выкладчик' || $task->work === 'Гастроном')) || $age > 17) {
// Подходит по возрасту сотрудник
if ($task->worker !== $parameters['worker']) {
// Идентификатор запрашиваемого сотрудника не равен актуальному
@ -685,6 +700,8 @@ final class task extends core
flush();
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Сотрудник уже назначен');
} else throw new exception('Сотрудник не подходит по возрасту');
} else throw new exception('Заявка не подходит по типу работы для сотрудника');
} else throw new exception('Нельзя назначить уволенного сотрудника');
} else throw new exception('Не найден сотрудник');
}
@ -834,9 +851,11 @@ final class task extends core
// Инициализация данных сотрудника
$this->view->task = model::read(
'd._key == "' . $parameters['task'] . '"',
return: $this->account->type === 'market'
? '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed }'
: '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed, hided: d.hided, updates: d.updates }'
return: match ($this->account->type) {
'market' => '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed }',
'administrator', 'operator' => '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed, hided: d.hided, updates: d.updates }',
default => 'd'
}
)->getAll();
// Заявка не принадлежит запросившему магазину?
@ -1020,7 +1039,7 @@ final class task extends core
'tax' => 'ИНН',
'city' => 'Город',
'payment' => 'Форма оплаты',
'works' => 'Формы работ',
'works' => 'Типы работ',
default => $key
},
'value' => $value
@ -1128,6 +1147,7 @@ final class task extends core
'number' => 'Номер',
'mail' => 'Почта',
'commentary' => 'Комментарий',
'bans' => 'Заблокированные',
default => $key
},
'value' => $value
@ -1694,6 +1714,94 @@ final class task extends core
}
}
/**
* Сгенерировать строку
*
* Используется тогда, когда нужно получить HTML-код строки заявки
*
* @param array $parameters Параметры запроса
*
* @return void В буфер вывода JSON-документ с запрашиваемыми параметрами
*/
public function row(array $parameters = []): void
{
try {
if ($this->account->status()) {
// Авторизован аккаунт
// Инициализация строки в глобальную переменную шаблонизатора
if ($this->account->type === 'worker')
$this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '" && task.worker == "' . account::worker($this->account->getId())?->id . '"');
else if ($this->account->type === 'operator')
$this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"');
else if ($this->account->type === 'market')
$this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '" && task.market == "' . account::market($this->account->getId())?->id . '"');
else if ($this->account->type === 'administrator')
$this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"');
else $this->view->rows = [];
// Генерация данных для генерации строки
$this->view->rows = static::preprocessing($this->account, $this->view->rows);
// Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение)
$this->view->page = null;
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Генерация ответа
echo json_encode(
[
'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'),
'errors' => self::parse_only_text($this->errors)
]
);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
} else throw new exception('Вы не авторизованы');
} catch (exception $e) {
// Запись в реестр ошибок
$this->errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
// Запись заголовков ответа
header('Content-Type: application/json');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');
// Инициализация буфера вывода
ob_start();
// Генерация ответа
echo json_encode(
[
'errors' => self::parse_only_text($this->errors)
]
);
// Запись заголовков ответа
header('Content-Length: ' . ob_get_length());
// Отправка и деинициализация буфера вывода
ob_end_flush();
flush();
}
}
/**
* Удалить
*

View File

@ -60,7 +60,7 @@ final class account extends core
$this->document = $account->document;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
self::session($session->getId(), $this->document->getId(), $errors);
// Блокировка доступа
if ($account?->active !== true) throw new exception('Свяжитесь с оператором');
@ -96,7 +96,7 @@ final class account extends core
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
self::session($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['number' => null, 'password' => null]]);
@ -129,7 +129,7 @@ final class account extends core
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
self::session($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['_key' => null, 'password' => null]]);
@ -155,7 +155,7 @@ final class account extends core
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
self::session($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['_key' => null, 'password' => null]]);
@ -181,7 +181,7 @@ final class account extends core
$this->document = $account;
// Связь сессии с аккаунтом
session::connect($session->getId(), $this->document->getId(), $errors);
self::session($session->getId(), $this->document->getId(), $errors);
// Удаление использованных данных из буфера сессии
$session->write(['entry' => ['_key' => null, 'password' => null]]);
@ -318,7 +318,7 @@ final class account extends core
}
/**
* Инициализировать связь аккаунта с сотрудником
* Подключить к сотруднику
*
* Ищет связь аккаунта с сотрудником, если не находит, то создаёт её
*
@ -328,6 +328,9 @@ final class account extends core
* @param array &$errors Реестр ошибок
*
* @return bool Связан аккаунт с сотрудником?
*
* @todo
* 1. Переделать на подобие account::session и перенести в mirzaev/ebala/models/worker
*/
public static function connect(string $account, string $target, string $type = 'worker', array &$errors = []): bool
{
@ -403,6 +406,65 @@ final class account extends core
return null;
}
/**
* Подключить к сессии
*
* Ищет связь сессии с аккаунтом, если не находит, то создаёт её
*
* @param string $session Идентификатор сессии
* @param string $account Идентификатор аккаунта
* @param array &$errors Реестр ошибок
*
* @return bool Аккаунт подключен к сессии?
*/
public static function session(string $session, string $account, array &$errors = []): bool
{
try {
if (
collection::init(static::$arangodb->session, self::COLLECTION)
&& collection::init(static::$arangodb->session, session::COLLECTION)
&& collection::init(static::$arangodb->session, session::COLLECTION . '_edge_' . self::COLLECTION, true)
) {
// Инициализированы коллекции
if (
collection::search(static::$arangodb->session, sprintf(
<<<AQL
FOR document IN %s
FILTER document._from == '%s' && document._to == '%s'
LIMIT 1
RETURN document
AQL,
session::COLLECTION . '_edge_' . self::COLLECTION,
$session,
$account
)) instanceof _document
|| document::write(static::$arangodb->session, session::COLLECTION . '_edge_' . self::COLLECTION, [
'_from' => $session,
'_to' => $account
])
) {
// Найдено, либо создано ребро: session -> account
// Возврат (успех)
return true;
} else throw new exception('Не удалось создать ребро: session -> account');
} else throw new exception('Не удалось инициализировать коллекции');
} catch (exception $e) {
// Запись в реестр ошибок
$errors['account'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Возврат (провал)
return false;
}
/**
* Записать
*

View File

@ -35,7 +35,7 @@ class core extends model
/**
* Путь до файла с настройками подключения к базе данных ArangoDB
*/
final public const ARANGODB = '../settings/arangodb.php';
final public const ARANGODB = __DIR__ . '/../settings/arangodb.php';
/**
* Соединение с базой данных ArangoDB
@ -203,7 +203,8 @@ class core extends model
*
* @return int|null Количество документов в базе данных, если найдены
*/
public static function count(?string $collection = null, array &$errors = []): int|null {
public static function count(?string $collection = null, array &$errors = []): int|null
{
try {
if (collection::init(static::$arangodb->session, static::COLLECTION)) {
// Инициализирована коллекция

View File

@ -164,67 +164,12 @@ final class session extends core
// Закрыть сессию
}
/**
* Инициализировать связь сессии с аккаунтом
*
* Ищет связь сессии с аккаунтом, если не находит, то создаёт её
*
* @param account $account Инстанция аккаунта
* @param array &$errors Реестр ошибок
*
* @return bool Связана сессия с аккаунтом?
*/
public static function connect(string $session, string $account, array &$errors = []): bool
{
try {
if (
collection::init(static::$arangodb->session, self::COLLECTION)
&& collection::init(static::$arangodb->session, account::COLLECTION)
&& collection::init(static::$arangodb->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
) {
// Инициализированы коллекции
if (
collection::search(static::$arangodb->session, sprintf(
<<<AQL
FOR document IN %s
FILTER document._from == '%s' && document._to == '%s'
LIMIT 1
RETURN document
AQL,
self::COLLECTION . '_edge_' . account::COLLECTION,
$session,
$account
)) instanceof _document
|| document::write(static::$arangodb->session, self::COLLECTION . '_edge_' . account::COLLECTION, [
'_from' => $session,
'_to' => $account
])
) {
// Найдено, либо создано ребро: 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 ?account Инстанция аккаунта, если удалось найти
* @return account|null Инстанция аккаунта, если удалось найти
*/
public function account(array &$errors = []): ?account
{
@ -273,6 +218,7 @@ final class session extends core
];
}
// Возврат (провал)
return null;
}

View File

@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
namespace mirzaev\ebala\models;
// Project files
use mirzaev\ebala\models\traits\instance,
mirzaev\ebala\models\traits\status,
mirzaev\ebala\models\account,
mirzaev\ebala\models\worker,
mirzaev\ebala\models\market;
// Библиотека для ArangoDB
use ArangoDBClient\Document as _document;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Встроенные библиотеки
use exception;
/**
* Модель сокета
*
* Управляет записью и чтением настроек сайта из ArangoDB
*
* @package mirzaev\ebala\models
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class socket extends core
{
use instance, status;
/**
* Collection name in ArangoDB
*/
final public const COLLECTION = 'socket';
/**
* Инстанция документа в базе данных
*/
protected readonly _document $document;
/**
* Подключение к сессии
*
* @param string $session Идентификатор сессии
* @param string $key Ключ для регистрации (из документа в static::COLLECTION)
* @param array &$errors Реестр ошибок
*
* @return bool Сокет подключен к сессии?
*/
public static function session(string $session, string $key, array &$errors = []): bool
{
try {
if (
collection::init(static::$arangodb->session, self::COLLECTION)
&& collection::init(static::$arangodb->session, session::COLLECTION)
&& collection::init(static::$arangodb->session, $edge = session::COLLECTION . '_edge_' . self::COLLECTION, true)
) {
// Инициализирована коллекция
// Чтение сокета
$socket = self::read(
filter: "d.key == \"$key\" && d.expires > DATE_NOW() / 1000",
sort: 'd.created desc, d.expires desc',
amount: 1,
);
if ($socket instanceof _document) {
// Найден сокет
if (document::write(static::$arangodb->session, $edge, [
'_from' => $session,
'_to' => $socket->getId()
])) {
// Записано ребро: СЕССИЯ -> СОКЕТ
// Возврат (успех)
return true;
} else throw new exception('Не удалось записать изменения в базу данных');
} else throw new exception('Не удалось найти сокет');
} else throw new exception('Не удалось инициализировать коллекции');
} catch (exception $e) {
// Write to the errors registry
$errors['socket'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Возврат (провал)
return false;
}
/**
* Найти аккаунт
*
* Ищет аккаунт по идентификатору подключения к сокету
*
* @param int $socket Идентификатор подключения к сокету (во внутренней базе данных OpenSwoole)
* @param array &$errors Реестр ошибок
*
* @returnaccount|null Аккаунт, если найден
*/
public static function account(int $socket, array &$errors = []): ?account
{
try {
if (
collection::init(static::$arangodb->session, self::COLLECTION)
&& collection::init(static::$arangodb->session, session::COLLECTION)
&& collection::init(static::$arangodb->session, account::COLLECTION)
&& collection::init(static::$arangodb->session, session::COLLECTION . '_edge_' . self::COLLECTION, true)
&& collection::init(static::$arangodb->session, session::COLLECTION . '_edge_' . account::COLLECTION, true)
) {
// Инициализированы коллекции
// Инициализация инстанции аккаунта
$account = new account;
// Поиск инстанции аккаунта в базе данных
$instance = $account->instance(collection::search(static::$arangodb->session, sprintf(
<<<AQL
FOR a IN 1 OUTBOUND
(FOR s IN 1 INBOUND
(FOR s IN %s
FILTER s.id == %s && s.expires > DATE_NOW() / 1000
SORT s.created desc, s.expires desc
LIMIT 1
RETURN s
)[0]._id
%s
RETURN s
)[0]._id
%s
RETURN a
AQL,
socket::COLLECTION,
$socket,
session::COLLECTION . '_edge_' . socket::COLLECTION,
session::COLLECTION . '_edge_' . account::COLLECTION,
)));
// Возврат (успех)
return $instance instanceof _document ? $account : throw new exception('Не удалось найти инстанцию аккаунта в базе данных');
} else throw new exception('Не удалось инициализировать коллекцию');
} catch (exception $e) {
// Запись в реестр ошибок
$errors['socket'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Возврат (провал)
return null;
}
}

View File

@ -294,4 +294,100 @@ final class task extends core
// Exit (fail)
return null;
}
/**
* Block by account
*
* @param int $task (_key)
* @param int $account (_key)
* @param array $errors
*
* @return bool Task is blocked?
*/
public static function block(
int $task,
int $account,
array &$errors = []
): bool {
try {
if (collection::init(static::$arangodb->session, self::COLLECTION)) {
// Инициализирована коллекция
if (($task = static::read('d._key == "' . $task . '"')) instanceof _document) {
// Найдена заявка
if ($task->block === null || $task->block['expires'] < time()) {
// Не заблокирована заявка
// Блокировка
$task->block = ['account' => $account, 'expires' => (int) strtotime('+1 minute')];
// Запись обновления в базу данных и возврат (успех)
return core::update($task);
} else throw new exception('Заявка уже заблокирована: ' . $task->block['account']);
} else throw new exception('Не удалось инициализировать коллекции');
} else throw new exception('Не удалось инициализировать коллекции');
} catch (exception $e) {
// Write to the errors registry
$errors['task'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return false;
}
/**
* Unblock by account
*
* @param int $task (_key)
* @param int $account (_key)
* @param array $errors
*
* @return bool Task is blocked?
*/
public static function unblock(
int $task,
int $account,
array &$errors = []
): bool {
try {
if (collection::init(static::$arangodb->session, self::COLLECTION)) {
// Инициализирована коллекция
if (($task = static::read('d._key == "' . $task . '"')) instanceof _document) {
// Найдена заявка
if ($task->block !== null) {
// Заблокирована заявка
if ($task->block['account'] === $account || $task->block['expires'] > time()) {
// Аккаунт отменяет свою блокировку или истекло время блокировки
// Разблокировка
$task->block = null;
// Запись обновления в базу данных и возврат (успех)
return core::update($task);
}
} else throw new exception('Заявка не заблокирована');
} else throw new exception('Не удалось инициализировать коллекции');
} else throw new exception('Не удалось инициализировать коллекции');
} catch (exception $e) {
// Write to the errors registry
$errors['task'][] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return false;
}
}

View File

@ -0,0 +1,35 @@
@charset "UTF-8";
#connection {
z-index: 999999;
position: fixed;
bottom: 20px;
left: 20px;
height: 16px;
display: flex;
}
#connection > i#indicator {
width: 16px;
height: 16px;
display: block;
cursor: help;
border-radius: 100%;
}
#connection > small {
margin-left: 7px;
height: 16px;
display: flex;
align-items: center;
font-weight: bold;
color: var(--socket-text);
}
#connection > i#indicator.disconnected:not(.connected) {
background-color: var(--socket-disconnected);
}
#connection > i#indicator.connected:not(.disconnected) {
background-color: var(--socket-connected);
}

0
mirzaev/ebala/system/public/css/fonts/dejavu.css Normal file → Executable file
View File

0
mirzaev/ebala/system/public/css/fonts/fira.css Normal file → Executable file
View File

0
mirzaev/ebala/system/public/css/fonts/hack.css Normal file → Executable file
View File

0
mirzaev/ebala/system/public/css/icons/home.css Normal file → Executable file
View File

View File

0
mirzaev/ebala/system/public/css/icons/smartphone.css Normal file → Executable file
View File

0
mirzaev/ebala/system/public/css/icons/timer.css Normal file → Executable file
View File

0
mirzaev/ebala/system/public/css/icons/user.css Normal file → Executable file
View File

0
mirzaev/ebala/system/public/css/icons/work_alt.css Normal file → Executable file
View File

View File

@ -112,13 +112,14 @@ section.panel.list>div#title>span {
}
section.panel.list>div.row {
--width: calc(100% - 24px);
--gap: 12px;
--background: var(--cloud);
position: relative;
left: 0px;
width: calc(100% - 24px);
height: 35px;
display: flex;
width: var(--width, calc(100% - 24px));
gap: var(--gap, 12px);
padding: 0 var(--gap, 12px);
border-radius: 0px;
@ -138,7 +139,7 @@ section.panel.list>div.row:not(:nth-of-type(1))>span {
-moz-box-shadow: var(--box-shadow);
}
section.panel.list>div.row:not(:nth-of-type(1)):is(:hover, :focus) {
section.panel.list>div.row:not(:nth-of-type(1)):not([data-blocked]):is(:hover, :focus) {
--padding-left: 24px;
--padding-right: 24px;
left: -12px;
@ -281,7 +282,7 @@ section.panel.list>div.row>span.field {
cursor: text;
}
section.panel.list>div.row:not(:nth-of-type(1))>span:is(.important, .interactive:is(:hover, :focus)) {
section.panel.list>div.row:not(:nth-of-type(1)):not([data-blocked])>span:is(.important, .interactive:is(:hover, :focus)) {
--margin: calc(var(--gap) / 2);
--border-left: calc(var(--padding-left, var(--margin, 0px)) * -1);
--border-right: var(--padding-right, var(--margin, 0px));
@ -482,3 +483,29 @@ section.panel.list>div.row[data-row="worker"]:not(:nth-of-type(1)).fired>span:is
section.panel.list>div.row[data-row="worker"]:not(:nth-of-type(1)):nth-child(2n + 1).fired>span:is(.important, .interactive:is(:hover, :focus)) {
--background: var(--magma-important-above);
}
section.panel.list>div.row[data-blocked] {
margin-left: 8px;
width: calc(var(--width) - 16px);
cursor: progress;
opacity: 70% !important;
}
section.panel.list>div.row[data-blocked]:before {
content: attr(data-blocked) !important;
color: var(--earth-text-important-below);
}
section.panel.list>div.row[data-blocked] * {
pointer-events: none;
}
section.panel.list>div.row:not([data-blocked]):has(+ div.row[data-blocked]) {
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
section.panel.list>div.row[data-blocked]+div.row:not([data-blocked]) {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}

0
mirzaev/ebala/system/public/css/popup.css Normal file → Executable file
View File

View File

@ -167,6 +167,10 @@
--link: #3c76ff;
--link-hover: #6594ff;
--link-active: #3064dd;
--socket-connected: #2be851;
--socket-disconnected: #8e8181;
--socket-text: #b09999;
}
body {

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

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