first init
This commit is contained in:
parent
b8e76159b9
commit
29ac14fbd5
|
@ -0,0 +1 @@
|
||||||
|
vendor
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "mirzaev/minimal-sessions-arangodb",
|
||||||
|
"description": "Module for session system based on the minimal framework using the ArangoDB database",
|
||||||
|
"keywords": [
|
||||||
|
"minimal",
|
||||||
|
"sessions",
|
||||||
|
"arangodb"
|
||||||
|
],
|
||||||
|
"type": "minimal-module",
|
||||||
|
"license": "WTFPL",
|
||||||
|
"homepage": "https://git.mirzaev.sexy/mirzaev/minimal-sessions-arangodb",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Arsen Mirzaev Tatyano-Muradovich",
|
||||||
|
"email": "arsen@mirzaev.sexy",
|
||||||
|
"homepage": "https://mirzaev.sexy",
|
||||||
|
"role": "Programmer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"mirzaev/minimal": "^2.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"mirzaev\\minimal\\sessions\\arangodb\\": "mirzaev/minimal-sessions-arangodb/system"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"mirzaev\\minimal\\sessions\\arangodb\\tests\\": "mirzaev/minimal-sessions-arangodb/tests"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "d14d4eec14bd24ff19dbca47317e07ac",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "mirzaev/minimal",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.mirzaev.sexy/mirzaev/minimal",
|
||||||
|
"reference": "22ad7304f980dc097cbb29d1c9aa130b59a7a58f"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "~8.1"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-PDO": "To work with SQL-based databases (MySQL, PostreSQL...)"
|
||||||
|
},
|
||||||
|
"type": "framework",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"mirzaev\\minimal\\": "mirzaev/minimal/system"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"WTFPL"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Arsen Mirzaev Tatyano-Muradovich",
|
||||||
|
"email": "arsen@mirzaev.sexy",
|
||||||
|
"homepage": "https://mirzaev.sexy",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Lightweight MVC framework that manages only the basic mechanisms, leaving the development of the programmer and not overloading the project",
|
||||||
|
"homepage": "https://git.mirzaev.sexy/mirzaev/minimal",
|
||||||
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"mvc"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://git.mirzaev.sexy/mirzaev/minimal/wiki",
|
||||||
|
"issues": "https://git.mirzaev.sexy/mirzaev/minimal/issues"
|
||||||
|
},
|
||||||
|
"time": "2022-11-02T22:27:45+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": "^8.1"
|
||||||
|
},
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.3.0"
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\minimal\sessions\arangodb\models;
|
||||||
|
|
||||||
|
// Фреймворк PHP
|
||||||
|
use mirzaev\minimal\model;
|
||||||
|
|
||||||
|
// Фреймворк ArangoDB
|
||||||
|
use mirzaev\arangodb\connection;
|
||||||
|
|
||||||
|
// Встроенные библиотеки
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ядро моделей
|
||||||
|
*
|
||||||
|
* @package mirzaev\virus\models
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
class core extends model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Коллекция в которой хранятся аккаунты
|
||||||
|
*/
|
||||||
|
public const SETTINGS = '../settings/arangodb.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Соединение с базой данных
|
||||||
|
*/
|
||||||
|
protected static connection $db;
|
||||||
|
|
||||||
|
public function __construct(connection $db = null)
|
||||||
|
{
|
||||||
|
if (isset($db)) {
|
||||||
|
// Получена инстанция соединения с базой данных
|
||||||
|
|
||||||
|
// Запись и инициализация соединения с базой данных
|
||||||
|
$this->__set('db', $db);
|
||||||
|
} else {
|
||||||
|
// Не получена инстанция соединения с базой данных
|
||||||
|
|
||||||
|
// Инициализация соединения с базой данных по умолчанию
|
||||||
|
$this->__get('db');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Записать свойство
|
||||||
|
*
|
||||||
|
* @param string $name Название
|
||||||
|
* @param mixed $value Значение
|
||||||
|
*/
|
||||||
|
public function __set(string $name, mixed $value = null): void
|
||||||
|
{
|
||||||
|
match ($name) {
|
||||||
|
'db' => (function () use ($value) {
|
||||||
|
if ($this->__isset('db')) {
|
||||||
|
// Свойство уже было инициализировано
|
||||||
|
|
||||||
|
// Выброс исключения (неудача)
|
||||||
|
throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500);
|
||||||
|
} else {
|
||||||
|
// Свойство ещё не было инициализировано
|
||||||
|
|
||||||
|
if ($value instanceof connection) {
|
||||||
|
// Передано подходящее значение
|
||||||
|
|
||||||
|
// Запись свойства (успех)
|
||||||
|
self::$db = $value;
|
||||||
|
} else {
|
||||||
|
// Передано неподходящее значение
|
||||||
|
|
||||||
|
// Выброс исключения (неудача)
|
||||||
|
throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией mirzaev\arangodb\connection', 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
default => parent::__set($name, $value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Прочитать свойство
|
||||||
|
*
|
||||||
|
* @param string $name Название
|
||||||
|
*
|
||||||
|
* @return mixed Содержимое
|
||||||
|
*/
|
||||||
|
public function __get(string $name): mixed
|
||||||
|
{
|
||||||
|
return match ($name) {
|
||||||
|
'db' => (function () {
|
||||||
|
if (!$this->__isset('db')) {
|
||||||
|
// Свойство не инициализировано
|
||||||
|
|
||||||
|
// Инициализация значения по умолчанию исходя из настроек
|
||||||
|
$this->__set('db', new connection(require static::SETTINGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$db;
|
||||||
|
})(),
|
||||||
|
default => parent::__get($name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверить свойство на инициализированность
|
||||||
|
*
|
||||||
|
* @param string $name Название
|
||||||
|
*/
|
||||||
|
public function __isset(string $name): bool
|
||||||
|
{
|
||||||
|
return match ($name) {
|
||||||
|
default => parent::__isset($name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Удалить свойство
|
||||||
|
*
|
||||||
|
* @param string $name Название
|
||||||
|
*/
|
||||||
|
public function __unset(string $name): void
|
||||||
|
{
|
||||||
|
match ($name) {
|
||||||
|
default => parent::__isset($name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Статический вызов
|
||||||
|
*
|
||||||
|
* @param string $name Название
|
||||||
|
* @param array $arguments Параметры
|
||||||
|
*/
|
||||||
|
public static function __callStatic(string $name, array $arguments): mixed
|
||||||
|
{
|
||||||
|
match ($name) {
|
||||||
|
'db' => (new static)->__get('db'),
|
||||||
|
default => throw new exception("Не найдено свойство или функция: $name", 500)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\minimal\sessions\arangodb\models;
|
||||||
|
|
||||||
|
// Файлы проекта
|
||||||
|
use mirzaev\virus\models\account_model as account;
|
||||||
|
|
||||||
|
// Фреймворк ArangoDB
|
||||||
|
use mirzaev\arangodb\collection,
|
||||||
|
mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
// Библиотека для ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
|
// Встроенные библиотеки
|
||||||
|
use exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Модель сессий
|
||||||
|
*
|
||||||
|
* @package mirzaev\virus\models
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class session_model extends core
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Коллекция
|
||||||
|
*/
|
||||||
|
public const COLLECTION = 'session';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация
|
||||||
|
*
|
||||||
|
* @param ?string $hash Хеш сессии в базе данных
|
||||||
|
* @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии)
|
||||||
|
* @param array &$errors Журнал ошибок
|
||||||
|
*
|
||||||
|
* @return ?_document Инстанция сессии, если удалось найти или создать
|
||||||
|
*/
|
||||||
|
public static function initialization(?string $hash = null, ?int $expires = null, array &$errors = []): ?_document
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (collection::init(static::$db->session, self::COLLECTION)) {
|
||||||
|
// Инициализирована коллекция
|
||||||
|
|
||||||
|
if (isset($hash) && $session = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR d IN %s
|
||||||
|
FILTER d.hash == '$hash' && d.expires > %d
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
self::COLLECTION,
|
||||||
|
time()
|
||||||
|
))) {
|
||||||
|
// Найдена сессия по хешу
|
||||||
|
|
||||||
|
// Возврат сессии
|
||||||
|
return $session;
|
||||||
|
} else if ($session = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR d IN %s
|
||||||
|
FILTER d.ip == '%s' && d.expires > %d
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
self::COLLECTION,
|
||||||
|
$_SERVER['REMOTE_ADDR'],
|
||||||
|
time()
|
||||||
|
))) {
|
||||||
|
// Найдена сессия по данным пользователя
|
||||||
|
|
||||||
|
// Возврат сессии
|
||||||
|
return $session;
|
||||||
|
} else {
|
||||||
|
// Не найдена сессия
|
||||||
|
|
||||||
|
// Запись сессии в базу данных
|
||||||
|
$_id = document::write(static::$db->session, self::COLLECTION, [
|
||||||
|
'ip' => $_SERVER['REMOTE_ADDR'],
|
||||||
|
'expires' => $expires ?? time() + 604800
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($session = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR d IN %s
|
||||||
|
FILTER d._id == '$_id' && d.expires > %d
|
||||||
|
RETURN d
|
||||||
|
AQL,
|
||||||
|
self::COLLECTION,
|
||||||
|
time()
|
||||||
|
))) {
|
||||||
|
// Найдена созданная сессия
|
||||||
|
|
||||||
|
// Запись хеша
|
||||||
|
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
|
||||||
|
|
||||||
|
if (document::update(static::$db->session, $session)) {
|
||||||
|
// Записано обновление
|
||||||
|
|
||||||
|
return $session;
|
||||||
|
} else throw new exception('Не удалось записать данные сессии');
|
||||||
|
} else throw new exception('Не удалось создать или найти созданную сессию');
|
||||||
|
}
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в журнал ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Связь сессии с аккаунтом
|
||||||
|
*
|
||||||
|
* @param _document $session Инстанция сессии
|
||||||
|
* @param _document $account Инстанция аккаунта
|
||||||
|
* @param array &$errors Журнал ошибок
|
||||||
|
*
|
||||||
|
* @return bool Статус выполнения
|
||||||
|
*/
|
||||||
|
public static function connect(_document $session, _document $account, array &$errors = []): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
collection::init(static::$db->session, self::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, account::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
|
||||||
|
) {
|
||||||
|
// Инициализирована коллекция
|
||||||
|
|
||||||
|
if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [
|
||||||
|
'_from' => $session->getId(),
|
||||||
|
'_to' => $account->getId()
|
||||||
|
])) {
|
||||||
|
// Создано ребро: session -> account
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else throw new exception('Не удалось создать ребро: session -> account');
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в журнал ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поиск связанного аккаунта
|
||||||
|
*
|
||||||
|
* @param _document $session Инстанция сессии
|
||||||
|
* @param array &$errors Журнал ошибок
|
||||||
|
*
|
||||||
|
* @return ?_document Инстанция аккаунта, если удалось найти
|
||||||
|
*/
|
||||||
|
public static function account(_document $session, array &$errors = []): ?_document
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
collection::init(static::$db->session, self::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, account::COLLECTION)
|
||||||
|
&& collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true)
|
||||||
|
) {
|
||||||
|
// Инициализированы коллекции
|
||||||
|
|
||||||
|
if ($account = collection::search(static::$db->session, sprintf(
|
||||||
|
<<<AQL
|
||||||
|
FOR document IN %s
|
||||||
|
LET edge = (
|
||||||
|
FOR edge IN %s
|
||||||
|
FILTER edge._from == '%s'
|
||||||
|
SORT edge._key DESC
|
||||||
|
LIMIT 1
|
||||||
|
RETURN edge
|
||||||
|
)
|
||||||
|
FILTER document._id == edge[0]._to
|
||||||
|
LIMIT 1
|
||||||
|
RETURN document
|
||||||
|
AQL,
|
||||||
|
account::COLLECTION,
|
||||||
|
self::COLLECTION . '_edge_' . account::COLLECTION,
|
||||||
|
$session->getId()
|
||||||
|
))) {
|
||||||
|
// Найден аккаунт
|
||||||
|
|
||||||
|
return $account;
|
||||||
|
} else throw new exception('Не удалось найти аккаунт');
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию');
|
||||||
|
} catch (exception $e) {
|
||||||
|
// Запись в журнал ошибок
|
||||||
|
$errors[] = [
|
||||||
|
'text' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'stack' => $e->getTrace()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue