c1
2
www/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.composer/
|
||||
.bash_history
|
||||
21
www/first.loc/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
19
www/first.loc/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# tsd
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
40
www/first.loc/api/Class/DataForms.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
class DataForms{
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->conn = $db;
|
||||
}
|
||||
|
||||
function to_UTF8($arr) {
|
||||
foreach($arr as $k=>$v){
|
||||
$v=iconv('Windows-1251','UTF-8',$v);
|
||||
$arr[$k]=$v;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function Query()
|
||||
{
|
||||
|
||||
$result=[
|
||||
1,2,3
|
||||
];
|
||||
|
||||
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
36
www/first.loc/api/Class/Database.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
# private $host = "10.10.1.246";
|
||||
# public $db_name = "tsd_main";
|
||||
# private $username = "tsd";
|
||||
#private $password = "gcIL6UrWFtSdDTbz";
|
||||
|
||||
|
||||
private $host = "10.10.0.20";
|
||||
public $db_name = "tsd";
|
||||
private $username = "tsd_user";
|
||||
private $password = "stptoamh";
|
||||
|
||||
|
||||
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
|
||||
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
28
www/first.loc/api/Class/DatabaseMS.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class DatabaseMS
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
private $host = "omega-server";
|
||||
public $db_name = "SKL_BLOKSQL";
|
||||
private $username = "sa";
|
||||
private $password = "haqdvega#";
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("dblib:host=" . $this->host . ";dbname=" . $this->db_name.";charset=CP1251", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
|
||||
31
www/first.loc/api/Class/DatabasePGSQL.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
//private $host = "localhost:3308";
|
||||
private $host = "10.10.1.246";
|
||||
//public $db_name = "authentication_jwt";
|
||||
//private $username = "tsd_user";
|
||||
private $username = "tsd";
|
||||
//private $password = "Dfrgvm3@1";
|
||||
private $password = "gcIL6UrWFtSdDTbz";
|
||||
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
|
||||
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
340
www/first.loc/api/Class/MainClass.php
Normal file
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
#[AllowDynamicProperties]
|
||||
|
||||
Class MainClass {
|
||||
|
||||
private $headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
|
||||
protected object $conn;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->conn = $db;
|
||||
}
|
||||
|
||||
|
||||
function WrFile($param){
|
||||
//запись в файл. Не понятно зачем, но пока оставил
|
||||
|
||||
|
||||
$path='/var/www/tmp';
|
||||
$file_name=$param['date_query'].'.json';
|
||||
if(!file_exists($path)){
|
||||
mkdir($path);
|
||||
chmod ( $path, 0777 );
|
||||
}
|
||||
$path=$path.'/beru';
|
||||
|
||||
if(!file_exists($path)){
|
||||
mkdir($path);
|
||||
chmod ( $path, 0777 );
|
||||
}
|
||||
|
||||
if(file_exists($path.'/'.$file_name)){
|
||||
unlink($path.'/'.$file_name);
|
||||
}
|
||||
file_put_contents($path.'/'.$file_name, $param['data_json']);
|
||||
|
||||
|
||||
|
||||
}
|
||||
function getSSLPage($url,$post_data,$headers) {
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
if(strlen(trim($post_data))>0){
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
}
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
$output = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
function wrDatabase($param){
|
||||
|
||||
/*
|
||||
$param=[
|
||||
`'date_query' => $date_query,
|
||||
'data_json' => $data_json,
|
||||
'db'=> $db,
|
||||
'user'= $user
|
||||
];`
|
||||
*/
|
||||
|
||||
$opers=json_decode($param['data_json'],true);
|
||||
|
||||
try{
|
||||
|
||||
//echo json_encode($opereturnrs);
|
||||
//die('stop');
|
||||
|
||||
$user=$param['user'];
|
||||
$date_query=$param['date_query'];
|
||||
$firma=$param['kag']['firma'];
|
||||
|
||||
for($i=0;$i<count($opers);$i++){
|
||||
|
||||
$item=$opers[$i];
|
||||
$vars=[
|
||||
'idoper'=>$item['id'],
|
||||
'data_json' => json_encode($item),
|
||||
'user'=>$user,
|
||||
'date_d'=>date('Y-m-d H:i:s'),
|
||||
'date_query'=>$date_query,
|
||||
'firma'=>$firma,
|
||||
|
||||
];
|
||||
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
//удалить предыдущие загрузки по дате загрузки и номеру акта
|
||||
|
||||
$sth = $this->conn->prepare("delete from oper where idoper=:id and firma=:firma and date_query=:date_query");
|
||||
$sth->execute([
|
||||
'id'=>$item['id'],
|
||||
'firma'=>$firma,
|
||||
'date_query'=>$date_query,
|
||||
]);
|
||||
|
||||
$query="insert into oper
|
||||
(idoper,firma, data_json,date_d,\"user\",date_query)
|
||||
values
|
||||
(:idoper,:firma, :data_json,:date_d,:user,:date_query)";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
$stmt->execute($vars);
|
||||
|
||||
$error=$stmt->errorCode();
|
||||
$ids=$this->conn->lastInsertId();
|
||||
$this->conn->commit();
|
||||
|
||||
//idoper == >
|
||||
$ids_arr[$item['id']]=$ids;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
$result= ['result'=>1,'result'=>$ids_arr];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}catch (Exception $e) {
|
||||
|
||||
$this->conn->rollBack();
|
||||
$error = "Ошибка: " . $e->getMessage();
|
||||
$result = ['result'=>0,'error'=>$error];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function DataCollect($param){
|
||||
//результаты сканирования актов
|
||||
|
||||
$result=[];
|
||||
|
||||
$query = "select
|
||||
id,
|
||||
barcode,
|
||||
date_up
|
||||
from oper_collect
|
||||
where idoper=:id and firma=:firma ORDER BY date_up";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$vars['id'] = $param['idoper'];
|
||||
$vars['firma'] = $param['kag']['firma'];
|
||||
$stmt->execute($vars);
|
||||
$error = $stmt->errorCode();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
|
||||
return ['idoper'=>$param['idoper'] ,'collect'=>$result,'v'=>$vars];
|
||||
}
|
||||
|
||||
|
||||
function History($param){
|
||||
|
||||
$limit=$param['limit'];
|
||||
$kag=$param['kag'];
|
||||
|
||||
$vars=[
|
||||
'firma'=>$param['kag']['firma'],
|
||||
|
||||
];
|
||||
|
||||
$query = "select
|
||||
id,
|
||||
idoper,
|
||||
date_query,
|
||||
to_char(date_query,'DD.MM.YY') as date_q,
|
||||
data_json
|
||||
from oper
|
||||
where firma=:firma
|
||||
ORDER BY date_query DESC, id DESC
|
||||
limit 10
|
||||
";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$stmt->execute($vars);
|
||||
$error = $stmt->errorCode();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$row['data_json']=json_decode($row['data_json'],true);
|
||||
$row['data_collect']=$this->DataCollect(['idoper'=>$row['idoper'],'kag'=>$param['kag']]);
|
||||
$row['param']=$param;
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
|
||||
return ['data'=>$result];
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function pushCollect($data){
|
||||
//записать данные по сборке
|
||||
|
||||
try{
|
||||
|
||||
|
||||
|
||||
|
||||
//--Повтор------------>
|
||||
$query = "select
|
||||
id
|
||||
from oper_collect
|
||||
where idoper=:id and firma=:firma and (barcode=:barcode OR barcode_json like '%".$data['barcode']."%') ORDER BY date_up";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$vars['id'] = $data['idoper'];
|
||||
$vars['firma'] = $data['kag']['firma'];
|
||||
$vars['barcode'] = $data['barcode'];
|
||||
|
||||
$stmt->execute($vars);
|
||||
$error = $stmt->errorCode();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result_b[] = $row;
|
||||
}
|
||||
// if(!$result_b){
|
||||
// $result = ['result'=>0,'error'=>$error,'rrr'=>$vars];
|
||||
// return $result;
|
||||
// }
|
||||
|
||||
|
||||
if($result_b and count($result_b)){
|
||||
$error = "Повтор ".$data['barcode'];
|
||||
$result = ['result'=>0,'error'=>$error];
|
||||
return $result;
|
||||
}
|
||||
|
||||
//<--Повтор------------
|
||||
|
||||
$vars=[
|
||||
'idoper'=>$data['idoper'],
|
||||
'date_query'=>$data['date_query'],
|
||||
'barcode' => $data['barcode'],
|
||||
'barcode_json'=> json_encode($data['barcode_json']),
|
||||
'date_up'=>date('Y-m-d H:i:s'),
|
||||
'iduser'=>$data['user']?$data['user']:0,
|
||||
'firma'=>$data['kag']['firma'],
|
||||
|
||||
];
|
||||
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
|
||||
$query="insert into oper_collect
|
||||
(idoper, firma , date_query, barcode, barcode_json, date_up, \"user\")
|
||||
values
|
||||
(:idoper, :firma, :date_query,:barcode,:barcode_json,:date_up,:iduser)";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
$stmt->execute($vars);
|
||||
|
||||
$error=$stmt->errorCode();
|
||||
$id_new=$this->conn->lastInsertId();
|
||||
$this->conn->commit();
|
||||
|
||||
|
||||
$result= ['result'=>1];
|
||||
|
||||
}catch (Exception $e) {
|
||||
|
||||
$this->conn->rollBack();
|
||||
$error = "Ошибка: " . $e->getMessage();
|
||||
$result = ['result'=>0,'error'=>$error,'data'=>$data];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function PostingStatus($params)
|
||||
{
|
||||
|
||||
//$kag=$params['kag'];
|
||||
//$posting_number=$params['posting_number'];
|
||||
|
||||
$url='https://marketplace.book-online.ru/api/posting_status/';
|
||||
$json=json_encode($params);
|
||||
$result=$this->getSSLPage($url,$json,$this->headers);
|
||||
|
||||
return json_decode($result);
|
||||
|
||||
}
|
||||
|
||||
function DeleteCollect($params)
|
||||
{
|
||||
$oper=$params['oper'];
|
||||
$firma=$params['firma'];
|
||||
|
||||
if(strlen(trim($oper))>0 and strlen($firma)>0){
|
||||
$query="delete from oper_collect
|
||||
where idoper=:id and firma=:firma";
|
||||
|
||||
$vars=['id'=>$oper,'firma'=>$firma];
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$stmt->execute($vars);
|
||||
$error=$stmt->errorCode();
|
||||
|
||||
return ['result'=>1,'error'=>$error,'vars'=>$vars];
|
||||
}
|
||||
|
||||
return ['result'=>0,'error'=>'неправильные входящие переменные!'];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
154
www/first.loc/api/Class/function.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
function http_code($code = NULL) {
|
||||
|
||||
if ($code !== NULL) {
|
||||
|
||||
switch ($code) {
|
||||
case 100: $text = 'Continue'; break;
|
||||
case 101: $text = 'Switching Protocols'; break;
|
||||
case 200: $text = 'OK'; break;
|
||||
case 201: $text = 'Created'; break;
|
||||
case 202: $text = 'Accepted'; break;
|
||||
case 203: $text = 'Non-Authoritative Information'; break;
|
||||
case 204: $text = 'No Content'; break;
|
||||
case 205: $text = 'Reset Content'; break;
|
||||
case 206: $text = 'Partial Content'; break;
|
||||
case 300: $text = 'Multiple Choices'; break;
|
||||
case 301: $text = 'Moved Permanently'; break;
|
||||
case 302: $text = 'Moved Temporarily'; break;
|
||||
case 303: $text = 'See Other'; break;
|
||||
case 304: $text = 'Not Modified'; break;
|
||||
case 305: $text = 'Use Proxy'; break;
|
||||
case 400: $text = 'Bad Request'; break;
|
||||
case 401: $text = 'Unauthorized'; break;
|
||||
case 402: $text = 'Payment Required'; break;
|
||||
case 403: $text = 'Forbidden'; break;
|
||||
case 404: $text = 'Not Found'; break;
|
||||
case 405: $text = 'Method Not Allowed'; break;
|
||||
case 406: $text = 'Not Acceptable'; break;
|
||||
case 407: $text = 'Proxy Authentication Required'; break;
|
||||
case 408: $text = 'Request Time-out'; break;
|
||||
case 409: $text = 'Conflict'; break;
|
||||
case 410: $text = 'Gone'; break;
|
||||
case 411: $text = 'Length Required'; break;
|
||||
case 412: $text = 'Precondition Failed'; break;
|
||||
case 413: $text = 'Request Entity Too Large'; break;
|
||||
case 414: $text = 'Request-URI Too Large'; break;
|
||||
case 415: $text = 'Unsupported Media Type'; break;
|
||||
case 500: $text = 'Internal Server Error'; break;
|
||||
case 501: $text = 'Not Implemented'; break;
|
||||
case 502: $text = 'Bad Gateway'; break;
|
||||
case 503: $text = 'Service Unavailable'; break;
|
||||
case 504: $text = 'Gateway Time-out'; break;
|
||||
case 505: $text = 'HTTP Version not supported'; break;
|
||||
default:
|
||||
exit('Unknown http status code "' . htmlentities($code) . '"');
|
||||
break;
|
||||
}
|
||||
|
||||
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
|
||||
|
||||
header($protocol . ' ' . $code . ' ' . $text);
|
||||
|
||||
$GLOBALS['http_response_code'] = $code;
|
||||
|
||||
} else {
|
||||
|
||||
$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
|
||||
|
||||
}
|
||||
|
||||
return $code;
|
||||
|
||||
}
|
||||
|
||||
function getQ_($url,$headers){
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
$output = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$ara=json_decode($output, true);
|
||||
return $ara;
|
||||
|
||||
}
|
||||
function getQ($url,$headers){
|
||||
//return [$url];
|
||||
// Инициализация сеанса cURL
|
||||
$ch = curl_init();
|
||||
// Установка URL
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
// Установка CURLOPT_RETURNTRANSFER (вернуть ответ в виде строки)
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
// Выполнение запроса cURL
|
||||
//$output содержит полученную строку
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
|
||||
$output = curl_exec($ch);
|
||||
// return $output;
|
||||
$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
|
||||
|
||||
if(stristr($output,'Something went wrong')){
|
||||
return [];//отправляем пустой массив
|
||||
}
|
||||
|
||||
if($http_code>=400 and $http_code<500){
|
||||
return [];//отправляем пустой массив
|
||||
}
|
||||
if($http_code>=500){
|
||||
return $http_code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$ara=json_decode($output, true);
|
||||
// закрытие сеанса curl для освобождения системных ресурсов
|
||||
curl_close($ch);
|
||||
return $ara;
|
||||
|
||||
}
|
||||
|
||||
function getSSLPage($url,$post_data,$headers) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
|
||||
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
if(strlen(trim($post_data))>0){
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
}
|
||||
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
|
||||
$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
|
||||
|
||||
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
$output = curl_exec($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
if($http_code>=400 or $http_code>=500){
|
||||
return $http_code;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
35
www/first.loc/api/authentication-jwt/Config/Database.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
# private $host = "10.10.1.246";
|
||||
# public $db_name = "tsd_users";
|
||||
#private $username = "tsd";
|
||||
##private $password = "gcIL6UrWFtSdDTbz";
|
||||
private $host = "10.10.0.20";
|
||||
public $db_name = "tsd";
|
||||
private $username = "tsd_user";
|
||||
private $password = "stptoamh";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
|
||||
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
//private $host = "localhost:3308";
|
||||
private $host = "10.10.1.246";
|
||||
//public $db_name = "authentication_jwt";
|
||||
private $db_name = "tsd_user";
|
||||
private $username = "tsd";
|
||||
//private $password = "Dfrgvm3@1";
|
||||
private $password = "gcIL6UrWFtSdDTbz";
|
||||
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
|
||||
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
16
www/first.loc/api/authentication-jwt/Config/core.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
// Показ сообщений об ошибках
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// Установим часовой пояс по умолчанию
|
||||
date_default_timezone_set("Europe/Moscow");
|
||||
|
||||
// Переменные, используемые для JWT
|
||||
$key = "d;rflgmd;lthkr;tlgd';tglldkfdfbkjldkaJNLKJDLKJDL2392093420lskfgj!!2";
|
||||
$iss = "https://tsd.book-online.ru";
|
||||
$aud = "https://tsd.book-online.ru";
|
||||
//$iat = 1356999524;
|
||||
//$nbf = 1357000000;
|
||||
$iat = time();
|
||||
$nbf = time();
|
||||
157
www/first.loc/api/authentication-jwt/Objects/User.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
class User
|
||||
{
|
||||
// Подключение к БД таблице "users"
|
||||
private $conn;
|
||||
private $table_name = "tsd_users";
|
||||
|
||||
// Свойства
|
||||
public $id;
|
||||
public $firstname;
|
||||
public $lastname;
|
||||
public $email;
|
||||
public $password;
|
||||
|
||||
// Конструктор класса User
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->conn = $db;
|
||||
}
|
||||
|
||||
// Метод для создания нового пользователя
|
||||
function create()
|
||||
{
|
||||
|
||||
// Запрос для добавления нового пользователя в БД
|
||||
$query = "INSERT INTO " . $this->table_name . "
|
||||
SET
|
||||
firstname = :firstname,
|
||||
lastname = :lastname,
|
||||
email = :email,
|
||||
password = :password";
|
||||
|
||||
// Подготовка запроса
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
// Инъекция
|
||||
$this->firstname = htmlspecialchars(strip_tags($this->firstname));
|
||||
$this->lastname = htmlspecialchars(strip_tags($this->lastname));
|
||||
$this->email = htmlspecialchars(strip_tags($this->email));
|
||||
$this->password = htmlspecialchars(strip_tags($this->password));
|
||||
|
||||
// Привязываем значения
|
||||
$stmt->bindParam(":firstname", $this->firstname);
|
||||
$stmt->bindParam(":lastname", $this->lastname);
|
||||
$stmt->bindParam(":email", $this->email);
|
||||
|
||||
// Для защиты пароля
|
||||
// Хешируем пароль перед сохранением в базу данных
|
||||
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
|
||||
$stmt->bindParam(":password", $password_hash);
|
||||
|
||||
// Выполняем запрос
|
||||
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
|
||||
if ($stmt->execute()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверка, существует ли электронная почта в нашей базе данных
|
||||
function emailExists() {
|
||||
|
||||
// Запрос, чтобы проверить, существует ли электронная почта
|
||||
$query = "SELECT id, firstname, lastname, password
|
||||
FROM " . $this->table_name . "
|
||||
WHERE email = :email
|
||||
limit 1
|
||||
";
|
||||
|
||||
// Подготовка запроса
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
// Инъекция
|
||||
$this->email=htmlspecialchars(strip_tags($this->email));
|
||||
|
||||
|
||||
// Привязываем значение e-mail
|
||||
// $stmt->bindParam("email", $this->email);
|
||||
|
||||
// Выполняем запрос
|
||||
$stmt->execute(["email"=>$this->email]);
|
||||
|
||||
// Получаем количество строк
|
||||
$num = $stmt->rowCount();
|
||||
|
||||
// Если электронная почта существует,
|
||||
// Присвоим значения свойствам объекта для легкого доступа и использования для php сессий
|
||||
if ($num > 0) {
|
||||
|
||||
// Получаем значения
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Присвоим значения свойствам объекта
|
||||
$this->id = $row["id"];
|
||||
$this->firstname = $row["firstname"];
|
||||
$this->lastname = $row["lastname"];
|
||||
$this->password = $row["password"];
|
||||
|
||||
// Вернём "true", потому что в базе данных существует электронная почта
|
||||
return ['result'=>true];
|
||||
}
|
||||
|
||||
// Вернём "false", если адрес электронной почты не существует в базе данных
|
||||
return ['result'=>false,'error'=>$this->conn->errorInfo()];
|
||||
}
|
||||
|
||||
// Здесь будет метод update()
|
||||
// Обновить запись пользователя
|
||||
public function update() {
|
||||
|
||||
// Если в HTML-форме был введен пароль (необходимо обновить пароль)
|
||||
$password_set=!empty($this->password) ? ", password = :password" : "";
|
||||
|
||||
// Если не введен пароль - не обновлять пароль
|
||||
$query = "UPDATE " . $this->table_name . "
|
||||
SET
|
||||
firstname = :firstname,
|
||||
lastname = :lastname,
|
||||
email = :email
|
||||
{$password_set}
|
||||
WHERE id = :id";
|
||||
|
||||
// Подготовка запроса
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
// Инъекция (очистка)
|
||||
$this->firstname=htmlspecialchars(strip_tags($this->firstname));
|
||||
$this->lastname=htmlspecialchars(strip_tags($this->lastname));
|
||||
$this->email=htmlspecialchars(strip_tags($this->email));
|
||||
|
||||
// Привязываем значения с HTML формы
|
||||
$stmt->bindParam(":firstname", $this->firstname);
|
||||
$stmt->bindParam(":lastname", $this->lastname);
|
||||
$stmt->bindParam(":email", $this->email);
|
||||
|
||||
// Метод password_hash () для защиты пароля пользователя в базе данных
|
||||
if(!empty($this->password)){
|
||||
$this->password=htmlspecialchars(strip_tags($this->password));
|
||||
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
|
||||
$stmt->bindParam(":password", $password_hash);
|
||||
}
|
||||
|
||||
// Уникальный идентификатор записи для редактирования
|
||||
$stmt->bindParam(":id", $this->id);
|
||||
|
||||
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
|
||||
if($stmt->execute()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
68
www/first.loc/api/authentication-jwt/create_user.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// Заголовки
|
||||
header("Access-Control-Allow-Origin:*");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
|
||||
// Подключение к БД
|
||||
// Файлы, необходимые для подключения к базе данных
|
||||
include_once __DIR__."/Config/Database.php";
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once __DIR__."/Objects/User.php";
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Создание объекта "User"
|
||||
$user = new User($db);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Отправляемые данные будут здесь
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Устанавливаем значения
|
||||
$user->firstname = $data->firstname;
|
||||
$user->lastname = $data->lastname;
|
||||
$user->email = $data->email;
|
||||
$user->password = $data->password;
|
||||
|
||||
// Поверка на существование e-mail в БД
|
||||
// $email_exists = $user->emailExists();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Здесь будет метод create()
|
||||
// Создание пользователя
|
||||
if (
|
||||
!empty($user->firstname) &&
|
||||
!empty($user->email) &&
|
||||
// $email_exists == 0 &&
|
||||
!empty($user->password) &&
|
||||
$user->create()
|
||||
) {
|
||||
// Устанавливаем код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Покажем сообщение о том, что пользователь был создан
|
||||
echo json_encode(array("message" => "Пользователь был создан"));
|
||||
}
|
||||
|
||||
// Сообщение, если не удаётся создать пользователя
|
||||
else {
|
||||
|
||||
// Устанавливаем код ответа
|
||||
http_response_code(400);
|
||||
|
||||
// Покажем сообщение о том, что создать пользователя не удалось
|
||||
echo json_encode(array("message" => "Невозможно создать пользователя"));
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class BeforeValidException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use ArrayAccess;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use OutOfBoundsException;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @implements ArrayAccess<string, Key>
|
||||
*/
|
||||
class CachedKeySet implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $jwksUri;
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $httpFactory;
|
||||
/**
|
||||
* @var CacheItemPoolInterface
|
||||
*/
|
||||
private $cache;
|
||||
/**
|
||||
* @var ?int
|
||||
*/
|
||||
private $expiresAfter;
|
||||
/**
|
||||
* @var ?CacheItemInterface
|
||||
*/
|
||||
private $cacheItem;
|
||||
/**
|
||||
* @var array<string, array<mixed>>
|
||||
*/
|
||||
private $keySet;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cacheKey;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cacheKeyPrefix = 'jwks';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxKeyLength = 64;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $rateLimit;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $rateLimitCacheKey;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxCallsPerMinute = 10;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $defaultAlg;
|
||||
|
||||
public function __construct(
|
||||
string $jwksUri,
|
||||
ClientInterface $httpClient,
|
||||
RequestFactoryInterface $httpFactory,
|
||||
CacheItemPoolInterface $cache,
|
||||
int $expiresAfter = null,
|
||||
bool $rateLimit = false,
|
||||
string $defaultAlg = null
|
||||
) {
|
||||
$this->jwksUri = $jwksUri;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->httpFactory = $httpFactory;
|
||||
$this->cache = $cache;
|
||||
$this->expiresAfter = $expiresAfter;
|
||||
$this->rateLimit = $rateLimit;
|
||||
$this->defaultAlg = $defaultAlg;
|
||||
$this->setCacheKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $keyId
|
||||
* @return Key
|
||||
*/
|
||||
public function offsetGet($keyId): Key
|
||||
{
|
||||
if (!$this->keyIdExists($keyId)) {
|
||||
throw new OutOfBoundsException('Key ID not found');
|
||||
}
|
||||
return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $keyId
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($keyId): bool
|
||||
{
|
||||
return $this->keyIdExists($keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $offset
|
||||
* @param Key $value
|
||||
*/
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
throw new LogicException('Method not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $offset
|
||||
*/
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
throw new LogicException('Method not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private function formatJwksForCache(string $jwks): array
|
||||
{
|
||||
$jwks = json_decode($jwks, true);
|
||||
|
||||
if (!isset($jwks['keys'])) {
|
||||
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||
}
|
||||
|
||||
if (empty($jwks['keys'])) {
|
||||
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
foreach ($jwks['keys'] as $k => $v) {
|
||||
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||
$keys[(string) $kid] = $v;
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
private function keyIdExists(string $keyId): bool
|
||||
{
|
||||
if (null === $this->keySet) {
|
||||
$item = $this->getCacheItem();
|
||||
// Try to load keys from cache
|
||||
if ($item->isHit()) {
|
||||
// item found! retrieve it
|
||||
$this->keySet = $item->get();
|
||||
// If the cached item is a string, the JWKS response was cached (previous behavior).
|
||||
// Parse this into expected format array<kid, jwk> instead.
|
||||
if (\is_string($this->keySet)) {
|
||||
$this->keySet = $this->formatJwksForCache($this->keySet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->keySet[$keyId])) {
|
||||
if ($this->rateLimitExceeded()) {
|
||||
return false;
|
||||
}
|
||||
$request = $this->httpFactory->createRequest('GET', $this->jwksUri);
|
||||
$jwksResponse = $this->httpClient->sendRequest($request);
|
||||
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
|
||||
|
||||
if (!isset($this->keySet[$keyId])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item = $this->getCacheItem();
|
||||
$item->set($this->keySet);
|
||||
if ($this->expiresAfter) {
|
||||
$item->expiresAfter($this->expiresAfter);
|
||||
}
|
||||
$this->cache->save($item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function rateLimitExceeded(): bool
|
||||
{
|
||||
if (!$this->rateLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
|
||||
if (!$cacheItem->isHit()) {
|
||||
$cacheItem->expiresAfter(1); // # of calls are cached each minute
|
||||
}
|
||||
|
||||
$callsPerMinute = (int) $cacheItem->get();
|
||||
if (++$callsPerMinute > $this->maxCallsPerMinute) {
|
||||
return true;
|
||||
}
|
||||
$cacheItem->set($callsPerMinute);
|
||||
$this->cache->save($cacheItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getCacheItem(): CacheItemInterface
|
||||
{
|
||||
if (\is_null($this->cacheItem)) {
|
||||
$this->cacheItem = $this->cache->getItem($this->cacheKey);
|
||||
}
|
||||
|
||||
return $this->cacheItem;
|
||||
}
|
||||
|
||||
private function setCacheKeys(): void
|
||||
{
|
||||
if (empty($this->jwksUri)) {
|
||||
throw new RuntimeException('JWKS URI is empty');
|
||||
}
|
||||
|
||||
// ensure we do not have illegal characters
|
||||
$key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
|
||||
|
||||
// add prefix
|
||||
$key = $this->cacheKeyPrefix . $key;
|
||||
|
||||
// Hash keys if they exceed $maxKeyLength of 64
|
||||
if (\strlen($key) > $this->maxKeyLength) {
|
||||
$key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
|
||||
}
|
||||
|
||||
$this->cacheKey = $key;
|
||||
|
||||
if ($this->rateLimit) {
|
||||
// add prefix
|
||||
$rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
|
||||
|
||||
// Hash keys if they exceed $maxKeyLength of 64
|
||||
if (\strlen($rateLimitKey) > $this->maxKeyLength) {
|
||||
$rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
|
||||
}
|
||||
|
||||
$this->rateLimitCacheKey = $rateLimitKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class ExpiredException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
323
www/first.loc/api/authentication-jwt/libs/php-jwt/JWK.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* JSON Web Key implementation, based on this spec:
|
||||
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Authentication
|
||||
* @package Authentication_JWT
|
||||
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||
* @link https://github.com/firebase/php-jwt
|
||||
*/
|
||||
class JWK
|
||||
{
|
||||
private const OID = '1.2.840.10045.2.1';
|
||||
private const ASN1_OBJECT_IDENTIFIER = 0x06;
|
||||
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
|
||||
private const ASN1_BIT_STRING = 0x03;
|
||||
private const EC_CURVES = [
|
||||
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
|
||||
'secp256k1' => '1.3.132.0.10', // Len: 64
|
||||
// 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported)
|
||||
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
|
||||
];
|
||||
|
||||
/**
|
||||
* Parse a set of JWK keys
|
||||
*
|
||||
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
|
||||
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||
* JSON Web Key Set
|
||||
*
|
||||
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWK Set is empty
|
||||
* @throws UnexpectedValueException Provided JWK Set was invalid
|
||||
* @throws DomainException OpenSSL failure
|
||||
*
|
||||
* @uses parseKey
|
||||
*/
|
||||
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
|
||||
{
|
||||
$keys = [];
|
||||
|
||||
if (!isset($jwks['keys'])) {
|
||||
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||
}
|
||||
|
||||
if (empty($jwks['keys'])) {
|
||||
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||
}
|
||||
|
||||
foreach ($jwks['keys'] as $k => $v) {
|
||||
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||
if ($key = self::parseKey($v, $defaultAlg)) {
|
||||
$keys[(string) $kid] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === \count($keys)) {
|
||||
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JWK key
|
||||
*
|
||||
* @param array<mixed> $jwk An individual JWK
|
||||
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||
* JSON Web Key Set
|
||||
*
|
||||
* @return Key The key object for the JWK
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWK is empty
|
||||
* @throws UnexpectedValueException Provided JWK was invalid
|
||||
* @throws DomainException OpenSSL failure
|
||||
*
|
||||
* @uses createPemFromModulusAndExponent
|
||||
*/
|
||||
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
|
||||
{
|
||||
if (empty($jwk)) {
|
||||
throw new InvalidArgumentException('JWK must not be empty');
|
||||
}
|
||||
|
||||
if (!isset($jwk['kty'])) {
|
||||
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
|
||||
}
|
||||
|
||||
if (!isset($jwk['alg'])) {
|
||||
if (\is_null($defaultAlg)) {
|
||||
// The "alg" parameter is optional in a KTY, but an algorithm is required
|
||||
// for parsing in this library. Use the $defaultAlg parameter when parsing the
|
||||
// key set in order to prevent this error.
|
||||
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
|
||||
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
|
||||
}
|
||||
$jwk['alg'] = $defaultAlg;
|
||||
}
|
||||
|
||||
switch ($jwk['kty']) {
|
||||
case 'RSA':
|
||||
if (!empty($jwk['d'])) {
|
||||
throw new UnexpectedValueException('RSA private keys are not supported');
|
||||
}
|
||||
if (!isset($jwk['n']) || !isset($jwk['e'])) {
|
||||
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
|
||||
}
|
||||
|
||||
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
|
||||
$publicKey = \openssl_pkey_get_public($pem);
|
||||
if (false === $publicKey) {
|
||||
throw new DomainException(
|
||||
'OpenSSL error: ' . \openssl_error_string()
|
||||
);
|
||||
}
|
||||
return new Key($publicKey, $jwk['alg']);
|
||||
case 'EC':
|
||||
if (isset($jwk['d'])) {
|
||||
// The key is actually a private key
|
||||
throw new UnexpectedValueException('Key data must be for a public key');
|
||||
}
|
||||
|
||||
if (empty($jwk['crv'])) {
|
||||
throw new UnexpectedValueException('crv not set');
|
||||
}
|
||||
|
||||
if (!isset(self::EC_CURVES[$jwk['crv']])) {
|
||||
throw new DomainException('Unrecognised or unsupported EC curve');
|
||||
}
|
||||
|
||||
if (empty($jwk['x']) || empty($jwk['y'])) {
|
||||
throw new UnexpectedValueException('x and y not set');
|
||||
}
|
||||
|
||||
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
|
||||
return new Key($publicKey, $jwk['alg']);
|
||||
default:
|
||||
// Currently only RSA is supported
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the EC JWK values to pem format.
|
||||
*
|
||||
* @param string $crv The EC curve (only P-256 is supported)
|
||||
* @param string $x The EC x-coordinate
|
||||
* @param string $y The EC y-coordinate
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
|
||||
{
|
||||
$pem =
|
||||
self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(
|
||||
self::ASN1_OBJECT_IDENTIFIER,
|
||||
self::encodeOID(self::OID)
|
||||
)
|
||||
. self::encodeDER(
|
||||
self::ASN1_OBJECT_IDENTIFIER,
|
||||
self::encodeOID(self::EC_CURVES[$crv])
|
||||
)
|
||||
) .
|
||||
self::encodeDER(
|
||||
self::ASN1_BIT_STRING,
|
||||
\chr(0x00) . \chr(0x04)
|
||||
. JWT::urlsafeB64Decode($x)
|
||||
. JWT::urlsafeB64Decode($y)
|
||||
)
|
||||
);
|
||||
|
||||
return sprintf(
|
||||
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
|
||||
wordwrap(base64_encode($pem), 64, "\n", true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a public key represented in PEM format from RSA modulus and exponent information
|
||||
*
|
||||
* @param string $n The RSA modulus encoded in Base64
|
||||
* @param string $e The RSA exponent encoded in Base64
|
||||
*
|
||||
* @return string The RSA public key represented in PEM format
|
||||
*
|
||||
* @uses encodeLength
|
||||
*/
|
||||
private static function createPemFromModulusAndExponent(
|
||||
string $n,
|
||||
string $e
|
||||
): string {
|
||||
$mod = JWT::urlsafeB64Decode($n);
|
||||
$exp = JWT::urlsafeB64Decode($e);
|
||||
|
||||
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
|
||||
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
|
||||
|
||||
$rsaPublicKey = \pack(
|
||||
'Ca*a*a*',
|
||||
48,
|
||||
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
|
||||
$modulus,
|
||||
$publicExponent
|
||||
);
|
||||
|
||||
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
||||
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
||||
$rsaPublicKey = \chr(0) . $rsaPublicKey;
|
||||
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
|
||||
|
||||
$rsaPublicKey = \pack(
|
||||
'Ca*a*',
|
||||
48,
|
||||
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
|
||||
$rsaOID . $rsaPublicKey
|
||||
);
|
||||
|
||||
return "-----BEGIN PUBLIC KEY-----\r\n" .
|
||||
\chunk_split(\base64_encode($rsaPublicKey), 64) .
|
||||
'-----END PUBLIC KEY-----';
|
||||
}
|
||||
|
||||
/**
|
||||
* DER-encode the length
|
||||
*
|
||||
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
||||
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
private static function encodeLength(int $length): string
|
||||
{
|
||||
if ($length <= 0x7F) {
|
||||
return \chr($length);
|
||||
}
|
||||
|
||||
$temp = \ltrim(\pack('N', $length), \chr(0));
|
||||
|
||||
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value into a DER object.
|
||||
* Also defined in Firebase\JWT\JWT
|
||||
*
|
||||
* @param int $type DER tag
|
||||
* @param string $value the value to encode
|
||||
* @return string the encoded object
|
||||
*/
|
||||
private static function encodeDER(int $type, string $value): string
|
||||
{
|
||||
$tag_header = 0;
|
||||
if ($type === self::ASN1_SEQUENCE) {
|
||||
$tag_header |= 0x20;
|
||||
}
|
||||
|
||||
// Type
|
||||
$der = \chr($tag_header | $type);
|
||||
|
||||
// Length
|
||||
$der .= \chr(\strlen($value));
|
||||
|
||||
return $der . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a string into a DER-encoded OID.
|
||||
*
|
||||
* @param string $oid the OID string
|
||||
* @return string the binary DER-encoded OID
|
||||
*/
|
||||
private static function encodeOID(string $oid): string
|
||||
{
|
||||
$octets = explode('.', $oid);
|
||||
|
||||
// Get the first octet
|
||||
$first = (int) array_shift($octets);
|
||||
$second = (int) array_shift($octets);
|
||||
$oid = \chr($first * 40 + $second);
|
||||
|
||||
// Iterate over subsequent octets
|
||||
foreach ($octets as $octet) {
|
||||
if ($octet == 0) {
|
||||
$oid .= \chr(0x00);
|
||||
continue;
|
||||
}
|
||||
$bin = '';
|
||||
|
||||
while ($octet) {
|
||||
$bin .= \chr(0x80 | ($octet & 0x7f));
|
||||
$octet >>= 7;
|
||||
}
|
||||
$bin[0] = $bin[0] & \chr(0x7f);
|
||||
|
||||
// Convert to big endian if necessary
|
||||
if (pack('V', 65534) == pack('L', 65534)) {
|
||||
$oid .= strrev($bin);
|
||||
} else {
|
||||
$oid .= $bin;
|
||||
}
|
||||
}
|
||||
|
||||
return $oid;
|
||||
}
|
||||
}
|
||||
638
www/first.loc/api/authentication-jwt/libs/php-jwt/JWT.php
Normal file
@@ -0,0 +1,638 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use ArrayAccess;
|
||||
use DateTime;
|
||||
use DomainException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use OpenSSLAsymmetricKey;
|
||||
use OpenSSLCertificate;
|
||||
use stdClass;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* JSON Web Token implementation, based on this spec:
|
||||
* https://tools.ietf.org/html/rfc7519
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Authentication
|
||||
* @package Authentication_JWT
|
||||
* @author Neuman Vong <neuman@twilio.com>
|
||||
* @author Anant Narayanan <anant@php.net>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||
* @link https://github.com/firebase/php-jwt
|
||||
*/
|
||||
class JWT
|
||||
{
|
||||
private const ASN1_INTEGER = 0x02;
|
||||
private const ASN1_SEQUENCE = 0x10;
|
||||
private const ASN1_BIT_STRING = 0x03;
|
||||
|
||||
/**
|
||||
* When checking nbf, iat or expiration times,
|
||||
* we want to provide some extra leeway time to
|
||||
* account for clock skew.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $leeway = 0;
|
||||
|
||||
/**
|
||||
* Allow the current timestamp to be specified.
|
||||
* Useful for fixing a value within unit testing.
|
||||
* Will default to PHP time() value if null.
|
||||
*
|
||||
* @var ?int
|
||||
*/
|
||||
public static $timestamp = null;
|
||||
|
||||
/**
|
||||
* @var array<string, string[]>
|
||||
*/
|
||||
public static $supported_algs = [
|
||||
'ES384' => ['openssl', 'SHA384'],
|
||||
'ES256' => ['openssl', 'SHA256'],
|
||||
'ES256K' => ['openssl', 'SHA256'],
|
||||
'HS256' => ['hash_hmac', 'SHA256'],
|
||||
'HS384' => ['hash_hmac', 'SHA384'],
|
||||
'HS512' => ['hash_hmac', 'SHA512'],
|
||||
'RS256' => ['openssl', 'SHA256'],
|
||||
'RS384' => ['openssl', 'SHA384'],
|
||||
'RS512' => ['openssl', 'SHA512'],
|
||||
'EdDSA' => ['sodium_crypto', 'EdDSA'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Decodes a JWT string into a PHP object.
|
||||
*
|
||||
* @param string $jwt The JWT
|
||||
* @param Key|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects.
|
||||
* If the algorithm used is asymmetric, this is the public key
|
||||
* Each Key object contains an algorithm and matching key.
|
||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
*
|
||||
* @return stdClass The JWT's payload as a PHP object
|
||||
*
|
||||
* @throws InvalidArgumentException Provided key/key-array was empty or malformed
|
||||
* @throws DomainException Provided JWT is malformed
|
||||
* @throws UnexpectedValueException Provided JWT was invalid
|
||||
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
||||
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
||||
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
|
||||
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
|
||||
*
|
||||
* @uses jsonDecode
|
||||
* @uses urlsafeB64Decode
|
||||
*/
|
||||
public static function decode(
|
||||
string $jwt,
|
||||
$keyOrKeyArray
|
||||
): stdClass {
|
||||
// Validate JWT
|
||||
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
||||
|
||||
if (empty($keyOrKeyArray)) {
|
||||
throw new InvalidArgumentException('Key may not be empty');
|
||||
}
|
||||
$tks = \explode('.', $jwt);
|
||||
if (\count($tks) !== 3) {
|
||||
throw new UnexpectedValueException('Wrong number of segments');
|
||||
}
|
||||
list($headb64, $bodyb64, $cryptob64) = $tks;
|
||||
$headerRaw = static::urlsafeB64Decode($headb64);
|
||||
if (null === ($header = static::jsonDecode($headerRaw))) {
|
||||
throw new UnexpectedValueException('Invalid header encoding');
|
||||
}
|
||||
$payloadRaw = static::urlsafeB64Decode($bodyb64);
|
||||
if (null === ($payload = static::jsonDecode($payloadRaw))) {
|
||||
throw new UnexpectedValueException('Invalid claims encoding');
|
||||
}
|
||||
if (\is_array($payload)) {
|
||||
// prevent PHP Fatal Error in edge-cases when payload is empty array
|
||||
$payload = (object) $payload;
|
||||
}
|
||||
if (!$payload instanceof stdClass) {
|
||||
throw new UnexpectedValueException('Payload must be a JSON object');
|
||||
}
|
||||
$sig = static::urlsafeB64Decode($cryptob64);
|
||||
if (empty($header->alg)) {
|
||||
throw new UnexpectedValueException('Empty algorithm');
|
||||
}
|
||||
if (empty(static::$supported_algs[$header->alg])) {
|
||||
throw new UnexpectedValueException('Algorithm not supported');
|
||||
}
|
||||
|
||||
$key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
|
||||
|
||||
// Check the algorithm
|
||||
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
|
||||
// See issue #351
|
||||
throw new UnexpectedValueException('Incorrect key for this algorithm');
|
||||
}
|
||||
if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
|
||||
// OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
|
||||
$sig = self::signatureToDER($sig);
|
||||
}
|
||||
if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
|
||||
throw new SignatureInvalidException('Signature verification failed');
|
||||
}
|
||||
|
||||
// Check the nbf if it is defined. This is the time that the
|
||||
// token can actually be used. If it's not yet that time, abort.
|
||||
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
|
||||
throw new BeforeValidException(
|
||||
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
|
||||
);
|
||||
}
|
||||
|
||||
// Check that this token has been created before 'now'. This prevents
|
||||
// using tokens that have been created for later use (and haven't
|
||||
// correctly used the nbf claim).
|
||||
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
|
||||
throw new BeforeValidException(
|
||||
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if this token has expired.
|
||||
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
|
||||
throw new ExpiredException('Expired token');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts and signs a PHP array into a JWT string.
|
||||
*
|
||||
* @param array<mixed> $payload PHP array
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
* @param string $keyId
|
||||
* @param array<string, string> $head An array with header elements to attach
|
||||
*
|
||||
* @return string A signed JWT
|
||||
*
|
||||
* @uses jsonEncode
|
||||
* @uses urlsafeB64Encode
|
||||
*/
|
||||
public static function encode(
|
||||
array $payload,
|
||||
$key,
|
||||
string $alg,
|
||||
string $keyId = null,
|
||||
array $head = null
|
||||
): string {
|
||||
$header = ['typ' => 'JWT', 'alg' => $alg];
|
||||
if ($keyId !== null) {
|
||||
$header['kid'] = $keyId;
|
||||
}
|
||||
if (isset($head) && \is_array($head)) {
|
||||
$header = \array_merge($head, $header);
|
||||
}
|
||||
$segments = [];
|
||||
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
|
||||
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
|
||||
$signing_input = \implode('.', $segments);
|
||||
|
||||
$signature = static::sign($signing_input, $key, $alg);
|
||||
$segments[] = static::urlsafeB64Encode($signature);
|
||||
|
||||
return \implode('.', $segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a string with a given key and algorithm.
|
||||
*
|
||||
* @param string $msg The message to sign
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
*
|
||||
* @return string An encrypted message
|
||||
*
|
||||
* @throws DomainException Unsupported algorithm or bad key was specified
|
||||
*/
|
||||
public static function sign(
|
||||
string $msg,
|
||||
$key,
|
||||
string $alg
|
||||
): string {
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'hash_hmac':
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||
}
|
||||
return \hash_hmac($algorithm, $msg, $key, true);
|
||||
case 'openssl':
|
||||
$signature = '';
|
||||
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
|
||||
if (!$success) {
|
||||
throw new DomainException('OpenSSL unable to sign data');
|
||||
}
|
||||
if ($alg === 'ES256' || $alg === 'ES256K') {
|
||||
$signature = self::signatureFromDER($signature, 256);
|
||||
} elseif ($alg === 'ES384') {
|
||||
$signature = self::signatureFromDER($signature, 384);
|
||||
}
|
||||
return $signature;
|
||||
case 'sodium_crypto':
|
||||
if (!\function_exists('sodium_crypto_sign_detached')) {
|
||||
throw new DomainException('libsodium is not available');
|
||||
}
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||
}
|
||||
try {
|
||||
// The last non-empty line is used as the key.
|
||||
$lines = array_filter(explode("\n", $key));
|
||||
$key = base64_decode((string) end($lines));
|
||||
if (\strlen($key) === 0) {
|
||||
throw new DomainException('Key cannot be empty string');
|
||||
}
|
||||
return sodium_crypto_sign_detached($msg, $key);
|
||||
} catch (Exception $e) {
|
||||
throw new DomainException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a signature with the message, key and method. Not all methods
|
||||
* are symmetric, so we must have a separate verify and sign method.
|
||||
*
|
||||
* @param string $msg The original message (header and body)
|
||||
* @param string $signature The original signature
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
|
||||
* @param string $alg The algorithm
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
|
||||
*/
|
||||
private static function verify(
|
||||
string $msg,
|
||||
string $signature,
|
||||
$keyMaterial,
|
||||
string $alg
|
||||
): bool {
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'openssl':
|
||||
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
|
||||
if ($success === 1) {
|
||||
return true;
|
||||
}
|
||||
if ($success === 0) {
|
||||
return false;
|
||||
}
|
||||
// returns 1 on success, 0 on failure, -1 on error.
|
||||
throw new DomainException(
|
||||
'OpenSSL error: ' . \openssl_error_string()
|
||||
);
|
||||
case 'sodium_crypto':
|
||||
if (!\function_exists('sodium_crypto_sign_verify_detached')) {
|
||||
throw new DomainException('libsodium is not available');
|
||||
}
|
||||
if (!\is_string($keyMaterial)) {
|
||||
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||
}
|
||||
try {
|
||||
// The last non-empty line is used as the key.
|
||||
$lines = array_filter(explode("\n", $keyMaterial));
|
||||
$key = base64_decode((string) end($lines));
|
||||
if (\strlen($key) === 0) {
|
||||
throw new DomainException('Key cannot be empty string');
|
||||
}
|
||||
if (\strlen($signature) === 0) {
|
||||
throw new DomainException('Signature cannot be empty string');
|
||||
}
|
||||
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
|
||||
} catch (Exception $e) {
|
||||
throw new DomainException($e->getMessage(), 0, $e);
|
||||
}
|
||||
case 'hash_hmac':
|
||||
default:
|
||||
if (!\is_string($keyMaterial)) {
|
||||
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||
}
|
||||
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
|
||||
return self::constantTimeEquals($hash, $signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a JSON string into a PHP object.
|
||||
*
|
||||
* @param string $input JSON string
|
||||
*
|
||||
* @return mixed The decoded JSON string
|
||||
*
|
||||
* @throws DomainException Provided string was invalid JSON
|
||||
*/
|
||||
public static function jsonDecode(string $input)
|
||||
{
|
||||
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
||||
|
||||
if ($errno = \json_last_error()) {
|
||||
self::handleJsonError($errno);
|
||||
} elseif ($obj === null && $input !== 'null') {
|
||||
throw new DomainException('Null result with non-null input');
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a PHP array into a JSON string.
|
||||
*
|
||||
* @param array<mixed> $input A PHP array
|
||||
*
|
||||
* @return string JSON representation of the PHP array
|
||||
*
|
||||
* @throws DomainException Provided object could not be encoded to valid JSON
|
||||
*/
|
||||
public static function jsonEncode(array $input): string
|
||||
{
|
||||
if (PHP_VERSION_ID >= 50400) {
|
||||
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
|
||||
} else {
|
||||
// PHP 5.3 only
|
||||
$json = \json_encode($input);
|
||||
}
|
||||
if ($errno = \json_last_error()) {
|
||||
self::handleJsonError($errno);
|
||||
} elseif ($json === 'null' && $input !== null) {
|
||||
throw new DomainException('Null result with non-null input');
|
||||
}
|
||||
if ($json === false) {
|
||||
throw new DomainException('Provided object could not be encoded to valid JSON');
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string with URL-safe Base64.
|
||||
*
|
||||
* @param string $input A Base64 encoded string
|
||||
*
|
||||
* @return string A decoded string
|
||||
*
|
||||
* @throws InvalidArgumentException invalid base64 characters
|
||||
*/
|
||||
public static function urlsafeB64Decode(string $input): string
|
||||
{
|
||||
$remainder = \strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$padlen = 4 - $remainder;
|
||||
$input .= \str_repeat('=', $padlen);
|
||||
}
|
||||
return \base64_decode(\strtr($input, '-_', '+/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a string with URL-safe Base64.
|
||||
*
|
||||
* @param string $input The string you want encoded
|
||||
*
|
||||
* @return string The base64 encode of what you passed in
|
||||
*/
|
||||
public static function urlsafeB64Encode(string $input): string
|
||||
{
|
||||
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if an algorithm has been provided for each Key
|
||||
*
|
||||
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
|
||||
* @param string|null $kid
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
*
|
||||
* @return Key
|
||||
*/
|
||||
private static function getKey(
|
||||
$keyOrKeyArray,
|
||||
?string $kid
|
||||
): Key {
|
||||
if ($keyOrKeyArray instanceof Key) {
|
||||
return $keyOrKeyArray;
|
||||
}
|
||||
|
||||
if (empty($kid)) {
|
||||
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
||||
}
|
||||
|
||||
if ($keyOrKeyArray instanceof CachedKeySet) {
|
||||
// Skip "isset" check, as this will automatically refresh if not set
|
||||
return $keyOrKeyArray[$kid];
|
||||
}
|
||||
|
||||
if (!isset($keyOrKeyArray[$kid])) {
|
||||
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
||||
}
|
||||
|
||||
return $keyOrKeyArray[$kid];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $left The string of known length to compare against
|
||||
* @param string $right The user-supplied string
|
||||
* @return bool
|
||||
*/
|
||||
public static function constantTimeEquals(string $left, string $right): bool
|
||||
{
|
||||
if (\function_exists('hash_equals')) {
|
||||
return \hash_equals($left, $right);
|
||||
}
|
||||
$len = \min(self::safeStrlen($left), self::safeStrlen($right));
|
||||
|
||||
$status = 0;
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
|
||||
}
|
||||
$status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
|
||||
|
||||
return ($status === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a JSON error.
|
||||
*
|
||||
* @param int $errno An error number from json_last_error()
|
||||
*
|
||||
* @throws DomainException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function handleJsonError(int $errno): void
|
||||
{
|
||||
$messages = [
|
||||
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
||||
];
|
||||
throw new DomainException(
|
||||
isset($messages[$errno])
|
||||
? $messages[$errno]
|
||||
: 'Unknown JSON error: ' . $errno
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes in cryptographic strings.
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private static function safeStrlen(string $str): int
|
||||
{
|
||||
if (\function_exists('mb_strlen')) {
|
||||
return \mb_strlen($str, '8bit');
|
||||
}
|
||||
return \strlen($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ECDSA signature to an ASN.1 DER sequence
|
||||
*
|
||||
* @param string $sig The ECDSA signature to convert
|
||||
* @return string The encoded DER object
|
||||
*/
|
||||
private static function signatureToDER(string $sig): string
|
||||
{
|
||||
// Separate the signature into r-value and s-value
|
||||
$length = max(1, (int) (\strlen($sig) / 2));
|
||||
list($r, $s) = \str_split($sig, $length);
|
||||
|
||||
// Trim leading zeros
|
||||
$r = \ltrim($r, "\x00");
|
||||
$s = \ltrim($s, "\x00");
|
||||
|
||||
// Convert r-value and s-value from unsigned big-endian integers to
|
||||
// signed two's complement
|
||||
if (\ord($r[0]) > 0x7f) {
|
||||
$r = "\x00" . $r;
|
||||
}
|
||||
if (\ord($s[0]) > 0x7f) {
|
||||
$s = "\x00" . $s;
|
||||
}
|
||||
|
||||
return self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(self::ASN1_INTEGER, $r) .
|
||||
self::encodeDER(self::ASN1_INTEGER, $s)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value into a DER object.
|
||||
*
|
||||
* @param int $type DER tag
|
||||
* @param string $value the value to encode
|
||||
*
|
||||
* @return string the encoded object
|
||||
*/
|
||||
private static function encodeDER(int $type, string $value): string
|
||||
{
|
||||
$tag_header = 0;
|
||||
if ($type === self::ASN1_SEQUENCE) {
|
||||
$tag_header |= 0x20;
|
||||
}
|
||||
|
||||
// Type
|
||||
$der = \chr($tag_header | $type);
|
||||
|
||||
// Length
|
||||
$der .= \chr(\strlen($value));
|
||||
|
||||
return $der . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes signature from a DER object.
|
||||
*
|
||||
* @param string $der binary signature in DER format
|
||||
* @param int $keySize the number of bits in the key
|
||||
*
|
||||
* @return string the signature
|
||||
*/
|
||||
private static function signatureFromDER(string $der, int $keySize): string
|
||||
{
|
||||
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
||||
list($offset, $_) = self::readDER($der);
|
||||
list($offset, $r) = self::readDER($der, $offset);
|
||||
list($offset, $s) = self::readDER($der, $offset);
|
||||
|
||||
// Convert r-value and s-value from signed two's compliment to unsigned
|
||||
// big-endian integers
|
||||
$r = \ltrim($r, "\x00");
|
||||
$s = \ltrim($s, "\x00");
|
||||
|
||||
// Pad out r and s so that they are $keySize bits long
|
||||
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||
|
||||
return $r . $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads binary DER-encoded data and decodes into a single object
|
||||
*
|
||||
* @param string $der the binary data in DER format
|
||||
* @param int $offset the offset of the data stream containing the object
|
||||
* to decode
|
||||
*
|
||||
* @return array{int, string|null} the new offset and the decoded object
|
||||
*/
|
||||
private static function readDER(string $der, int $offset = 0): array
|
||||
{
|
||||
$pos = $offset;
|
||||
$size = \strlen($der);
|
||||
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
|
||||
$type = \ord($der[$pos++]) & 0x1f;
|
||||
|
||||
// Length
|
||||
$len = \ord($der[$pos++]);
|
||||
if ($len & 0x80) {
|
||||
$n = $len & 0x1f;
|
||||
$len = 0;
|
||||
while ($n-- && $pos < $size) {
|
||||
$len = ($len << 8) | \ord($der[$pos++]);
|
||||
}
|
||||
}
|
||||
|
||||
// Value
|
||||
if ($type === self::ASN1_BIT_STRING) {
|
||||
$pos++; // Skip the first contents octet (padding indicator)
|
||||
$data = \substr($der, $pos, $len - 1);
|
||||
$pos += $len - 1;
|
||||
} elseif (!$constructed) {
|
||||
$data = \substr($der, $pos, $len);
|
||||
$pos += $len;
|
||||
} else {
|
||||
$data = null;
|
||||
}
|
||||
|
||||
return [$pos, $data];
|
||||
}
|
||||
}
|
||||
64
www/first.loc/api/authentication-jwt/libs/php-jwt/Key.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OpenSSLAsymmetricKey;
|
||||
use OpenSSLCertificate;
|
||||
use TypeError;
|
||||
|
||||
class Key
|
||||
{
|
||||
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
|
||||
private $keyMaterial;
|
||||
/** @var string */
|
||||
private $algorithm;
|
||||
|
||||
/**
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
|
||||
* @param string $algorithm
|
||||
*/
|
||||
public function __construct(
|
||||
$keyMaterial,
|
||||
string $algorithm
|
||||
) {
|
||||
if (
|
||||
!\is_string($keyMaterial)
|
||||
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
||||
&& !$keyMaterial instanceof OpenSSLCertificate
|
||||
&& !\is_resource($keyMaterial)
|
||||
) {
|
||||
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
|
||||
}
|
||||
|
||||
if (empty($keyMaterial)) {
|
||||
throw new InvalidArgumentException('Key material must not be empty');
|
||||
}
|
||||
|
||||
if (empty($algorithm)) {
|
||||
throw new InvalidArgumentException('Algorithm must not be empty');
|
||||
}
|
||||
|
||||
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
|
||||
$this->keyMaterial = $keyMaterial;
|
||||
$this->algorithm = $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the algorithm valid for this key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlgorithm(): string
|
||||
{
|
||||
return $this->algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
|
||||
*/
|
||||
public function getKeyMaterial()
|
||||
{
|
||||
return $this->keyMaterial;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class SignatureInvalidException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
78
www/first.loc/api/authentication-jwt/login.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
|
||||
|
||||
// Здесь будет соединение с БД
|
||||
// Файлы необходимые для соединения с БД
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once __DIR__."/Config/Database.php";
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once __DIR__."/Objects/User.php";
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Создание объекта "User"
|
||||
$user = new User($db);
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Устанавливаем значения
|
||||
$user->email = $data->email;
|
||||
$email_exists = $user->emailExists();
|
||||
|
||||
// Файлы для JWT будут здесь
|
||||
// Подключение файлов JWT
|
||||
include_once __DIR__."/Config/core.php";
|
||||
include_once __DIR__."/libs/php-jwt/BeforeValidException.php";
|
||||
include_once __DIR__."/libs/php-jwt/ExpiredException.php";
|
||||
include_once __DIR__."/libs/php-jwt/SignatureInvalidException.php";
|
||||
include_once __DIR__."/libs/php-jwt/JWT.php";
|
||||
use \Firebase\JWT\JWT;
|
||||
|
||||
// Существует ли электронная почта и соответствует ли пароль тому, что находится в базе данных
|
||||
if ($email_exists['result'] && password_verify($data->password, $user->password)) {
|
||||
|
||||
$token = array(
|
||||
"iss" => $iss,
|
||||
"aud" => $aud,
|
||||
"iat" => $iat,
|
||||
"nbf" => $nbf,
|
||||
"data" => array(
|
||||
"id" => $user->id,
|
||||
"firstname" => $user->firstname,
|
||||
"lastname" => $user->lastname,
|
||||
"email" => $user->email
|
||||
)
|
||||
);
|
||||
|
||||
// Код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Создание jwt
|
||||
$jwt = JWT::encode($token, $key, 'HS256');
|
||||
echo json_encode(
|
||||
array(
|
||||
"message" => "Успешный вход в систему",
|
||||
"jwt" => $jwt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Если электронная почта не существует или пароль не совпадает,
|
||||
// Сообщим пользователю, что он не может войти в систему
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
// http_response_code(401);
|
||||
// Скажем пользователю что войти не удалось
|
||||
echo json_encode(array("message" => "Ошибка входа","error"=>$email_exists['error']));
|
||||
}
|
||||
?>
|
||||
124
www/first.loc/api/authentication-jwt/update_user.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
// Заголовки
|
||||
header("Access-Control-Allow-Origin:*");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Требуется для кодирования веб-токена JSON
|
||||
include_once "Config/core.php";
|
||||
include_once "libs/php-jwt/BeforeValidException.php";
|
||||
include_once "libs/php-jwt/ExpiredException.php";
|
||||
include_once "libs/php-jwt/SignatureInvalidException.php";
|
||||
include_once "libs/php-jwt/JWT.php";
|
||||
include_once "libs/php-jwt/Key.php";
|
||||
|
||||
use \Firebase\JWT\JWT;
|
||||
use \Firebase\JWT\Key;
|
||||
|
||||
// Файлы, необходимые для подключения к базе данных
|
||||
include_once "Config/Database.php";
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once "Objects/User.php";
|
||||
|
||||
// Получаем соединение с БД
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Создание объекта "User"
|
||||
$user = new User($db);
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Получаем jwt
|
||||
$jwt = isset($data->jwt) ? $data->jwt : "";
|
||||
|
||||
// Если JWT не пуст
|
||||
if ($jwt) {
|
||||
|
||||
// Если декодирование выполнено успешно, показать данные пользователя
|
||||
try {
|
||||
|
||||
// Декодирование jwt
|
||||
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||
|
||||
// Нам нужно установить отправленные данные (через форму HTML) в свойствах объекта пользователя
|
||||
$user->firstname = $data->firstname;
|
||||
$user->lastname = $data->lastname;
|
||||
$user->email = $data->email;
|
||||
$user->password = $data->password;
|
||||
$user->id = $decoded->data->id;
|
||||
|
||||
// Создание пользователя
|
||||
if ($user->update()) {
|
||||
// сгенерировать заново JWT здесь --->
|
||||
|
||||
|
||||
// Нам нужно заново сгенерировать JWT, потому что данные пользователя могут отличаться
|
||||
$token = array(
|
||||
"iss" => $iss,
|
||||
"aud" => $aud,
|
||||
"iat" => $iat,
|
||||
"nbf" => $nbf,
|
||||
"data" => array(
|
||||
"id" => $user->id,
|
||||
"firstname" => $user->firstname,
|
||||
"lastname" => $user->lastname,
|
||||
"email" => $user->email
|
||||
)
|
||||
);
|
||||
|
||||
$jwt = JWT::encode($token, $key, 'HS256');
|
||||
|
||||
// Код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Ответ в формате JSON
|
||||
echo json_encode(
|
||||
array(
|
||||
"message" => "Пользователь был обновлён",
|
||||
"jwt" => $jwt
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
//<-----
|
||||
}
|
||||
|
||||
// Сообщение, если не удается обновить пользователя
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Показать сообщение об ошибке
|
||||
echo json_encode(array("message" => "Невозможно обновить пользователя"));
|
||||
}
|
||||
}
|
||||
|
||||
// Если декодирование не удалось, это означает, что JWT является недействительным
|
||||
catch (Exception $e) {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Сообщение об ошибке
|
||||
echo json_encode(array(
|
||||
"message" => "Доступ закрыт",
|
||||
"error" => $e->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Показать сообщение об ошибке, если jwt пуст
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Сообщить пользователю что доступ запрещен
|
||||
echo json_encode(array("message" => "Доступ закрыт"));
|
||||
}
|
||||
69
www/first.loc/api/authentication-jwt/validate_token.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
// Заголовки
|
||||
header("Access-Control-Allow-Origin:*");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Требуется для декодирования JWT
|
||||
include_once "Config/core.php";
|
||||
include_once "libs/php-jwt/BeforeValidException.php";
|
||||
include_once "libs/php-jwt/ExpiredException.php";
|
||||
include_once "libs/php-jwt/SignatureInvalidException.php";
|
||||
include_once "libs/php-jwt/JWT.php";
|
||||
include_once "libs/php-jwt/Key.php";
|
||||
use \Firebase\JWT\JWT;
|
||||
use \Firebase\JWT\Key;
|
||||
|
||||
// Получаем значение веб-токена JSON
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Получаем JWT
|
||||
$jwt = isset($data->jwt) ? $data->jwt : "";
|
||||
|
||||
// Если JWT не пуст
|
||||
if ($jwt) {
|
||||
|
||||
// Если декодирование выполнено успешно, показать данные пользователя
|
||||
try {
|
||||
|
||||
// Декодирование jwt
|
||||
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||
|
||||
// Код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Покажем детали
|
||||
echo json_encode(array(
|
||||
"result"=>1,
|
||||
"message" => "Доступ разрешен",
|
||||
"data" => $decoded->data
|
||||
));
|
||||
}
|
||||
|
||||
// Если декодирование не удалось, это означает, что JWT является недействительным
|
||||
catch (Exception $e) {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Сообщим пользователю что ему отказано в доступе и покажем сообщение об ошибке
|
||||
echo json_encode(array(
|
||||
"result"=>0,
|
||||
"message" => "Close ".$jwt,
|
||||
"error" => $e->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Покажем сообщение об ошибке, если JWT пуст
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
// http_response_code(401);
|
||||
|
||||
// Сообщим пользователю что доступ запрещен
|
||||
echo json_encode(array("message" => "Access denied"));
|
||||
}
|
||||
33
www/first.loc/api/delete_collect.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"),true);
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->DeleteCollect($data);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
|
||||
|
||||
|
||||
31
www/first.loc/api/getCollect.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->DataCollect($arr);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
|
||||
3
www/first.loc/api/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
echo "OOOOOO";
|
||||
?>
|
||||
40
www/first.loc/api/mysql_api/Class/DataForms.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
class DataForms{
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->conn = $db;
|
||||
}
|
||||
|
||||
function to_UTF8($arr) {
|
||||
foreach($arr as $k=>$v){
|
||||
$v=iconv('Windows-1251','UTF-8',$v);
|
||||
$arr[$k]=$v;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function Query()
|
||||
{
|
||||
|
||||
$result=[
|
||||
1,2,3
|
||||
];
|
||||
|
||||
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
31
www/first.loc/api/mysql_api/Class/Database.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
private $host = "mysql:3306";
|
||||
public $db_name = "tsd";
|
||||
private $username = "tsduser";
|
||||
|
||||
private $password = "sdWs@a201!";
|
||||
// private $username = "root";
|
||||
|
||||
// private $password = "secret";
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
|
||||
28
www/first.loc/api/mysql_api/Class/DatabaseMS.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class DatabaseMS
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
private $host = "omega-server";
|
||||
public $db_name = "SKL_BLOKSQL";
|
||||
private $username = "sa";
|
||||
private $password = "haqdvega#";
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("dblib:host=" . $this->host . ";dbname=" . $this->db_name.";charset=CP1251", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
|
||||
31
www/first.loc/api/mysql_api/Class/DatabasePGSQL.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
//private $host = "localhost:3308";
|
||||
private $host = "10.10.1.246";
|
||||
public $db_name = "tsd_users";
|
||||
//private $username = "tsd_user";
|
||||
private $username = "tsd";
|
||||
//private $password = "Dfrgvm3@1";
|
||||
private $password = "gcIL6UrWFtSdDTbz";
|
||||
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
|
||||
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
331
www/first.loc/api/mysql_api/Class/MainClass.php
Normal file
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
|
||||
|
||||
class MainClass {
|
||||
|
||||
private $headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
|
||||
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->conn = $db;
|
||||
}
|
||||
|
||||
|
||||
function WrFile($param){
|
||||
//запись в файл. Не понятно зачем, но пока оставил
|
||||
|
||||
|
||||
$path='/var/www/tmp';
|
||||
$file_name=$param['date_query'].'.json';
|
||||
if(!file_exists($path)){
|
||||
mkdir($path);
|
||||
chmod ( $path, 0777 );
|
||||
}
|
||||
$path=$path.'/beru';
|
||||
|
||||
if(!file_exists($path)){
|
||||
mkdir($path);
|
||||
chmod ( $path, 0777 );
|
||||
}
|
||||
|
||||
if(file_exists($path.'/'.$file_name)){
|
||||
unlink($path.'/'.$file_name);
|
||||
}
|
||||
file_put_contents($path.'/'.$file_name, $param['data_json']);
|
||||
|
||||
|
||||
|
||||
}
|
||||
function getSSLPage($url,$post_data,$headers) {
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
if(strlen(trim($post_data))>0){
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
}
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
$output = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
function wrDatabase($param){
|
||||
|
||||
/*
|
||||
$param=[
|
||||
`'date_query' => $date_query,
|
||||
'data_json' => $data_json,
|
||||
'db'=> $db,
|
||||
'user'= $user
|
||||
];`
|
||||
*/
|
||||
|
||||
|
||||
$opers=json_decode($param['data_json'],true);
|
||||
$user=$param['user'];
|
||||
$date_query=$param['date_query'];
|
||||
$firma=$param['kag']['firma'];
|
||||
try{
|
||||
|
||||
|
||||
for($i=0;$i<count($opers);$i++){
|
||||
|
||||
$item=$opers[$i];
|
||||
$vars=[
|
||||
'idoper'=>$item['id'],
|
||||
'data_json' => json_encode($item),
|
||||
'user'=>$user,
|
||||
'date_d'=>date('Y-m-d H:i:s'),
|
||||
'date_query'=>$date_query,
|
||||
'firma'=>$firma,
|
||||
|
||||
];
|
||||
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
//удалить предыдущие загрузки по дате загрузки и номеру акта
|
||||
|
||||
$sth = $this->conn->prepare("delete from oper where idoper=:id and firma=:firma and date_query=:date_query");
|
||||
$sth->execute([
|
||||
'id'=>$item['id'],
|
||||
'firma'=>$firma,
|
||||
'date_query'=>$date_query,
|
||||
]);
|
||||
|
||||
$query="insert into oper
|
||||
(idoper,firma, data_json,date_d,user,date_query)
|
||||
values
|
||||
(:idoper,:firma, :data_json,:date_d,:user,:date_query)";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
$stmt->execute($vars);
|
||||
|
||||
$error=$stmt->errorCode();
|
||||
$ids=$this->conn->lastInsertId();
|
||||
$this->conn->commit();
|
||||
|
||||
//idoper == >
|
||||
$ids_arr[$item['id']]=$ids;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
$result= ['result'=>1,'result'=>$ids_arr];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}catch (Exception $e) {
|
||||
|
||||
$this->conn->rollBack();
|
||||
$error = "Ошибка: " . $e->getMessage();
|
||||
$result = ['result'=>0,'error'=>$error];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function DataCollect($param){
|
||||
//результаты сканирования актов
|
||||
|
||||
|
||||
|
||||
$query = "select
|
||||
id,
|
||||
barcode,
|
||||
date_up
|
||||
from oper_collect
|
||||
where idoper=:id and firma=:firma ORDER BY date_up";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$vars['id'] = $param['idoper'];
|
||||
$vars['firma'] = $param['kag']['firma'];
|
||||
$stmt->execute($vars);
|
||||
$error = $stmt->errorCode();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
|
||||
return ['idoper'=>$param['idoper'] ,'collect'=>$result,'v'=>$vars];
|
||||
}
|
||||
|
||||
|
||||
function History($param){
|
||||
|
||||
$limit=$param['limit'];
|
||||
$kag=$param['kag'];
|
||||
|
||||
$vars=[
|
||||
'firma'=>$param['kag']['firma'],
|
||||
|
||||
];
|
||||
|
||||
$query = "select
|
||||
id,
|
||||
idoper,
|
||||
date_query,
|
||||
DATE_FORMAT(date_query,'%d.%m.%Y') as date_q,
|
||||
data_json
|
||||
from oper
|
||||
where firma=:firma
|
||||
ORDER BY date_query DESC, id DESC
|
||||
limit 10
|
||||
";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$stmt->execute($vars);
|
||||
$error = $stmt->errorCode();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$row['data_json']=json_decode($row['data_json'],true);
|
||||
$row['data_collect']=$this->DataCollect(['idoper'=>$row['idoper'],'kag'=>$param['kag']]);
|
||||
$row['param']=$param;
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
|
||||
return ['data'=>$result];
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function pushCollect($data){
|
||||
//записать данные по сборке
|
||||
|
||||
try{
|
||||
|
||||
|
||||
|
||||
|
||||
//--Повтор------------>
|
||||
$query = "select
|
||||
id
|
||||
from oper_collect
|
||||
where idoper=:id and firma=:firma and (barcode=:barcode OR barcode_json like '%".$data['barcode']."%') ORDER BY date_up";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$vars['id'] = $data['idoper'];
|
||||
$vars['firma'] = $data['kag']['firma'];
|
||||
$vars['barcode'] = $data['barcode'];
|
||||
|
||||
$stmt->execute($vars);
|
||||
$error = $stmt->errorCode();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result_b[] = $row;
|
||||
}
|
||||
if(count($result_b)){
|
||||
$error = "Повтор ".$data['barcode'];
|
||||
$result = ['result'=>0,'error'=>$error];
|
||||
return $result;
|
||||
}
|
||||
|
||||
//<--Повтор------------
|
||||
|
||||
$vars=[
|
||||
'idoper'=>$data['idoper'],
|
||||
'date_query'=>$data['date_query'],
|
||||
'barcode' => $data['barcode'],
|
||||
'barcode_json'=> json_encode($data['barcode_json']),
|
||||
'date_up'=>date('Y-m-d H:i:s'),
|
||||
'user'=>$data['user'],
|
||||
'firma'=>$data['kag']['firma'],
|
||||
|
||||
];
|
||||
|
||||
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
|
||||
$query="insert into oper_collect
|
||||
(idoper,firma , date_query, barcode,barcode_json,date_up,user)
|
||||
values
|
||||
(:idoper, :firma, :date_query,:barcode,:barcode_json,:date_up,:user)";
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
$stmt->execute($vars);
|
||||
|
||||
$error=$stmt->errorCode();
|
||||
$id_new=$this->conn->lastInsertId();
|
||||
$this->conn->commit();
|
||||
|
||||
|
||||
$result= ['result'=>1];
|
||||
|
||||
}catch (Exception $e) {
|
||||
|
||||
$this->conn->rollBack();
|
||||
$error = "Ошибка: " . $e->getMessage();
|
||||
$result = ['result'=>0,'error'=>$error,'data'=>$data];
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function PostingStatus($params)
|
||||
{
|
||||
|
||||
//$kag=$params['kag'];
|
||||
//$posting_number=$params['posting_number'];
|
||||
|
||||
$url='https://marketplace.book-online.ru/api/posting_status/';
|
||||
$json=json_encode($params);
|
||||
$result=$this->getSSLPage($url,$json,$this->headers);
|
||||
|
||||
return json_decode($result);
|
||||
|
||||
}
|
||||
|
||||
function DeleteCollect($params)
|
||||
{
|
||||
$oper=$params['oper'];
|
||||
$firma=$params['firma'];
|
||||
|
||||
if(strlen(trim($oper))>0 and strlen($firma)>0){
|
||||
$query="delete from oper_collect
|
||||
where idoper=:id and firma=:firma";
|
||||
|
||||
$vars=['id'=>$oper,'firma'=>$firma];
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$stmt->execute($vars);
|
||||
$error=$stmt->errorCode();
|
||||
|
||||
return ['result'=>1,'error'=>$error,'vars'=>$vars];
|
||||
}
|
||||
|
||||
return ['result'=>0,'error'=>'неправильные входящие переменные!'];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
145
www/first.loc/api/mysql_api/Class/function.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
function http_code($code = NULL) {
|
||||
|
||||
if ($code !== NULL) {
|
||||
|
||||
switch ($code) {
|
||||
case 100: $text = 'Continue'; break;
|
||||
case 101: $text = 'Switching Protocols'; break;
|
||||
case 200: $text = 'OK'; break;
|
||||
case 201: $text = 'Created'; break;
|
||||
case 202: $text = 'Accepted'; break;
|
||||
case 203: $text = 'Non-Authoritative Information'; break;
|
||||
case 204: $text = 'No Content'; break;
|
||||
case 205: $text = 'Reset Content'; break;
|
||||
case 206: $text = 'Partial Content'; break;
|
||||
case 300: $text = 'Multiple Choices'; break;
|
||||
case 301: $text = 'Moved Permanently'; break;
|
||||
case 302: $text = 'Moved Temporarily'; break;
|
||||
case 303: $text = 'See Other'; break;
|
||||
case 304: $text = 'Not Modified'; break;
|
||||
case 305: $text = 'Use Proxy'; break;
|
||||
case 400: $text = 'Bad Request'; break;
|
||||
case 401: $text = 'Unauthorized'; break;
|
||||
case 402: $text = 'Payment Required'; break;
|
||||
case 403: $text = 'Forbidden'; break;
|
||||
case 404: $text = 'Not Found'; break;
|
||||
case 405: $text = 'Method Not Allowed'; break;
|
||||
case 406: $text = 'Not Acceptable'; break;
|
||||
case 407: $text = 'Proxy Authentication Required'; break;
|
||||
case 408: $text = 'Request Time-out'; break;
|
||||
case 409: $text = 'Conflict'; break;
|
||||
case 410: $text = 'Gone'; break;
|
||||
case 411: $text = 'Length Required'; break;
|
||||
case 412: $text = 'Precondition Failed'; break;
|
||||
case 413: $text = 'Request Entity Too Large'; break;
|
||||
case 414: $text = 'Request-URI Too Large'; break;
|
||||
case 415: $text = 'Unsupported Media Type'; break;
|
||||
case 500: $text = 'Internal Server Error'; break;
|
||||
case 501: $text = 'Not Implemented'; break;
|
||||
case 502: $text = 'Bad Gateway'; break;
|
||||
case 503: $text = 'Service Unavailable'; break;
|
||||
case 504: $text = 'Gateway Time-out'; break;
|
||||
case 505: $text = 'HTTP Version not supported'; break;
|
||||
default:
|
||||
exit('Unknown http status code "' . htmlentities($code) . '"');
|
||||
break;
|
||||
}
|
||||
|
||||
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
|
||||
|
||||
header($protocol . ' ' . $code . ' ' . $text);
|
||||
|
||||
$GLOBALS['http_response_code'] = $code;
|
||||
|
||||
} else {
|
||||
|
||||
$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
|
||||
|
||||
}
|
||||
|
||||
return $code;
|
||||
|
||||
}
|
||||
|
||||
function getQ_($url,$headers){
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
$output = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$ara=json_decode($output, true);
|
||||
return $ara;
|
||||
|
||||
}
|
||||
function getQ($url,$headers){
|
||||
//return [$url];
|
||||
// Инициализация сеанса cURL
|
||||
$ch = curl_init();
|
||||
// Установка URL
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
// Установка CURLOPT_RETURNTRANSFER (вернуть ответ в виде строки)
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
// Выполнение запроса cURL
|
||||
//$output содержит полученную строку
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
|
||||
$output = curl_exec($ch);
|
||||
// return $output;
|
||||
$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
|
||||
|
||||
if(stristr($output,'Something went wrong')){
|
||||
return [];//отправляем пустой массив
|
||||
}
|
||||
|
||||
if($http_code>=400 and $http_code<500){
|
||||
return [];//отправляем пустой массив
|
||||
}
|
||||
if($http_code>=500){
|
||||
return $http_code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$ara=json_decode($output, true);
|
||||
// закрытие сеанса curl для освобождения системных ресурсов
|
||||
curl_close($ch);
|
||||
return $ara;
|
||||
|
||||
}
|
||||
|
||||
function getSSLPage($url,$post_data,$headers) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
|
||||
|
||||
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
if(strlen(trim($post_data))>0){
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
|
||||
}
|
||||
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
|
||||
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
|
||||
$output = curl_exec($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
private $host = "mysql:3306";
|
||||
private $db_name = "authentication_jwt";
|
||||
private $username = "tsduser";
|
||||
private $password = "sdWs@a201!";
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// Используем для подключения к базе данных MySQL
|
||||
class Database
|
||||
{
|
||||
// Учётные данные базы данных
|
||||
//private $host = "localhost:3308";
|
||||
private $host = "10.10.1.246";
|
||||
public $db_name = "authentication_jwt";
|
||||
//private $username = "tsd_user";
|
||||
private $username = "tsd";
|
||||
//private $password = "Dfrgvm3@1";
|
||||
private $password = "gcIL6UrWFtSdDTbz";
|
||||
|
||||
|
||||
public $conn;
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
public function getConnection()
|
||||
{
|
||||
$this->conn = null;
|
||||
|
||||
try {
|
||||
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
|
||||
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
|
||||
} catch (PDOException $exception) {
|
||||
echo "Ошибка соединения с БД: " . $exception->getMessage();
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
// Показ сообщений об ошибках
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// Установим часовой пояс по умолчанию
|
||||
date_default_timezone_set("Europe/Moscow");
|
||||
|
||||
// Переменные, используемые для JWT
|
||||
$key = "d;rflgmd;lthkr;tlgd';tglldkfdfbkjldkaJNLKJDLKJDL2392093420lskfgj!!2";
|
||||
$iss = "https://tsd.book-online.ru";
|
||||
$aud = "https://tsd.book-online.ru";
|
||||
//$iat = 1356999524;
|
||||
//$nbf = 1357000000;
|
||||
$iat = time();
|
||||
$nbf = time();
|
||||
157
www/first.loc/api/mysql_api/authentication-jwt/Objects/User.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
class User
|
||||
{
|
||||
// Подключение к БД таблице "users"
|
||||
private $conn;
|
||||
private $table_name = "users";
|
||||
|
||||
// Свойства
|
||||
public $id;
|
||||
public $firstname;
|
||||
public $lastname;
|
||||
public $email;
|
||||
public $password;
|
||||
|
||||
// Конструктор класса User
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->conn = $db;
|
||||
}
|
||||
|
||||
// Метод для создания нового пользователя
|
||||
function create()
|
||||
{
|
||||
|
||||
// Запрос для добавления нового пользователя в БД
|
||||
$query = "INSERT INTO " . $this->table_name . "
|
||||
SET
|
||||
firstname = :firstname,
|
||||
lastname = :lastname,
|
||||
email = :email,
|
||||
password = :password";
|
||||
|
||||
// Подготовка запроса
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
// Инъекция
|
||||
$this->firstname = htmlspecialchars(strip_tags($this->firstname));
|
||||
$this->lastname = htmlspecialchars(strip_tags($this->lastname));
|
||||
$this->email = htmlspecialchars(strip_tags($this->email));
|
||||
$this->password = htmlspecialchars(strip_tags($this->password));
|
||||
|
||||
// Привязываем значения
|
||||
$stmt->bindParam(":firstname", $this->firstname);
|
||||
$stmt->bindParam(":lastname", $this->lastname);
|
||||
$stmt->bindParam(":email", $this->email);
|
||||
|
||||
// Для защиты пароля
|
||||
// Хешируем пароль перед сохранением в базу данных
|
||||
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
|
||||
$stmt->bindParam(":password", $password_hash);
|
||||
|
||||
// Выполняем запрос
|
||||
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
|
||||
if ($stmt->execute()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверка, существует ли электронная почта в нашей базе данных
|
||||
function emailExists() {
|
||||
|
||||
// Запрос, чтобы проверить, существует ли электронная почта
|
||||
$query = "SELECT id, firstname, lastname, password
|
||||
FROM " . $this->table_name . "
|
||||
WHERE email = :email
|
||||
limit 1
|
||||
";
|
||||
|
||||
// Подготовка запроса
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
// Инъекция
|
||||
$this->email=htmlspecialchars(strip_tags($this->email));
|
||||
|
||||
|
||||
// Привязываем значение e-mail
|
||||
// $stmt->bindParam("email", $this->email);
|
||||
|
||||
// Выполняем запрос
|
||||
$stmt->execute(["email"=>$this->email]);
|
||||
|
||||
// Получаем количество строк
|
||||
$num = $stmt->rowCount();
|
||||
|
||||
// Если электронная почта существует,
|
||||
// Присвоим значения свойствам объекта для легкого доступа и использования для php сессий
|
||||
if ($num > 0) {
|
||||
|
||||
// Получаем значения
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Присвоим значения свойствам объекта
|
||||
$this->id = $row["id"];
|
||||
$this->firstname = $row["firstname"];
|
||||
$this->lastname = $row["lastname"];
|
||||
$this->password = $row["password"];
|
||||
|
||||
// Вернём "true", потому что в базе данных существует электронная почта
|
||||
return ['result'=>true];
|
||||
}
|
||||
|
||||
// Вернём "false", если адрес электронной почты не существует в базе данных
|
||||
return ['result'=>false,'error'=>$this->conn->errorInfo()];
|
||||
}
|
||||
|
||||
// Здесь будет метод update()
|
||||
// Обновить запись пользователя
|
||||
public function update() {
|
||||
|
||||
// Если в HTML-форме был введен пароль (необходимо обновить пароль)
|
||||
$password_set=!empty($this->password) ? ", password = :password" : "";
|
||||
|
||||
// Если не введен пароль - не обновлять пароль
|
||||
$query = "UPDATE " . $this->table_name . "
|
||||
SET
|
||||
firstname = :firstname,
|
||||
lastname = :lastname,
|
||||
email = :email
|
||||
{$password_set}
|
||||
WHERE id = :id";
|
||||
|
||||
// Подготовка запроса
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
// Инъекция (очистка)
|
||||
$this->firstname=htmlspecialchars(strip_tags($this->firstname));
|
||||
$this->lastname=htmlspecialchars(strip_tags($this->lastname));
|
||||
$this->email=htmlspecialchars(strip_tags($this->email));
|
||||
|
||||
// Привязываем значения с HTML формы
|
||||
$stmt->bindParam(":firstname", $this->firstname);
|
||||
$stmt->bindParam(":lastname", $this->lastname);
|
||||
$stmt->bindParam(":email", $this->email);
|
||||
|
||||
// Метод password_hash () для защиты пароля пользователя в базе данных
|
||||
if(!empty($this->password)){
|
||||
$this->password=htmlspecialchars(strip_tags($this->password));
|
||||
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
|
||||
$stmt->bindParam(":password", $password_hash);
|
||||
}
|
||||
|
||||
// Уникальный идентификатор записи для редактирования
|
||||
$stmt->bindParam(":id", $this->id);
|
||||
|
||||
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
|
||||
if($stmt->execute()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
// Заголовки
|
||||
header("Access-Control-Allow-Origin:*");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Подключение к БД
|
||||
// Файлы, необходимые для подключения к базе данных
|
||||
include_once __DIR__."/Config/Database.php";
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once __DIR__."/Objects/User.php";
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Создание объекта "User"
|
||||
$user = new User($db);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Отправляемые данные будут здесь
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Устанавливаем значения
|
||||
$user->firstname = $data->firstname;
|
||||
$user->lastname = $data->lastname;
|
||||
$user->email = $data->email;
|
||||
$user->password = $data->password;
|
||||
|
||||
// Поверка на существование e-mail в БД
|
||||
// $email_exists = $user->emailExists();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Здесь будет метод create()
|
||||
// Создание пользователя
|
||||
if (
|
||||
!empty($user->firstname) &&
|
||||
!empty($user->email) &&
|
||||
// $email_exists == 0 &&
|
||||
!empty($user->password) &&
|
||||
$user->create()
|
||||
) {
|
||||
// Устанавливаем код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Покажем сообщение о том, что пользователь был создан
|
||||
echo json_encode(array("message" => "Пользователь был создан"));
|
||||
}
|
||||
|
||||
// Сообщение, если не удаётся создать пользователя
|
||||
else {
|
||||
|
||||
// Устанавливаем код ответа
|
||||
http_response_code(400);
|
||||
|
||||
// Покажем сообщение о том, что создать пользователя не удалось
|
||||
echo json_encode(array("message" => "Невозможно создать пользователя"));
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class BeforeValidException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use ArrayAccess;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use OutOfBoundsException;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @implements ArrayAccess<string, Key>
|
||||
*/
|
||||
class CachedKeySet implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $jwksUri;
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $httpFactory;
|
||||
/**
|
||||
* @var CacheItemPoolInterface
|
||||
*/
|
||||
private $cache;
|
||||
/**
|
||||
* @var ?int
|
||||
*/
|
||||
private $expiresAfter;
|
||||
/**
|
||||
* @var ?CacheItemInterface
|
||||
*/
|
||||
private $cacheItem;
|
||||
/**
|
||||
* @var array<string, array<mixed>>
|
||||
*/
|
||||
private $keySet;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cacheKey;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cacheKeyPrefix = 'jwks';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxKeyLength = 64;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $rateLimit;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $rateLimitCacheKey;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxCallsPerMinute = 10;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $defaultAlg;
|
||||
|
||||
public function __construct(
|
||||
string $jwksUri,
|
||||
ClientInterface $httpClient,
|
||||
RequestFactoryInterface $httpFactory,
|
||||
CacheItemPoolInterface $cache,
|
||||
int $expiresAfter = null,
|
||||
bool $rateLimit = false,
|
||||
string $defaultAlg = null
|
||||
) {
|
||||
$this->jwksUri = $jwksUri;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->httpFactory = $httpFactory;
|
||||
$this->cache = $cache;
|
||||
$this->expiresAfter = $expiresAfter;
|
||||
$this->rateLimit = $rateLimit;
|
||||
$this->defaultAlg = $defaultAlg;
|
||||
$this->setCacheKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $keyId
|
||||
* @return Key
|
||||
*/
|
||||
public function offsetGet($keyId): Key
|
||||
{
|
||||
if (!$this->keyIdExists($keyId)) {
|
||||
throw new OutOfBoundsException('Key ID not found');
|
||||
}
|
||||
return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $keyId
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($keyId): bool
|
||||
{
|
||||
return $this->keyIdExists($keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $offset
|
||||
* @param Key $value
|
||||
*/
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
throw new LogicException('Method not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $offset
|
||||
*/
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
throw new LogicException('Method not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private function formatJwksForCache(string $jwks): array
|
||||
{
|
||||
$jwks = json_decode($jwks, true);
|
||||
|
||||
if (!isset($jwks['keys'])) {
|
||||
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||
}
|
||||
|
||||
if (empty($jwks['keys'])) {
|
||||
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
foreach ($jwks['keys'] as $k => $v) {
|
||||
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||
$keys[(string) $kid] = $v;
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
private function keyIdExists(string $keyId): bool
|
||||
{
|
||||
if (null === $this->keySet) {
|
||||
$item = $this->getCacheItem();
|
||||
// Try to load keys from cache
|
||||
if ($item->isHit()) {
|
||||
// item found! retrieve it
|
||||
$this->keySet = $item->get();
|
||||
// If the cached item is a string, the JWKS response was cached (previous behavior).
|
||||
// Parse this into expected format array<kid, jwk> instead.
|
||||
if (\is_string($this->keySet)) {
|
||||
$this->keySet = $this->formatJwksForCache($this->keySet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->keySet[$keyId])) {
|
||||
if ($this->rateLimitExceeded()) {
|
||||
return false;
|
||||
}
|
||||
$request = $this->httpFactory->createRequest('GET', $this->jwksUri);
|
||||
$jwksResponse = $this->httpClient->sendRequest($request);
|
||||
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
|
||||
|
||||
if (!isset($this->keySet[$keyId])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item = $this->getCacheItem();
|
||||
$item->set($this->keySet);
|
||||
if ($this->expiresAfter) {
|
||||
$item->expiresAfter($this->expiresAfter);
|
||||
}
|
||||
$this->cache->save($item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function rateLimitExceeded(): bool
|
||||
{
|
||||
if (!$this->rateLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
|
||||
if (!$cacheItem->isHit()) {
|
||||
$cacheItem->expiresAfter(1); // # of calls are cached each minute
|
||||
}
|
||||
|
||||
$callsPerMinute = (int) $cacheItem->get();
|
||||
if (++$callsPerMinute > $this->maxCallsPerMinute) {
|
||||
return true;
|
||||
}
|
||||
$cacheItem->set($callsPerMinute);
|
||||
$this->cache->save($cacheItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getCacheItem(): CacheItemInterface
|
||||
{
|
||||
if (\is_null($this->cacheItem)) {
|
||||
$this->cacheItem = $this->cache->getItem($this->cacheKey);
|
||||
}
|
||||
|
||||
return $this->cacheItem;
|
||||
}
|
||||
|
||||
private function setCacheKeys(): void
|
||||
{
|
||||
if (empty($this->jwksUri)) {
|
||||
throw new RuntimeException('JWKS URI is empty');
|
||||
}
|
||||
|
||||
// ensure we do not have illegal characters
|
||||
$key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
|
||||
|
||||
// add prefix
|
||||
$key = $this->cacheKeyPrefix . $key;
|
||||
|
||||
// Hash keys if they exceed $maxKeyLength of 64
|
||||
if (\strlen($key) > $this->maxKeyLength) {
|
||||
$key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
|
||||
}
|
||||
|
||||
$this->cacheKey = $key;
|
||||
|
||||
if ($this->rateLimit) {
|
||||
// add prefix
|
||||
$rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
|
||||
|
||||
// Hash keys if they exceed $maxKeyLength of 64
|
||||
if (\strlen($rateLimitKey) > $this->maxKeyLength) {
|
||||
$rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
|
||||
}
|
||||
|
||||
$this->rateLimitCacheKey = $rateLimitKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class ExpiredException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* JSON Web Key implementation, based on this spec:
|
||||
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Authentication
|
||||
* @package Authentication_JWT
|
||||
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||
* @link https://github.com/firebase/php-jwt
|
||||
*/
|
||||
class JWK
|
||||
{
|
||||
private const OID = '1.2.840.10045.2.1';
|
||||
private const ASN1_OBJECT_IDENTIFIER = 0x06;
|
||||
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
|
||||
private const ASN1_BIT_STRING = 0x03;
|
||||
private const EC_CURVES = [
|
||||
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
|
||||
'secp256k1' => '1.3.132.0.10', // Len: 64
|
||||
// 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported)
|
||||
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
|
||||
];
|
||||
|
||||
/**
|
||||
* Parse a set of JWK keys
|
||||
*
|
||||
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
|
||||
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||
* JSON Web Key Set
|
||||
*
|
||||
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWK Set is empty
|
||||
* @throws UnexpectedValueException Provided JWK Set was invalid
|
||||
* @throws DomainException OpenSSL failure
|
||||
*
|
||||
* @uses parseKey
|
||||
*/
|
||||
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
|
||||
{
|
||||
$keys = [];
|
||||
|
||||
if (!isset($jwks['keys'])) {
|
||||
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||
}
|
||||
|
||||
if (empty($jwks['keys'])) {
|
||||
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||
}
|
||||
|
||||
foreach ($jwks['keys'] as $k => $v) {
|
||||
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||
if ($key = self::parseKey($v, $defaultAlg)) {
|
||||
$keys[(string) $kid] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === \count($keys)) {
|
||||
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JWK key
|
||||
*
|
||||
* @param array<mixed> $jwk An individual JWK
|
||||
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||
* JSON Web Key Set
|
||||
*
|
||||
* @return Key The key object for the JWK
|
||||
*
|
||||
* @throws InvalidArgumentException Provided JWK is empty
|
||||
* @throws UnexpectedValueException Provided JWK was invalid
|
||||
* @throws DomainException OpenSSL failure
|
||||
*
|
||||
* @uses createPemFromModulusAndExponent
|
||||
*/
|
||||
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
|
||||
{
|
||||
if (empty($jwk)) {
|
||||
throw new InvalidArgumentException('JWK must not be empty');
|
||||
}
|
||||
|
||||
if (!isset($jwk['kty'])) {
|
||||
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
|
||||
}
|
||||
|
||||
if (!isset($jwk['alg'])) {
|
||||
if (\is_null($defaultAlg)) {
|
||||
// The "alg" parameter is optional in a KTY, but an algorithm is required
|
||||
// for parsing in this library. Use the $defaultAlg parameter when parsing the
|
||||
// key set in order to prevent this error.
|
||||
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
|
||||
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
|
||||
}
|
||||
$jwk['alg'] = $defaultAlg;
|
||||
}
|
||||
|
||||
switch ($jwk['kty']) {
|
||||
case 'RSA':
|
||||
if (!empty($jwk['d'])) {
|
||||
throw new UnexpectedValueException('RSA private keys are not supported');
|
||||
}
|
||||
if (!isset($jwk['n']) || !isset($jwk['e'])) {
|
||||
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
|
||||
}
|
||||
|
||||
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
|
||||
$publicKey = \openssl_pkey_get_public($pem);
|
||||
if (false === $publicKey) {
|
||||
throw new DomainException(
|
||||
'OpenSSL error: ' . \openssl_error_string()
|
||||
);
|
||||
}
|
||||
return new Key($publicKey, $jwk['alg']);
|
||||
case 'EC':
|
||||
if (isset($jwk['d'])) {
|
||||
// The key is actually a private key
|
||||
throw new UnexpectedValueException('Key data must be for a public key');
|
||||
}
|
||||
|
||||
if (empty($jwk['crv'])) {
|
||||
throw new UnexpectedValueException('crv not set');
|
||||
}
|
||||
|
||||
if (!isset(self::EC_CURVES[$jwk['crv']])) {
|
||||
throw new DomainException('Unrecognised or unsupported EC curve');
|
||||
}
|
||||
|
||||
if (empty($jwk['x']) || empty($jwk['y'])) {
|
||||
throw new UnexpectedValueException('x and y not set');
|
||||
}
|
||||
|
||||
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
|
||||
return new Key($publicKey, $jwk['alg']);
|
||||
default:
|
||||
// Currently only RSA is supported
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the EC JWK values to pem format.
|
||||
*
|
||||
* @param string $crv The EC curve (only P-256 is supported)
|
||||
* @param string $x The EC x-coordinate
|
||||
* @param string $y The EC y-coordinate
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
|
||||
{
|
||||
$pem =
|
||||
self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(
|
||||
self::ASN1_OBJECT_IDENTIFIER,
|
||||
self::encodeOID(self::OID)
|
||||
)
|
||||
. self::encodeDER(
|
||||
self::ASN1_OBJECT_IDENTIFIER,
|
||||
self::encodeOID(self::EC_CURVES[$crv])
|
||||
)
|
||||
) .
|
||||
self::encodeDER(
|
||||
self::ASN1_BIT_STRING,
|
||||
\chr(0x00) . \chr(0x04)
|
||||
. JWT::urlsafeB64Decode($x)
|
||||
. JWT::urlsafeB64Decode($y)
|
||||
)
|
||||
);
|
||||
|
||||
return sprintf(
|
||||
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
|
||||
wordwrap(base64_encode($pem), 64, "\n", true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a public key represented in PEM format from RSA modulus and exponent information
|
||||
*
|
||||
* @param string $n The RSA modulus encoded in Base64
|
||||
* @param string $e The RSA exponent encoded in Base64
|
||||
*
|
||||
* @return string The RSA public key represented in PEM format
|
||||
*
|
||||
* @uses encodeLength
|
||||
*/
|
||||
private static function createPemFromModulusAndExponent(
|
||||
string $n,
|
||||
string $e
|
||||
): string {
|
||||
$mod = JWT::urlsafeB64Decode($n);
|
||||
$exp = JWT::urlsafeB64Decode($e);
|
||||
|
||||
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
|
||||
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
|
||||
|
||||
$rsaPublicKey = \pack(
|
||||
'Ca*a*a*',
|
||||
48,
|
||||
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
|
||||
$modulus,
|
||||
$publicExponent
|
||||
);
|
||||
|
||||
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
||||
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
||||
$rsaPublicKey = \chr(0) . $rsaPublicKey;
|
||||
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
|
||||
|
||||
$rsaPublicKey = \pack(
|
||||
'Ca*a*',
|
||||
48,
|
||||
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
|
||||
$rsaOID . $rsaPublicKey
|
||||
);
|
||||
|
||||
return "-----BEGIN PUBLIC KEY-----\r\n" .
|
||||
\chunk_split(\base64_encode($rsaPublicKey), 64) .
|
||||
'-----END PUBLIC KEY-----';
|
||||
}
|
||||
|
||||
/**
|
||||
* DER-encode the length
|
||||
*
|
||||
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
||||
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
private static function encodeLength(int $length): string
|
||||
{
|
||||
if ($length <= 0x7F) {
|
||||
return \chr($length);
|
||||
}
|
||||
|
||||
$temp = \ltrim(\pack('N', $length), \chr(0));
|
||||
|
||||
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value into a DER object.
|
||||
* Also defined in Firebase\JWT\JWT
|
||||
*
|
||||
* @param int $type DER tag
|
||||
* @param string $value the value to encode
|
||||
* @return string the encoded object
|
||||
*/
|
||||
private static function encodeDER(int $type, string $value): string
|
||||
{
|
||||
$tag_header = 0;
|
||||
if ($type === self::ASN1_SEQUENCE) {
|
||||
$tag_header |= 0x20;
|
||||
}
|
||||
|
||||
// Type
|
||||
$der = \chr($tag_header | $type);
|
||||
|
||||
// Length
|
||||
$der .= \chr(\strlen($value));
|
||||
|
||||
return $der . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a string into a DER-encoded OID.
|
||||
*
|
||||
* @param string $oid the OID string
|
||||
* @return string the binary DER-encoded OID
|
||||
*/
|
||||
private static function encodeOID(string $oid): string
|
||||
{
|
||||
$octets = explode('.', $oid);
|
||||
|
||||
// Get the first octet
|
||||
$first = (int) array_shift($octets);
|
||||
$second = (int) array_shift($octets);
|
||||
$oid = \chr($first * 40 + $second);
|
||||
|
||||
// Iterate over subsequent octets
|
||||
foreach ($octets as $octet) {
|
||||
if ($octet == 0) {
|
||||
$oid .= \chr(0x00);
|
||||
continue;
|
||||
}
|
||||
$bin = '';
|
||||
|
||||
while ($octet) {
|
||||
$bin .= \chr(0x80 | ($octet & 0x7f));
|
||||
$octet >>= 7;
|
||||
}
|
||||
$bin[0] = $bin[0] & \chr(0x7f);
|
||||
|
||||
// Convert to big endian if necessary
|
||||
if (pack('V', 65534) == pack('L', 65534)) {
|
||||
$oid .= strrev($bin);
|
||||
} else {
|
||||
$oid .= $bin;
|
||||
}
|
||||
}
|
||||
|
||||
return $oid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use ArrayAccess;
|
||||
use DateTime;
|
||||
use DomainException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use OpenSSLAsymmetricKey;
|
||||
use OpenSSLCertificate;
|
||||
use stdClass;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* JSON Web Token implementation, based on this spec:
|
||||
* https://tools.ietf.org/html/rfc7519
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Authentication
|
||||
* @package Authentication_JWT
|
||||
* @author Neuman Vong <neuman@twilio.com>
|
||||
* @author Anant Narayanan <anant@php.net>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||
* @link https://github.com/firebase/php-jwt
|
||||
*/
|
||||
class JWT
|
||||
{
|
||||
private const ASN1_INTEGER = 0x02;
|
||||
private const ASN1_SEQUENCE = 0x10;
|
||||
private const ASN1_BIT_STRING = 0x03;
|
||||
|
||||
/**
|
||||
* When checking nbf, iat or expiration times,
|
||||
* we want to provide some extra leeway time to
|
||||
* account for clock skew.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $leeway = 0;
|
||||
|
||||
/**
|
||||
* Allow the current timestamp to be specified.
|
||||
* Useful for fixing a value within unit testing.
|
||||
* Will default to PHP time() value if null.
|
||||
*
|
||||
* @var ?int
|
||||
*/
|
||||
public static $timestamp = null;
|
||||
|
||||
/**
|
||||
* @var array<string, string[]>
|
||||
*/
|
||||
public static $supported_algs = [
|
||||
'ES384' => ['openssl', 'SHA384'],
|
||||
'ES256' => ['openssl', 'SHA256'],
|
||||
'ES256K' => ['openssl', 'SHA256'],
|
||||
'HS256' => ['hash_hmac', 'SHA256'],
|
||||
'HS384' => ['hash_hmac', 'SHA384'],
|
||||
'HS512' => ['hash_hmac', 'SHA512'],
|
||||
'RS256' => ['openssl', 'SHA256'],
|
||||
'RS384' => ['openssl', 'SHA384'],
|
||||
'RS512' => ['openssl', 'SHA512'],
|
||||
'EdDSA' => ['sodium_crypto', 'EdDSA'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Decodes a JWT string into a PHP object.
|
||||
*
|
||||
* @param string $jwt The JWT
|
||||
* @param Key|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects.
|
||||
* If the algorithm used is asymmetric, this is the public key
|
||||
* Each Key object contains an algorithm and matching key.
|
||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
*
|
||||
* @return stdClass The JWT's payload as a PHP object
|
||||
*
|
||||
* @throws InvalidArgumentException Provided key/key-array was empty or malformed
|
||||
* @throws DomainException Provided JWT is malformed
|
||||
* @throws UnexpectedValueException Provided JWT was invalid
|
||||
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
||||
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
||||
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
|
||||
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
|
||||
*
|
||||
* @uses jsonDecode
|
||||
* @uses urlsafeB64Decode
|
||||
*/
|
||||
public static function decode(
|
||||
string $jwt,
|
||||
$keyOrKeyArray
|
||||
): stdClass {
|
||||
// Validate JWT
|
||||
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
||||
|
||||
if (empty($keyOrKeyArray)) {
|
||||
throw new InvalidArgumentException('Key may not be empty');
|
||||
}
|
||||
$tks = \explode('.', $jwt);
|
||||
if (\count($tks) !== 3) {
|
||||
throw new UnexpectedValueException('Wrong number of segments');
|
||||
}
|
||||
list($headb64, $bodyb64, $cryptob64) = $tks;
|
||||
$headerRaw = static::urlsafeB64Decode($headb64);
|
||||
if (null === ($header = static::jsonDecode($headerRaw))) {
|
||||
throw new UnexpectedValueException('Invalid header encoding');
|
||||
}
|
||||
$payloadRaw = static::urlsafeB64Decode($bodyb64);
|
||||
if (null === ($payload = static::jsonDecode($payloadRaw))) {
|
||||
throw new UnexpectedValueException('Invalid claims encoding');
|
||||
}
|
||||
if (\is_array($payload)) {
|
||||
// prevent PHP Fatal Error in edge-cases when payload is empty array
|
||||
$payload = (object) $payload;
|
||||
}
|
||||
if (!$payload instanceof stdClass) {
|
||||
throw new UnexpectedValueException('Payload must be a JSON object');
|
||||
}
|
||||
$sig = static::urlsafeB64Decode($cryptob64);
|
||||
if (empty($header->alg)) {
|
||||
throw new UnexpectedValueException('Empty algorithm');
|
||||
}
|
||||
if (empty(static::$supported_algs[$header->alg])) {
|
||||
throw new UnexpectedValueException('Algorithm not supported');
|
||||
}
|
||||
|
||||
$key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
|
||||
|
||||
// Check the algorithm
|
||||
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
|
||||
// See issue #351
|
||||
throw new UnexpectedValueException('Incorrect key for this algorithm');
|
||||
}
|
||||
if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
|
||||
// OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
|
||||
$sig = self::signatureToDER($sig);
|
||||
}
|
||||
if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
|
||||
throw new SignatureInvalidException('Signature verification failed');
|
||||
}
|
||||
|
||||
// Check the nbf if it is defined. This is the time that the
|
||||
// token can actually be used. If it's not yet that time, abort.
|
||||
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
|
||||
throw new BeforeValidException(
|
||||
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
|
||||
);
|
||||
}
|
||||
|
||||
// Check that this token has been created before 'now'. This prevents
|
||||
// using tokens that have been created for later use (and haven't
|
||||
// correctly used the nbf claim).
|
||||
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
|
||||
throw new BeforeValidException(
|
||||
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if this token has expired.
|
||||
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
|
||||
throw new ExpiredException('Expired token');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts and signs a PHP array into a JWT string.
|
||||
*
|
||||
* @param array<mixed> $payload PHP array
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
* @param string $keyId
|
||||
* @param array<string, string> $head An array with header elements to attach
|
||||
*
|
||||
* @return string A signed JWT
|
||||
*
|
||||
* @uses jsonEncode
|
||||
* @uses urlsafeB64Encode
|
||||
*/
|
||||
public static function encode(
|
||||
array $payload,
|
||||
$key,
|
||||
string $alg,
|
||||
string $keyId = null,
|
||||
array $head = null
|
||||
): string {
|
||||
$header = ['typ' => 'JWT', 'alg' => $alg];
|
||||
if ($keyId !== null) {
|
||||
$header['kid'] = $keyId;
|
||||
}
|
||||
if (isset($head) && \is_array($head)) {
|
||||
$header = \array_merge($head, $header);
|
||||
}
|
||||
$segments = [];
|
||||
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
|
||||
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
|
||||
$signing_input = \implode('.', $segments);
|
||||
|
||||
$signature = static::sign($signing_input, $key, $alg);
|
||||
$segments[] = static::urlsafeB64Encode($signature);
|
||||
|
||||
return \implode('.', $segments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a string with a given key and algorithm.
|
||||
*
|
||||
* @param string $msg The message to sign
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
*
|
||||
* @return string An encrypted message
|
||||
*
|
||||
* @throws DomainException Unsupported algorithm or bad key was specified
|
||||
*/
|
||||
public static function sign(
|
||||
string $msg,
|
||||
$key,
|
||||
string $alg
|
||||
): string {
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'hash_hmac':
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||
}
|
||||
return \hash_hmac($algorithm, $msg, $key, true);
|
||||
case 'openssl':
|
||||
$signature = '';
|
||||
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
|
||||
if (!$success) {
|
||||
throw new DomainException('OpenSSL unable to sign data');
|
||||
}
|
||||
if ($alg === 'ES256' || $alg === 'ES256K') {
|
||||
$signature = self::signatureFromDER($signature, 256);
|
||||
} elseif ($alg === 'ES384') {
|
||||
$signature = self::signatureFromDER($signature, 384);
|
||||
}
|
||||
return $signature;
|
||||
case 'sodium_crypto':
|
||||
if (!\function_exists('sodium_crypto_sign_detached')) {
|
||||
throw new DomainException('libsodium is not available');
|
||||
}
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||
}
|
||||
try {
|
||||
// The last non-empty line is used as the key.
|
||||
$lines = array_filter(explode("\n", $key));
|
||||
$key = base64_decode((string) end($lines));
|
||||
if (\strlen($key) === 0) {
|
||||
throw new DomainException('Key cannot be empty string');
|
||||
}
|
||||
return sodium_crypto_sign_detached($msg, $key);
|
||||
} catch (Exception $e) {
|
||||
throw new DomainException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a signature with the message, key and method. Not all methods
|
||||
* are symmetric, so we must have a separate verify and sign method.
|
||||
*
|
||||
* @param string $msg The original message (header and body)
|
||||
* @param string $signature The original signature
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
|
||||
* @param string $alg The algorithm
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
|
||||
*/
|
||||
private static function verify(
|
||||
string $msg,
|
||||
string $signature,
|
||||
$keyMaterial,
|
||||
string $alg
|
||||
): bool {
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
throw new DomainException('Algorithm not supported');
|
||||
}
|
||||
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'openssl':
|
||||
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
|
||||
if ($success === 1) {
|
||||
return true;
|
||||
}
|
||||
if ($success === 0) {
|
||||
return false;
|
||||
}
|
||||
// returns 1 on success, 0 on failure, -1 on error.
|
||||
throw new DomainException(
|
||||
'OpenSSL error: ' . \openssl_error_string()
|
||||
);
|
||||
case 'sodium_crypto':
|
||||
if (!\function_exists('sodium_crypto_sign_verify_detached')) {
|
||||
throw new DomainException('libsodium is not available');
|
||||
}
|
||||
if (!\is_string($keyMaterial)) {
|
||||
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||
}
|
||||
try {
|
||||
// The last non-empty line is used as the key.
|
||||
$lines = array_filter(explode("\n", $keyMaterial));
|
||||
$key = base64_decode((string) end($lines));
|
||||
if (\strlen($key) === 0) {
|
||||
throw new DomainException('Key cannot be empty string');
|
||||
}
|
||||
if (\strlen($signature) === 0) {
|
||||
throw new DomainException('Signature cannot be empty string');
|
||||
}
|
||||
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
|
||||
} catch (Exception $e) {
|
||||
throw new DomainException($e->getMessage(), 0, $e);
|
||||
}
|
||||
case 'hash_hmac':
|
||||
default:
|
||||
if (!\is_string($keyMaterial)) {
|
||||
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||
}
|
||||
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
|
||||
return self::constantTimeEquals($hash, $signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a JSON string into a PHP object.
|
||||
*
|
||||
* @param string $input JSON string
|
||||
*
|
||||
* @return mixed The decoded JSON string
|
||||
*
|
||||
* @throws DomainException Provided string was invalid JSON
|
||||
*/
|
||||
public static function jsonDecode(string $input)
|
||||
{
|
||||
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
||||
|
||||
if ($errno = \json_last_error()) {
|
||||
self::handleJsonError($errno);
|
||||
} elseif ($obj === null && $input !== 'null') {
|
||||
throw new DomainException('Null result with non-null input');
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a PHP array into a JSON string.
|
||||
*
|
||||
* @param array<mixed> $input A PHP array
|
||||
*
|
||||
* @return string JSON representation of the PHP array
|
||||
*
|
||||
* @throws DomainException Provided object could not be encoded to valid JSON
|
||||
*/
|
||||
public static function jsonEncode(array $input): string
|
||||
{
|
||||
if (PHP_VERSION_ID >= 50400) {
|
||||
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
|
||||
} else {
|
||||
// PHP 5.3 only
|
||||
$json = \json_encode($input);
|
||||
}
|
||||
if ($errno = \json_last_error()) {
|
||||
self::handleJsonError($errno);
|
||||
} elseif ($json === 'null' && $input !== null) {
|
||||
throw new DomainException('Null result with non-null input');
|
||||
}
|
||||
if ($json === false) {
|
||||
throw new DomainException('Provided object could not be encoded to valid JSON');
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string with URL-safe Base64.
|
||||
*
|
||||
* @param string $input A Base64 encoded string
|
||||
*
|
||||
* @return string A decoded string
|
||||
*
|
||||
* @throws InvalidArgumentException invalid base64 characters
|
||||
*/
|
||||
public static function urlsafeB64Decode(string $input): string
|
||||
{
|
||||
$remainder = \strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$padlen = 4 - $remainder;
|
||||
$input .= \str_repeat('=', $padlen);
|
||||
}
|
||||
return \base64_decode(\strtr($input, '-_', '+/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a string with URL-safe Base64.
|
||||
*
|
||||
* @param string $input The string you want encoded
|
||||
*
|
||||
* @return string The base64 encode of what you passed in
|
||||
*/
|
||||
public static function urlsafeB64Encode(string $input): string
|
||||
{
|
||||
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if an algorithm has been provided for each Key
|
||||
*
|
||||
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
|
||||
* @param string|null $kid
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
*
|
||||
* @return Key
|
||||
*/
|
||||
private static function getKey(
|
||||
$keyOrKeyArray,
|
||||
?string $kid
|
||||
): Key {
|
||||
if ($keyOrKeyArray instanceof Key) {
|
||||
return $keyOrKeyArray;
|
||||
}
|
||||
|
||||
if (empty($kid)) {
|
||||
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
||||
}
|
||||
|
||||
if ($keyOrKeyArray instanceof CachedKeySet) {
|
||||
// Skip "isset" check, as this will automatically refresh if not set
|
||||
return $keyOrKeyArray[$kid];
|
||||
}
|
||||
|
||||
if (!isset($keyOrKeyArray[$kid])) {
|
||||
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
||||
}
|
||||
|
||||
return $keyOrKeyArray[$kid];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $left The string of known length to compare against
|
||||
* @param string $right The user-supplied string
|
||||
* @return bool
|
||||
*/
|
||||
public static function constantTimeEquals(string $left, string $right): bool
|
||||
{
|
||||
if (\function_exists('hash_equals')) {
|
||||
return \hash_equals($left, $right);
|
||||
}
|
||||
$len = \min(self::safeStrlen($left), self::safeStrlen($right));
|
||||
|
||||
$status = 0;
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
|
||||
}
|
||||
$status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
|
||||
|
||||
return ($status === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a JSON error.
|
||||
*
|
||||
* @param int $errno An error number from json_last_error()
|
||||
*
|
||||
* @throws DomainException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function handleJsonError(int $errno): void
|
||||
{
|
||||
$messages = [
|
||||
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
||||
];
|
||||
throw new DomainException(
|
||||
isset($messages[$errno])
|
||||
? $messages[$errno]
|
||||
: 'Unknown JSON error: ' . $errno
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes in cryptographic strings.
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private static function safeStrlen(string $str): int
|
||||
{
|
||||
if (\function_exists('mb_strlen')) {
|
||||
return \mb_strlen($str, '8bit');
|
||||
}
|
||||
return \strlen($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ECDSA signature to an ASN.1 DER sequence
|
||||
*
|
||||
* @param string $sig The ECDSA signature to convert
|
||||
* @return string The encoded DER object
|
||||
*/
|
||||
private static function signatureToDER(string $sig): string
|
||||
{
|
||||
// Separate the signature into r-value and s-value
|
||||
$length = max(1, (int) (\strlen($sig) / 2));
|
||||
list($r, $s) = \str_split($sig, $length);
|
||||
|
||||
// Trim leading zeros
|
||||
$r = \ltrim($r, "\x00");
|
||||
$s = \ltrim($s, "\x00");
|
||||
|
||||
// Convert r-value and s-value from unsigned big-endian integers to
|
||||
// signed two's complement
|
||||
if (\ord($r[0]) > 0x7f) {
|
||||
$r = "\x00" . $r;
|
||||
}
|
||||
if (\ord($s[0]) > 0x7f) {
|
||||
$s = "\x00" . $s;
|
||||
}
|
||||
|
||||
return self::encodeDER(
|
||||
self::ASN1_SEQUENCE,
|
||||
self::encodeDER(self::ASN1_INTEGER, $r) .
|
||||
self::encodeDER(self::ASN1_INTEGER, $s)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value into a DER object.
|
||||
*
|
||||
* @param int $type DER tag
|
||||
* @param string $value the value to encode
|
||||
*
|
||||
* @return string the encoded object
|
||||
*/
|
||||
private static function encodeDER(int $type, string $value): string
|
||||
{
|
||||
$tag_header = 0;
|
||||
if ($type === self::ASN1_SEQUENCE) {
|
||||
$tag_header |= 0x20;
|
||||
}
|
||||
|
||||
// Type
|
||||
$der = \chr($tag_header | $type);
|
||||
|
||||
// Length
|
||||
$der .= \chr(\strlen($value));
|
||||
|
||||
return $der . $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes signature from a DER object.
|
||||
*
|
||||
* @param string $der binary signature in DER format
|
||||
* @param int $keySize the number of bits in the key
|
||||
*
|
||||
* @return string the signature
|
||||
*/
|
||||
private static function signatureFromDER(string $der, int $keySize): string
|
||||
{
|
||||
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
||||
list($offset, $_) = self::readDER($der);
|
||||
list($offset, $r) = self::readDER($der, $offset);
|
||||
list($offset, $s) = self::readDER($der, $offset);
|
||||
|
||||
// Convert r-value and s-value from signed two's compliment to unsigned
|
||||
// big-endian integers
|
||||
$r = \ltrim($r, "\x00");
|
||||
$s = \ltrim($s, "\x00");
|
||||
|
||||
// Pad out r and s so that they are $keySize bits long
|
||||
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||
|
||||
return $r . $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads binary DER-encoded data and decodes into a single object
|
||||
*
|
||||
* @param string $der the binary data in DER format
|
||||
* @param int $offset the offset of the data stream containing the object
|
||||
* to decode
|
||||
*
|
||||
* @return array{int, string|null} the new offset and the decoded object
|
||||
*/
|
||||
private static function readDER(string $der, int $offset = 0): array
|
||||
{
|
||||
$pos = $offset;
|
||||
$size = \strlen($der);
|
||||
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
|
||||
$type = \ord($der[$pos++]) & 0x1f;
|
||||
|
||||
// Length
|
||||
$len = \ord($der[$pos++]);
|
||||
if ($len & 0x80) {
|
||||
$n = $len & 0x1f;
|
||||
$len = 0;
|
||||
while ($n-- && $pos < $size) {
|
||||
$len = ($len << 8) | \ord($der[$pos++]);
|
||||
}
|
||||
}
|
||||
|
||||
// Value
|
||||
if ($type === self::ASN1_BIT_STRING) {
|
||||
$pos++; // Skip the first contents octet (padding indicator)
|
||||
$data = \substr($der, $pos, $len - 1);
|
||||
$pos += $len - 1;
|
||||
} elseif (!$constructed) {
|
||||
$data = \substr($der, $pos, $len);
|
||||
$pos += $len;
|
||||
} else {
|
||||
$data = null;
|
||||
}
|
||||
|
||||
return [$pos, $data];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OpenSSLAsymmetricKey;
|
||||
use OpenSSLCertificate;
|
||||
use TypeError;
|
||||
|
||||
class Key
|
||||
{
|
||||
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
|
||||
private $keyMaterial;
|
||||
/** @var string */
|
||||
private $algorithm;
|
||||
|
||||
/**
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
|
||||
* @param string $algorithm
|
||||
*/
|
||||
public function __construct(
|
||||
$keyMaterial,
|
||||
string $algorithm
|
||||
) {
|
||||
if (
|
||||
!\is_string($keyMaterial)
|
||||
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
||||
&& !$keyMaterial instanceof OpenSSLCertificate
|
||||
&& !\is_resource($keyMaterial)
|
||||
) {
|
||||
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
|
||||
}
|
||||
|
||||
if (empty($keyMaterial)) {
|
||||
throw new InvalidArgumentException('Key material must not be empty');
|
||||
}
|
||||
|
||||
if (empty($algorithm)) {
|
||||
throw new InvalidArgumentException('Algorithm must not be empty');
|
||||
}
|
||||
|
||||
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
|
||||
$this->keyMaterial = $keyMaterial;
|
||||
$this->algorithm = $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the algorithm valid for this key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlgorithm(): string
|
||||
{
|
||||
return $this->algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
|
||||
*/
|
||||
public function getKeyMaterial()
|
||||
{
|
||||
return $this->keyMaterial;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Firebase\JWT;
|
||||
|
||||
class SignatureInvalidException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
78
www/first.loc/api/mysql_api/authentication-jwt/login.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
|
||||
|
||||
// Здесь будет соединение с БД
|
||||
// Файлы необходимые для соединения с БД
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once __DIR__."/Config/Database.php";
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once __DIR__."/Objects/User.php";
|
||||
|
||||
// Получаем соединение с базой данных
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Создание объекта "User"
|
||||
$user = new User($db);
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Устанавливаем значения
|
||||
$user->email = $data->email;
|
||||
$email_exists = $user->emailExists();
|
||||
|
||||
// Файлы для JWT будут здесь
|
||||
// Подключение файлов JWT
|
||||
include_once __DIR__."/Config/core.php";
|
||||
include_once __DIR__."/libs/php-jwt/BeforeValidException.php";
|
||||
include_once __DIR__."/libs/php-jwt/ExpiredException.php";
|
||||
include_once __DIR__."/libs/php-jwt/SignatureInvalidException.php";
|
||||
include_once __DIR__."/libs/php-jwt/JWT.php";
|
||||
use \Firebase\JWT\JWT;
|
||||
|
||||
// Существует ли электронная почта и соответствует ли пароль тому, что находится в базе данных
|
||||
if ($email_exists['result'] && password_verify($data->password, $user->password)) {
|
||||
|
||||
$token = array(
|
||||
"iss" => $iss,
|
||||
"aud" => $aud,
|
||||
"iat" => $iat,
|
||||
"nbf" => $nbf,
|
||||
"data" => array(
|
||||
"id" => $user->id,
|
||||
"firstname" => $user->firstname,
|
||||
"lastname" => $user->lastname,
|
||||
"email" => $user->email
|
||||
)
|
||||
);
|
||||
|
||||
// Код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Создание jwt
|
||||
$jwt = JWT::encode($token, $key, 'HS256');
|
||||
echo json_encode(
|
||||
array(
|
||||
"message" => "Успешный вход в систему",
|
||||
"jwt" => $jwt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Если электронная почта не существует или пароль не совпадает,
|
||||
// Сообщим пользователю, что он не может войти в систему
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
// http_response_code(401);
|
||||
// Скажем пользователю что войти не удалось
|
||||
echo json_encode(array("message" => "Ошибка входа","error"=>$email_exists['error']));
|
||||
}
|
||||
?>
|
||||
124
www/first.loc/api/mysql_api/authentication-jwt/update_user.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
// Заголовки
|
||||
header("Access-Control-Allow-Origin:*");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Требуется для кодирования веб-токена JSON
|
||||
include_once "Config/core.php";
|
||||
include_once "libs/php-jwt/BeforeValidException.php";
|
||||
include_once "libs/php-jwt/ExpiredException.php";
|
||||
include_once "libs/php-jwt/SignatureInvalidException.php";
|
||||
include_once "libs/php-jwt/JWT.php";
|
||||
include_once "libs/php-jwt/Key.php";
|
||||
|
||||
use \Firebase\JWT\JWT;
|
||||
use \Firebase\JWT\Key;
|
||||
|
||||
// Файлы, необходимые для подключения к базе данных
|
||||
include_once "Config/Database.php";
|
||||
//include_once __DIR__."/Config/DatabasePGSQL.php";
|
||||
include_once "Objects/User.php";
|
||||
|
||||
// Получаем соединение с БД
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
// Создание объекта "User"
|
||||
$user = new User($db);
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Получаем jwt
|
||||
$jwt = isset($data->jwt) ? $data->jwt : "";
|
||||
|
||||
// Если JWT не пуст
|
||||
if ($jwt) {
|
||||
|
||||
// Если декодирование выполнено успешно, показать данные пользователя
|
||||
try {
|
||||
|
||||
// Декодирование jwt
|
||||
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||
|
||||
// Нам нужно установить отправленные данные (через форму HTML) в свойствах объекта пользователя
|
||||
$user->firstname = $data->firstname;
|
||||
$user->lastname = $data->lastname;
|
||||
$user->email = $data->email;
|
||||
$user->password = $data->password;
|
||||
$user->id = $decoded->data->id;
|
||||
|
||||
// Создание пользователя
|
||||
if ($user->update()) {
|
||||
// сгенерировать заново JWT здесь --->
|
||||
|
||||
|
||||
// Нам нужно заново сгенерировать JWT, потому что данные пользователя могут отличаться
|
||||
$token = array(
|
||||
"iss" => $iss,
|
||||
"aud" => $aud,
|
||||
"iat" => $iat,
|
||||
"nbf" => $nbf,
|
||||
"data" => array(
|
||||
"id" => $user->id,
|
||||
"firstname" => $user->firstname,
|
||||
"lastname" => $user->lastname,
|
||||
"email" => $user->email
|
||||
)
|
||||
);
|
||||
|
||||
$jwt = JWT::encode($token, $key, 'HS256');
|
||||
|
||||
// Код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Ответ в формате JSON
|
||||
echo json_encode(
|
||||
array(
|
||||
"message" => "Пользователь был обновлён",
|
||||
"jwt" => $jwt
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
//<-----
|
||||
}
|
||||
|
||||
// Сообщение, если не удается обновить пользователя
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Показать сообщение об ошибке
|
||||
echo json_encode(array("message" => "Невозможно обновить пользователя"));
|
||||
}
|
||||
}
|
||||
|
||||
// Если декодирование не удалось, это означает, что JWT является недействительным
|
||||
catch (Exception $e) {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Сообщение об ошибке
|
||||
echo json_encode(array(
|
||||
"message" => "Доступ закрыт",
|
||||
"error" => $e->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Показать сообщение об ошибке, если jwt пуст
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Сообщить пользователю что доступ запрещен
|
||||
echo json_encode(array("message" => "Доступ закрыт"));
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
// Заголовки
|
||||
header("Access-Control-Allow-Origin:*");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Требуется для декодирования JWT
|
||||
include_once "Config/core.php";
|
||||
include_once "libs/php-jwt/BeforeValidException.php";
|
||||
include_once "libs/php-jwt/ExpiredException.php";
|
||||
include_once "libs/php-jwt/SignatureInvalidException.php";
|
||||
include_once "libs/php-jwt/JWT.php";
|
||||
include_once "libs/php-jwt/Key.php";
|
||||
use \Firebase\JWT\JWT;
|
||||
use \Firebase\JWT\Key;
|
||||
|
||||
// Получаем значение веб-токена JSON
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
// Получаем JWT
|
||||
$jwt = isset($data->jwt) ? $data->jwt : "";
|
||||
|
||||
// Если JWT не пуст
|
||||
if ($jwt) {
|
||||
|
||||
// Если декодирование выполнено успешно, показать данные пользователя
|
||||
try {
|
||||
|
||||
// Декодирование jwt
|
||||
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||
|
||||
// Код ответа
|
||||
http_response_code(200);
|
||||
|
||||
// Покажем детали
|
||||
echo json_encode(array(
|
||||
"result"=>1,
|
||||
"message" => "Доступ разрешен",
|
||||
"data" => $decoded->data
|
||||
));
|
||||
}
|
||||
|
||||
// Если декодирование не удалось, это означает, что JWT является недействительным
|
||||
catch (Exception $e) {
|
||||
|
||||
// Код ответа
|
||||
http_response_code(401);
|
||||
|
||||
// Сообщим пользователю что ему отказано в доступе и покажем сообщение об ошибке
|
||||
echo json_encode(array(
|
||||
"result"=>0,
|
||||
"message" => "Close ".$jwt,
|
||||
"error" => $e->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Покажем сообщение об ошибке, если JWT пуст
|
||||
else {
|
||||
|
||||
// Код ответа
|
||||
// http_response_code(401);
|
||||
|
||||
// Сообщим пользователю что доступ запрещен
|
||||
echo json_encode(array("message" => "Access denied"));
|
||||
}
|
||||
32
www/first.loc/api/mysql_api/delete_collect.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"),true);
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->DeleteCollect($data);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
|
||||
|
||||
|
||||
30
www/first.loc/api/mysql_api/getCollect.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->DataCollect($arr);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
|
||||
3
www/first.loc/api/mysql_api/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
echo "OOOOOO";
|
||||
?>
|
||||
32
www/first.loc/api/mysql_api/posting_status.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->PostingStatus($arr);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
|
||||
|
||||
|
||||
29
www/first.loc/api/mysql_api/pushCollect.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->pushCollect($arr);
|
||||
|
||||
$result['data_collect']=$main_class->DataCollect($arr); //результаты сканирования актов
|
||||
|
||||
echo json_encode($result);
|
||||
29
www/first.loc/api/mysql_api/queryHistory.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->History($arr);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
107
www/first.loc/api/mysql_api/queryMainPost.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$kag=$arr['kag'];
|
||||
$param_api=$arr['kag']['param_api'];
|
||||
|
||||
|
||||
|
||||
$url='https://marketplace.book-online.ru/api/shipment_barcodes/';
|
||||
|
||||
//переворачиваем дату для яндекса
|
||||
$date_query_strtotime=strtotime($arr['date_query']);
|
||||
$date_query_dmy=date('d-m-Y',$date_query_strtotime);
|
||||
|
||||
if($kag['firma']==0){
|
||||
$post_data=json_encode(['date'=> $date_query_dmy,'kag'=>$kag['kag']]);
|
||||
|
||||
}else{
|
||||
$post_data=json_encode(['date'=> $arr['date_query'],'kag'=>$kag['kag']]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
$headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
|
||||
|
||||
$data_json=getSSLPage($url,$post_data,$headers);
|
||||
|
||||
if(is_numeric($data_json) and $data_json>=400){
|
||||
http_code($data_json);
|
||||
}
|
||||
|
||||
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$user=0;
|
||||
|
||||
$param=[
|
||||
'date_query' => $arr['date_query'],
|
||||
'data_json' => $data_json,
|
||||
'user' => $user,
|
||||
'kag'=> $arr['kag']
|
||||
];
|
||||
|
||||
$data=json_decode($data_json,true);
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
//$main_class->WrFile($param);
|
||||
|
||||
if($data['error'] and strlen($data['error'])){
|
||||
$result=null;
|
||||
|
||||
}else{
|
||||
$result=$main_class->wrDatabase($param);
|
||||
|
||||
//передается несколько актов
|
||||
for($i=0;$i<count(data);$i++){
|
||||
$param1=[
|
||||
'idoper'=>$data[$i]['id'],
|
||||
'kag'=> $arr['kag'],
|
||||
];
|
||||
|
||||
//очень важно------------------>
|
||||
$data[$i]['idoper']=$data[$i]['id'];
|
||||
$data[$i]['id']=$result['result'][$data[$i]['id']];//id (в нем содержится idoper) меняется на id строки
|
||||
$data[$i]['data_collect']=$main_class->DataCollect($param1); //результаты сканирования актов
|
||||
$data[$i]['date_query']=$arr['date_query'];
|
||||
$data[$i]['date_q']=$date_query_dmy;
|
||||
$data[$i]['data_json']=['barcodes'=>$data[$i]['barcodes'],'id'=>$data[$i]['idoper']];
|
||||
//unset($data[$i]['barcodes']);
|
||||
//<------------------очень важно
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(!$result['result']){
|
||||
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>0,'error'=>$result['error'], 'id'=>0,'date_query'=>$arr['date_query'],'data_collect'=>[],'method'=>$url,'param'=>$param]);
|
||||
}else{
|
||||
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>1,'error'=>$result['error'],'id'=>$result['ids'],'idoper'=>$result['idoper'], 'date_query'=>$arr['date_query'],'data_collect'=>$data_collect,'method'=>$url,'param'=>$param]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
echo $data_;
|
||||
|
||||
4
www/first.loc/api/mysql_api/t1.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
// Получаем данные
|
||||
echo phpinfo();
|
||||
33
www/first.loc/api/posting_status.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->PostingStatus($arr);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
|
||||
|
||||
|
||||
30
www/first.loc/api/pushCollect.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
error_reporting(E_ERROR);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->pushCollect($arr);
|
||||
|
||||
$result['data_collect']=$main_class->DataCollect($arr); //результаты сканирования актов
|
||||
|
||||
echo json_encode($result);
|
||||
30
www/first.loc/api/queryHistory.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
include_once "Class/function.php";
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
|
||||
$result=$main_class->History($arr);
|
||||
|
||||
|
||||
|
||||
echo json_encode($result);
|
||||
157
www/first.loc/api/queryMainPost.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
header("Access-Control-Allow-Methods: POST");
|
||||
header("Access-Control-Max-Age: 3600");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
|
||||
|
||||
|
||||
function checkJSON($string) {
|
||||
$result = [
|
||||
'is_valid' => false,
|
||||
'type' => null,
|
||||
'error' => null
|
||||
];
|
||||
|
||||
$decoded = json_decode($string);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$result['is_valid'] = true;
|
||||
$result['type'] = gettype($decoded);
|
||||
} else {
|
||||
$result['error'] = json_last_error_msg();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
// Получаем данные
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
|
||||
|
||||
|
||||
include_once "Class/function.php";
|
||||
|
||||
$arr= json_decode($data -> data,true);
|
||||
|
||||
$kag=$arr['kag'];
|
||||
$param_api=$arr['kag']['param_api'];
|
||||
|
||||
|
||||
|
||||
$url='https://marketplace.book-online.ru/api/shipment_barcodes/';
|
||||
|
||||
//переворачиваем дату для яндекса
|
||||
$date_query_strtotime=strtotime($arr['date_query']);
|
||||
$date_query_dmy=date('d-m-Y',$date_query_strtotime);
|
||||
|
||||
if($kag['firma']==0){
|
||||
$post_data=json_encode(['date'=> $date_query_dmy,'kag'=>$kag['kag']]);
|
||||
|
||||
}else{
|
||||
$post_data=json_encode(['date'=> $arr['date_query'],'kag'=>$kag['kag']]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
$headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
|
||||
|
||||
$data_json=getSSLPage($url,$post_data,$headers);
|
||||
|
||||
//echo $data_json;
|
||||
|
||||
|
||||
$check = checkJSON($data_json);
|
||||
|
||||
if (!$check['is_valid']) {
|
||||
$data_=json_encode(['data_json'=>'','error'=>$data_json,'post_data'=>$post_data,'result'=>1,]);
|
||||
echo $data_;
|
||||
|
||||
die('');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(is_numeric($data_json) and $data_json>=400){
|
||||
http_code($data_json);
|
||||
}
|
||||
|
||||
|
||||
include_once "Class/Database.php";
|
||||
//include_once "Class/DatabasePGSQL.php";
|
||||
include_once "Class/MainClass.php";
|
||||
|
||||
$database = new Database();
|
||||
//$database = new Database();//PGSQL
|
||||
$db = $database->getConnection();
|
||||
|
||||
$user=0;
|
||||
|
||||
$param=[
|
||||
'date_query' => $arr['date_query'],
|
||||
'data_json' => $data_json,
|
||||
'user' => $user,
|
||||
'kag'=> $arr['kag']
|
||||
];
|
||||
|
||||
$data=json_decode($data_json,true);
|
||||
|
||||
$main_class = new MainClass($db);
|
||||
//$main_class->WrFile($param);
|
||||
|
||||
if(isset($data['error']) and strlen($data['error'])){
|
||||
$result=null;
|
||||
|
||||
}else{
|
||||
$result=$main_class->wrDatabase($param);
|
||||
|
||||
//передается несколько актов
|
||||
|
||||
for($i=0;$i<count($data);$i++){
|
||||
$param1=[
|
||||
'idoper'=>$data[$i]['id'],
|
||||
'kag'=> $arr['kag'],
|
||||
];
|
||||
|
||||
//очень важно------------------>
|
||||
$data[$i]['idoper']=$data[$i]['id'];
|
||||
$data[$i]['id']=$result['result'][$data[$i]['id']];//id (в нем содержится idoper) меняется на id строки
|
||||
$data[$i]['data_collect']=$main_class->DataCollect($param1); //результаты сканирования актов
|
||||
$data[$i]['date_query']=$arr['date_query'];
|
||||
$data[$i]['date_q']=$date_query_dmy;
|
||||
$data[$i]['data_json']=['barcodes'=>$data[$i]['barcodes'],'id'=>$data[$i]['idoper']];
|
||||
//unset($data[$i]['barcodes']);
|
||||
//<------------------очень важно
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(!$result['result']){
|
||||
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>0, 'id'=>0,'date_query'=>$arr['date_query'],'data_collect'=>[],'method'=>$url,'param'=>$param]);
|
||||
}else{
|
||||
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>1,'id'=>$result['ids'],'idoper'=>$result['idoper'], 'date_query'=>$arr['date_query'],'data_collect'=>$data_collect,'method'=>$url,'param'=>$param]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
echo $data_;
|
||||
|
||||
}catch (Exception $err){
|
||||
$data_=json_encode(['data_json'=>'','server_error'=>$err,'post_data'=>'','result'=>1,]);
|
||||
|
||||
echo $data_;
|
||||
|
||||
}
|
||||
4
www/first.loc/api/t1.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
// Получаем данные
|
||||
echo phpinfo();
|
||||
5
www/first.loc/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
2
www/first.loc/build.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
vue build
|
||||
19
www/first.loc/jsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
||||
10509
www/first.loc/package-lock.json
generated
Normal file
30
www/first.loc/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "tsd",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.5.1",
|
||||
"core-js": "^3.8.3",
|
||||
"ua-parser-js": "^1.0.36",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.0.3",
|
||||
"vue-sse": "^2.5.2",
|
||||
"vuex": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
}
|
||||
BIN
www/first.loc/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 234 B |
17
www/first.loc/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
219
www/first.loc/src/App.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div v-if="load">
|
||||
|
||||
<div v-if="auth" class="content">
|
||||
|
||||
<nav>
|
||||
<router-link to="/">Главная</router-link>
|
||||
<div class="a_link" @click="OutLogin">Выход</div>
|
||||
</nav>
|
||||
<router-view/>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<authorization v-else></authorization>
|
||||
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState,mapActions,mapMutations} from 'vuex';
|
||||
import Authorization from "./components/Authorization.vue";
|
||||
export default {
|
||||
components: {Authorization},
|
||||
|
||||
|
||||
data() {
|
||||
return {
|
||||
load:false,
|
||||
info:'',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
auth: state=>state.ApiClass.auth,
|
||||
url_api: state=>state.ApiClass.url_api,
|
||||
}),
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
methods:{
|
||||
...mapMutations({
|
||||
setAuth:'ApiClass/setAuth',
|
||||
setUser:'ApiClass/setUser',
|
||||
}),
|
||||
|
||||
...mapActions({
|
||||
loadData:'ApiClass/loadData',
|
||||
|
||||
}),
|
||||
|
||||
|
||||
|
||||
OutLogin(){
|
||||
this.setAuth(false);
|
||||
this.setUser({});
|
||||
localStorage.setItem('token', '');
|
||||
|
||||
},
|
||||
validateToken(){
|
||||
|
||||
//console.log(localStorage.token);
|
||||
|
||||
|
||||
if(typeof localStorage.token!=='undefined' && localStorage.token!=''){
|
||||
this.loadData({
|
||||
|
||||
method:'post',
|
||||
url:this.url_api+'/api/authentication-jwt/validate_token.php',
|
||||
json:{
|
||||
jwt:localStorage.token,
|
||||
},
|
||||
|
||||
|
||||
}).then(
|
||||
(r)=>{
|
||||
//console.log(r)
|
||||
if(typeof r.result!=='undefined' && r.result===1){
|
||||
this.info='';
|
||||
this.setAuth(true);
|
||||
this.setUser(r.data);
|
||||
}else{
|
||||
if(typeof r.error!=='undefined'){
|
||||
this.info=r.error;
|
||||
}
|
||||
this.setAuth(false);
|
||||
this.setUser({});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
).catch(
|
||||
(e)=>{
|
||||
if(typeof e.error!=='undefined'){
|
||||
this.info=e.error;
|
||||
}
|
||||
this.setAuth(false);
|
||||
this.setUser({});
|
||||
}
|
||||
|
||||
|
||||
|
||||
).finally(
|
||||
()=>{
|
||||
this.load=true;
|
||||
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
;
|
||||
}else{
|
||||
this.load=true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
mounted(){
|
||||
this.validateToken();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
@import './assets/fonts/fonts.css';
|
||||
@import './assets/main.css';
|
||||
|
||||
|
||||
*{
|
||||
margin:0;
|
||||
padding: 0;
|
||||
font-family: Inter;
|
||||
|
||||
}
|
||||
|
||||
#app {
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: #f8f8f8;
|
||||
color: #2c3e50;
|
||||
}
|
||||
.content{
|
||||
max-width: 800px;
|
||||
height: 100vh;
|
||||
margin: auto;
|
||||
background: #ffffff;
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
|
||||
nav {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap:20px;
|
||||
justify-content: end;
|
||||
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: #364d64;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: #5f88b0;
|
||||
}
|
||||
|
||||
.error_downloads{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 20px;
|
||||
background: #d0b2b2;
|
||||
border-radius: 8px;
|
||||
color: #501717;
|
||||
}
|
||||
|
||||
.a_link{
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
color: #364d64;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.a_link:hover{
|
||||
color: #09151f !important;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
6
www/first.loc/src/assets/.htaccess
Normal file
@@ -0,0 +1,6 @@
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.html$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.html [L]
|
||||
93
www/first.loc/src/assets/fonts/Inter/OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
72
www/first.loc/src/assets/fonts/Inter/README.txt
Normal file
@@ -0,0 +1,72 @@
|
||||
Inter Variable Font
|
||||
===================
|
||||
|
||||
This download contains Inter as both a variable font and static fonts.
|
||||
|
||||
Inter is a variable font with these axes:
|
||||
slnt
|
||||
wght
|
||||
|
||||
This means all the styles are contained in a single file:
|
||||
Inter-VariableFont_slnt,wght.ttf
|
||||
|
||||
If your app fully supports variable fonts, you can now pick intermediate styles
|
||||
that aren’t available as static fonts. Not all apps support variable fonts, and
|
||||
in those cases you can use the static font files for Inter:
|
||||
static/Inter-Thin.ttf
|
||||
static/Inter-ExtraLight.ttf
|
||||
static/Inter-Light.ttf
|
||||
static/Inter-Regular.ttf
|
||||
static/Inter-Medium.ttf
|
||||
static/Inter-SemiBold.ttf
|
||||
static/Inter-Bold.ttf
|
||||
static/Inter-ExtraBold.ttf
|
||||
static/Inter-Black.ttf
|
||||
|
||||
Get started
|
||||
-----------
|
||||
|
||||
1. Install the font files you want to use
|
||||
|
||||
2. Use your app's font picker to view the font family and all the
|
||||
available styles
|
||||
|
||||
Learn more about variable fonts
|
||||
-------------------------------
|
||||
|
||||
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
||||
https://variablefonts.typenetwork.com
|
||||
https://medium.com/variable-fonts
|
||||
|
||||
In desktop apps
|
||||
|
||||
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
||||
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
||||
|
||||
Online
|
||||
|
||||
https://developers.google.com/fonts/docs/getting_started
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
||||
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
||||
|
||||
Installing fonts
|
||||
|
||||
MacOS: https://support.apple.com/en-us/HT201749
|
||||
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
||||
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
||||
|
||||
Android Apps
|
||||
|
||||
https://developers.google.com/fonts/docs/android
|
||||
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
||||
|
||||
License
|
||||
-------
|
||||
Please read the full license text (OFL.txt) to understand the permissions,
|
||||
restrictions and requirements for usage, redistribution, and modification.
|
||||
|
||||
You can use them in your products & projects – print or digital,
|
||||
commercial or otherwise.
|
||||
|
||||
This isn't legal advice, please consider consulting a lawyer and see the full
|
||||
license for all details.
|
||||
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-Black.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-Bold.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-ExtraBold.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-ExtraLight.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-Light.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-Medium.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-Regular.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-SemiBold.ttf
Normal file
BIN
www/first.loc/src/assets/fonts/Inter/static/Inter-Thin.ttf
Normal file
1
www/first.loc/src/assets/fonts/README.md
Normal file
@@ -0,0 +1 @@
|
||||
шрифты
|
||||
5
www/first.loc/src/assets/fonts/fonts.css
Normal file
@@ -0,0 +1,5 @@
|
||||
@font-face {
|
||||
font-family: Inter;
|
||||
/*src:url("./Inter/Inter-VariableFont_slnt,wght.ttf");*/
|
||||
src:url("./Inter/static/Inter-Regular.ttf");
|
||||
}
|
||||
BIN
www/first.loc/src/assets/images/foreign.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
www/first.loc/src/assets/images/out2.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
www/first.loc/src/assets/images/own.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
www/first.loc/src/assets/images/progress.gif
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
1
www/first.loc/src/assets/images/progress.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0" width="109px" height="10px" viewBox="0 0 512 47" xml:space="preserve"><g><circle fill="#aca9a9" cx="-14.781" cy="22.328" r="12.813"/><animateTransform attributeName="transform" type="translate" values="88 0;182 0;251 0;298 0;321 0;323.33 0;325.66 0;327.99 0;330.32 0;332.65 0;334.98 0;337.31 0;339.64 0;341.97 0;344.3 0;346.63 0;348.96 0;351.29 0;353.62 0;356 0;379 0;426 0;494 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0;542 0" dur="3330ms" repeatCount="indefinite"/></g><g><circle fill="#aca9a9" cx="-50.328" cy="22.328" r="12.797"/><animateTransform attributeName="transform" type="translate" values="0 0;0 0;0 0;0 0;0 0;88 0;182 0;251 0;298 0;321 0;323.33 0;325.66 0;327.99 0;330.32 0;332.65 0;334.98 0;337.31 0;339.64 0;341.97 0;344.3 0;346.63 0;348.96 0;351.29 0;353.62 0;356 0;406 0;452 0;522 0;577 0;577 0;577 0;577 0;577 0;577 0;577 0;577 0;577 0;577 0" dur="3330ms" repeatCount="indefinite"/></g><g><circle fill="#aca9a9" cx="-87.203" cy="22.328" r="12.797"/><animateTransform attributeName="transform" type="translate" values="0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;88 0;182 0;251 0;298 0;321 0;323.33 0;325.66 0;327.99 0;330.32 0;332.65 0;334.98 0;337.31 0;339.64 0;341.97 0;344.3 0;346.63 0;348.96 0;351.29 0;353.62 0;356 0;403 0;450 0;520 0;614 0;614 0;614 0;614 0;614 0;614 0" dur="3330ms" repeatCount="indefinite"/></g><g><circle fill="#aca9a9" cx="-125.234" cy="22.328" r="12.797"/><animateTransform attributeName="transform" type="translate" values="0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;0 0;88 0;182 0;251 0;298 0;321 0;323.33 0;325.66 0;327.99 0;330.32 0;332.65 0;334.98 0;337.31 0;339.64 0;341.97 0;344.3 0;346.63 0;348.96 0;351.29 0;353.62 0;356 0;402 0;448 0;518 0;611 0" dur="3330ms" repeatCount="indefinite"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
77
www/first.loc/src/assets/images/sprite.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
|
||||
<symbol id="help" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2582 8.02434C11.7927 7.94449 11.314 8.03197 10.9068 8.27129C10.4996 8.5106 10.1902 8.88631 10.0335 9.33187C9.85018 9.85286 9.27926 10.1266 8.75827 9.94336C8.23728 9.76008 7.96351 9.18917 8.14678 8.66818C8.46025 7.77707 9.07898 7.02565 9.89339 6.54702C10.7078 6.06839 11.6653 5.89343 12.5964 6.05313C13.5274 6.21283 14.3719 6.69688 14.9802 7.41955C15.5884 8.14207 15.9214 9.0565 15.9201 10.0009C15.9197 11.5313 14.7851 12.5419 13.9748 13.0821C13.5392 13.3725 13.1107 13.586 12.795 13.7263C12.6358 13.7971 12.5016 13.8508 12.405 13.8876C12.3566 13.9061 12.3174 13.9204 12.2888 13.9305L12.2541 13.9427L12.243 13.9465L12.2391 13.9478L12.2376 13.9483C12.2373 13.9484 12.2363 13.9487 11.9201 13L12.2363 13.9487C11.7124 14.1234 11.1461 13.8402 10.9714 13.3162C10.7969 12.7927 11.0796 12.2267 11.6028 12.0517L11.6016 12.0521C11.6017 12.0521 11.6018 12.0521 11.6028 12.0517L11.6188 12.0461C11.6342 12.0406 11.6594 12.0315 11.693 12.0187C11.7605 11.993 11.8607 11.9529 11.9827 11.8987C12.2296 11.789 12.551 11.6276 12.8654 11.418C13.555 10.9582 13.9201 10.4692 13.9201 10L13.9201 9.99853C13.9208 9.52621 13.7543 9.06889 13.4502 8.70755C13.146 8.34622 12.7238 8.10419 12.2582 8.02434Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 15C12.5523 15 13 15.4477 13 16V16.5C13 17.0523 12.5523 17.5 12 17.5C11.4477 17.5 11 17.0523 11 16.5V16C11 15.4477 11.4477 15 12 15Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<symbol id="help1" viewBox="0 0 24 24" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" fill="#5D5D5D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2582 8.02434C11.7927 7.94449 11.314 8.03197 10.9068 8.27129C10.4996 8.5106 10.1902 8.88631 10.0335 9.33187C9.85018 9.85286 9.27926 10.1266 8.75827 9.94336C8.23728 9.76008 7.96351 9.18917 8.14678 8.66818C8.46025 7.77707 9.07898 7.02565 9.89339 6.54702C10.7078 6.06839 11.6653 5.89343 12.5964 6.05313C13.5274 6.21283 14.3719 6.69688 14.9802 7.41955C15.5884 8.14207 15.9214 9.0565 15.9201 10.0009C15.9197 11.5313 14.7851 12.5419 13.9748 13.0821C13.5392 13.3725 13.1107 13.586 12.795 13.7263C12.6358 13.7971 12.5016 13.8508 12.405 13.8876C12.3566 13.9061 12.3174 13.9204 12.2888 13.9305L12.2541 13.9427L12.243 13.9465L12.2391 13.9478L12.2376 13.9483C12.2373 13.9484 12.2363 13.9487 11.9201 13L12.2363 13.9487C11.7124 14.1234 11.1461 13.8402 10.9714 13.3162C10.7969 12.7927 11.0796 12.2267 11.6028 12.0517L11.6016 12.0521C11.6017 12.0521 11.6018 12.0521 11.6028 12.0517L11.6188 12.0461C11.6342 12.0406 11.6594 12.0315 11.693 12.0187C11.7605 11.993 11.8607 11.9529 11.9827 11.8987C12.2296 11.789 12.551 11.6276 12.8654 11.418C13.555 10.9582 13.9201 10.4692 13.9201 10L13.9201 9.99853C13.9208 9.52621 13.7543 9.06889 13.4502 8.70755C13.146 8.34622 12.7238 8.10419 12.2582 8.02434Z" fill="#5D5D5D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 15C12.5523 15 13 15.4477 13 16V16.5C13 17.0523 12.5523 17.5 12 17.5C11.4477 17.5 11 17.0523 11 16.5V16C11 15.4477 11.4477 15 12 15Z" fill="#5D5D5D"/>
|
||||
</symbol>
|
||||
|
||||
|
||||
<symbol id="star" viewBox="0 0 24 24" fill="none" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 1C12.3806 1 12.7283 1.21607 12.8967 1.55738L15.7543 7.34647L22.1447 8.28051C22.5212 8.33555 22.8339 8.59956 22.9513 8.96157C23.0687 9.32358 22.9704 9.72083 22.6978 9.98636L18.0746 14.4894L19.1656 20.851C19.23 21.2261 19.0757 21.6053 18.7678 21.8291C18.4598 22.0528 18.0515 22.0823 17.7146 21.9051L12 18.8998L6.28548 21.9051C5.94856 22.0823 5.54027 22.0528 5.2323 21.8291C4.92432 21.6053 4.77007 21.2261 4.83442 20.851L5.92551 14.4894L1.3023 9.98636C1.02968 9.72083 0.931405 9.32358 1.04878 8.96157C1.16616 8.59956 1.47884 8.33555 1.8554 8.28051L8.24577 7.34647L11.1033 1.55738C11.2718 1.21607 11.6194 1 12 1ZM12 4.25925L9.80674 8.70262C9.6612 8.99747 9.38001 9.20193 9.05466 9.24949L4.14844 9.9666L7.69776 13.4236C7.93364 13.6534 8.0413 13.9845 7.98564 14.309L7.14821 19.1917L11.5346 16.8849C11.826 16.7317 12.1741 16.7317 12.4655 16.8849L16.8518 19.1917L16.0144 14.309C15.9588 13.9845 16.0664 13.6534 16.3023 13.4236L19.8516 9.9666L14.9454 9.24949C14.62 9.20193 14.3389 8.99747 14.1933 8.70262L12 4.25925Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="success" viewBox="0 0 24 24" fill="none" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.663 3.77342C13.8902 2.98352 11.9096 2.78783 10.0166 3.21555C8.12351 3.64326 6.41942 4.67145 5.15845 6.14678C3.89749 7.62211 3.14721 9.46552 3.01951 11.4021C2.89181 13.3387 3.39354 15.2646 4.44987 16.8928C5.50619 18.5209 7.06051 19.764 8.88102 20.4365C10.7015 21.1091 12.6907 21.1752 14.5518 20.6249C16.413 20.0746 18.0464 18.9375 19.2084 17.3831C20.3705 15.8286 20.9989 13.9402 21 11.9994V11.08C21 10.5277 21.4477 10.08 22 10.08C22.5523 10.08 23 10.5277 23 11.08V12C22.9986 14.3721 22.2306 16.6807 20.8103 18.5806C19.39 20.4804 17.3936 21.8703 15.1189 22.5428C12.8442 23.2154 10.413 23.1346 8.18792 22.3126C5.96285 21.4906 4.06312 19.9713 2.77206 17.9813C1.48099 15.9914 0.86777 13.6374 1.02384 11.2705C1.17992 8.90358 2.09693 6.65052 3.63811 4.84734C5.17929 3.04416 7.26206 1.78748 9.57581 1.26472C11.8896 0.74196 14.3103 0.981129 16.477 1.94656C16.9815 2.17134 17.2082 2.76252 16.9834 3.26699C16.7587 3.77146 16.1675 3.9982 15.663 3.77342Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.7068 3.29254C23.0975 3.68287 23.0978 4.31603 22.7075 4.70675L12.7075 14.7168C12.52 14.9044 12.2656 15.0099 12.0002 15.01C11.7349 15.0101 11.4805 14.9047 11.2929 14.7171L8.29289 11.7171C7.90237 11.3266 7.90237 10.6934 8.29289 10.3029C8.68342 9.91237 9.31658 9.91237 9.70711 10.3029L11.9996 12.5954L21.2925 3.29325C21.6829 2.90253 22.316 2.90221 22.7068 3.29254Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<symbol id="Layer_1" fill="currentColor" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><g><path d="m242.1 416.2-124.2-124.2 31.1-31.1 90.9 90.9 223-255.8 33.1 28.9z"/></g><g><path d="m71.5 250.7h44v175.5h-44z" transform="matrix(.707 -.707 .707 .707 -211.971 165.259)"/></g><g><path d="m180.7 178h237.5v44h-237.5z" transform="matrix(.656 -.755 .755 .656 -47.963 294.707)"/></g></symbol>
|
||||
<symbol id="Layer_2" fill="currentColor" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m413.9 97-223 254.8-94.9-94.9-31.1 31.1 128.2 128.2 253.9-290.3z"/></symbol>
|
||||
<symbol id="Checkmark" fill="currentColor" viewBox="0 0 512 512" ><g><path d="m256 48c-114.7 0-208 93.3-208 208s93.3 208 208 208 208-93.3 208-208-93.3-208-208-208zm0 384c-97 0-176-79-176-176s79-176 176-176 176 79 176 176-79 176-176 176z"/><path d="m362.3 163.7c-6.8-5.7-16.9-4.8-22.5 2l-122.6 145.9-45.3-50.3c-5.9-6.6-16-7.1-22.6-1.2s-7.1 16-1.2 22.6l57.6 64c3 3.4 7.4 5.3 11.9 5.3h.3c4.6-.1 9-2.2 12-5.7l134.4-160c5.6-6.8 4.8-16.9-2-22.6z"/></g></symbol>
|
||||
|
||||
|
||||
<symbol id="Checked" viewBox="0 0 24 24" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.7071 5.29289C21.0976 5.68342 21.0976 6.31658 20.7071 6.70711L9.70711 17.7071C9.31658 18.0976 8.68342 18.0976 8.29289 17.7071L3.29289 12.7071C2.90237 12.3166 2.90237 11.6834 3.29289 11.2929C3.68342 10.9024 4.31658 10.9024 4.70711 11.2929L9 15.5858L19.2929 5.29289C19.6834 4.90237 20.3166 4.90237 20.7071 5.29289Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
|
||||
|
||||
|
||||
<symbol id="strelka_select" width="18" height="10" viewBox="0 0 18 10" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.292893 0.292893C0.683417 -0.0976311 1.31658 -0.0976311 1.70711 0.292893L9 7.58579L16.2929 0.292893C16.6834 -0.0976311 17.3166 -0.0976311 17.7071 0.292893C18.0976 0.683417 18.0976 1.31658 17.7071 1.70711L9.70711 9.70711C9.31658 10.0976 8.68342 10.0976 8.29289 9.70711L0.292893 1.70711C-0.0976311 1.31658 -0.0976311 0.683417 0.292893 0.292893Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="plus" viewBox="0 0 16 16">
|
||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" fill="currentColor" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="minus" viewBox="0 0 24 24" fill="currentColor" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 12C6 11.4477 6.44772 11 7 11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H7C6.44772 13 6 12.5523 6 12Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="close_bold" viewBox="0 0 41.756 41.756" >
|
||||
<g><path d="M27.948,20.878L40.291,8.536c1.953-1.953,1.953-5.119,0-7.071c-1.951-1.952-5.119-1.952-7.07,0L20.878,13.809L8.535,1.465 c-1.951-1.952-5.119-1.952-7.07,0c-1.953,1.953-1.953,5.119,0,7.071l12.342,12.342L1.465,33.22c-1.953,1.953-1.953,5.119,0,7.071 C2.44,41.268,3.721,41.755,5,41.755c1.278,0,2.56-0.487,3.535-1.464l12.343-12.342l12.343,12.343 c0.976,0.977,2.256,1.464,3.535,1.464s2.56-0.487,3.535-1.464c1.953-1.953,1.953-5.119,0-7.071L27.948,20.878z" fill="currentColor" /></g>
|
||||
</symbol>
|
||||
<symbol id="close" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.22676 4.22676C4.5291 3.92441 5.01929 3.92441 5.32163 4.22676L12 10.9051L18.6784 4.22676C18.9807 3.92441 19.4709 3.92441 19.7732 4.22676C20.0756 4.5291 20.0756 5.01929 19.7732 5.32163L13.0949 12L19.7732 18.6784C20.0756 18.9807 20.0756 19.4709 19.7732 19.7732C19.4709 20.0756 18.9807 20.0756 18.6784 19.7732L12 13.0949L5.32163 19.7732C5.01929 20.0756 4.5291 20.0756 4.22676 19.7732C3.92441 19.4709 3.92441 18.9807 4.22676 18.6784L10.9051 12L4.22676 5.32163C3.92441 5.01929 3.92441 4.5291 4.22676 4.22676Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<symbol id="search" viewBox="0 0 24 24" fill="currentColor" >
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 4C7.13401 4 4 7.13401 4 11C4 14.866 7.13401 18 11 18C14.866 18 18 14.866 18 11C18 7.13401 14.866 4 11 4ZM2 11C2 6.02944 6.02944 2 11 2C15.9706 2 20 6.02944 20 11C20 15.9706 15.9706 20 11 20C6.02944 20 2 15.9706 2 11Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9428 15.9429C16.3333 15.5524 16.9665 15.5524 17.357 15.9429L21.707 20.2929C22.0975 20.6834 22.0975 21.3166 21.707 21.7071C21.3165 22.0977 20.6833 22.0977 20.2928 21.7071L15.9428 17.3571C15.5523 16.9666 15.5523 16.3334 15.9428 15.9429Z" fill="currentColor"/>
|
||||
</symbol>
|
||||
<symbol id="three_dots" fill="currentColor" viewBox="3 0 10 15">
|
||||
<path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="edit1" viewBox="0 0 24 24" fill="none" >
|
||||
<path d="M12 3.99997H6C4.89543 3.99997 4 4.8954 4 5.99997V18C4 19.1045 4.89543 20 6 20H18C19.1046 20 20 19.1045 20 18V12M18.4142 8.41417L19.5 7.32842C20.281 6.54737 20.281 5.28104 19.5 4.5C18.7189 3.71895 17.4526 3.71895 16.6715 4.50001L15.5858 5.58575M18.4142 8.41417L12.3779 14.4505C12.0987 14.7297 11.7431 14.9201 11.356 14.9975L8.41422 15.5858L9.00257 12.6441C9.08001 12.2569 9.27032 11.9013 9.54951 11.6221L15.5858 5.58575M18.4142 8.41417L15.5858 5.58575" stroke="#444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="edit" viewBox="0 0 24 24" fill="none" >
|
||||
<path d="M14 6L8 12V16H12L18 10M14 6L17 3L21 7L18 10M14 6L18 10M10 4L4 4L4 20L20 20V14" stroke="#444" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="strelka_r" fill="currentColor" viewBox="0 0 24 24">
|
||||
<polygon points="11.293 4.707 17.586 11 4 11 4 13 17.586 13 11.293 19.293 12.707 20.707 21.414 12 12.707 3.293 11.293 4.707" />
|
||||
</symbol>
|
||||
|
||||
|
||||
</defs>
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
BIN
www/first.loc/src/assets/images/tt.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
www/first.loc/src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
26
www/first.loc/src/assets/main.css
Normal file
@@ -0,0 +1,26 @@
|
||||
:root {
|
||||
--value-background: #eeeded;
|
||||
--grey-color:#282828;
|
||||
--grey-mini:#888888;
|
||||
--grey-hover:#dedede;
|
||||
--grey-label:#727272;
|
||||
--grey-disabled:#f6f6f6;
|
||||
--color-disabled:#757575;
|
||||
--color-text-input: #444;
|
||||
--black:#000;
|
||||
--white:#fff;
|
||||
--border-color: #dddee0;
|
||||
--secondary-hover-color:#5028C6;
|
||||
--primary-hover-color:#2342C0;
|
||||
--primary-background:#4263EB;
|
||||
--background-button-primary: #adadad;
|
||||
--background-button-primary-hover: #9d9d9d;
|
||||
--background-button-color-primary: #fffff;
|
||||
--grey-1: #c0c0c0;
|
||||
--background-green: #abdc77;
|
||||
--background-green-hover: #86b654;
|
||||
--background-blue: #aadefa;
|
||||
--grey-2:#9d9d9d;
|
||||
--grey-4: #eeeeee;
|
||||
|
||||
}
|
||||
163
www/first.loc/src/components/Authorization.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
|
||||
<div class="dialog" >
|
||||
|
||||
<div class="dialog__content">
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<my-input :type_input="`text`"
|
||||
|
||||
:exception="true"
|
||||
v-model="email"
|
||||
placeholder="Логин"/>
|
||||
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<my-input :type_input="`password`"
|
||||
|
||||
:exception="true"
|
||||
v-model="passwd"
|
||||
placeholder="Пароль"/>
|
||||
|
||||
</div>
|
||||
<my-button @click="Login" style="float: right" type="submit">Ввод</my-button>
|
||||
<div @click="info=null" v-if="info">{{ info }}</div>
|
||||
|
||||
<div style="clear:both"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MyButton from "./UI/MyButton.vue";
|
||||
import MyInput from "./UI/MyInput.vue";
|
||||
import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'
|
||||
|
||||
|
||||
export default {
|
||||
name: "Authorization",
|
||||
components: {MyInput, MyButton},
|
||||
data(){
|
||||
return {
|
||||
info:null,
|
||||
email:null,
|
||||
passwd:null,
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
url_api:'',
|
||||
|
||||
}),
|
||||
...mapGetters({
|
||||
|
||||
|
||||
}),
|
||||
|
||||
},
|
||||
methods:{
|
||||
...mapMutations({
|
||||
setAuth:'ApiClass/setAuth',
|
||||
}),
|
||||
...mapActions({
|
||||
loadData:'ApiClass/loadData',
|
||||
|
||||
}),
|
||||
|
||||
|
||||
Login(){
|
||||
|
||||
this.loadData(
|
||||
{
|
||||
method:'post',
|
||||
json:{ email:this.email,
|
||||
password:this.passwd,
|
||||
},
|
||||
url: 'https://tsd.book-online.ru/api/authentication-jwt/login.php', //process.env.VUE_APP_API_HOST+'/authentication-jwt/login.php',
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
).then(
|
||||
(r)=>{
|
||||
//console.log(r);
|
||||
|
||||
if(typeof r.jwt!=='undefined'){
|
||||
this.info='';
|
||||
localStorage.setItem('token', r.jwt);
|
||||
this.setAuth(true);
|
||||
}else{
|
||||
this.info='неправильный пароль или логин';
|
||||
if(typeof r.message!=='undefined'){
|
||||
this.info=r.message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog{
|
||||
|
||||
height: 100%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background: rgba(236, 236, 236, 0.5);
|
||||
position: fixed;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
.dialog__content{
|
||||
|
||||
width: 300px;
|
||||
border:1px solid #d8d8d8;
|
||||
border-radius: 10px ;
|
||||
padding: 20px;
|
||||
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.dialog__content input{
|
||||
border-radius: 5px ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
19
www/first.loc/src/components/PageNotFound.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>404! Страница не найдена!</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PageNotFound"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div{
|
||||
font-size: 20pt;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
color:red;
|
||||
}
|
||||
</style>
|
||||
613
www/first.loc/src/components/TSD/Collect.vue
Normal file
@@ -0,0 +1,613 @@
|
||||
<template>
|
||||
|
||||
<div class="main" v-if="oper_barcode.length">
|
||||
<div>Акт: {{ id }} от {{ oper_current.date_q || '' }}</div>
|
||||
<div class="collect">
|
||||
|
||||
<my-input id="main_search_barcodes" @keyup.enter="Search" @input="Search" v-focus class="collect__search" v-model="search" :placeholder="'Поиск по штрихкоду'"></my-input>
|
||||
|
||||
<div v-if="show_result">
|
||||
{{ text_dialog }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="collect__stat">
|
||||
<div><div class="collect__value collect__value_m1">Собрано:</div> <div class="collect__value collect__value_m">{{ data_collect.length }}</div></div>
|
||||
<div><div @click="show_dialog_barcodes=true" class="collect__value collect__value_m1 collect__value_m2">Не собрано:</div> <div class="collect__value collect__value_m">{{ oper_barcode.length-data_collect.length }}</div></div>
|
||||
<div><div class="collect__value collect__value_m1">Всего в акте:</div> <div class="collect__value collect__value_m">{{ oper_barcode.length }}</div></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<my-button @click="show_dialog_delete=!show_dialog_delete" >Удалить сборку</my-button>
|
||||
<my-dialog :css="{top:true}" v-model:show="show_dialog_delete" >
|
||||
<div class="delete_collect">
|
||||
|
||||
Удалить сборку?
|
||||
<div class="delete_collect__bt">
|
||||
<my-button @click="deleteCollect">Да</my-button>
|
||||
<my-button @click="show_dialog_delete=!show_dialog_delete">Нет</my-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</my-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<my-dialog v-model:show="show_dialog">
|
||||
|
||||
<div class="collect__dialog collect__dialog_m">
|
||||
|
||||
<div>{{ this.text_dialog }}</div>
|
||||
<div>{{ this.text_status }}</div>
|
||||
|
||||
<my-button class="collect__bt" @click="addResult">{{ text_btn }}</my-button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</my-dialog>
|
||||
|
||||
<my-dialog :css="{top:true}" v-model:show="show_dialog_barcodes" >
|
||||
|
||||
<collect-barcodes :barcodes_obj="barcodesNotCollect"></collect-barcodes>
|
||||
|
||||
|
||||
</my-dialog>
|
||||
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
Акт не выбран
|
||||
</div>
|
||||
<div @click="error=false" class="error_downloads" v-if="error">
|
||||
|
||||
<div>
|
||||
<b>Ошибка:</b>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ error_downloads.text }}
|
||||
</div>
|
||||
<div v-html="error_downloads.html">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState,mapActions,mapMutations} from 'vuex'
|
||||
import CollectBarcodes from "./CollectBarcodes.vue";
|
||||
import MyButton from "../UI/MyButton.vue";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
name:'Collect',
|
||||
components: {MyButton, CollectBarcodes},
|
||||
|
||||
data() {
|
||||
return {
|
||||
show_dialog_delete:false,
|
||||
id:localStorage.id || null,
|
||||
search:'',
|
||||
data_collect:[],
|
||||
result_s:null,
|
||||
result_obj:null,
|
||||
show_dialog:false,
|
||||
text_dialog:'',
|
||||
text_btn:'',
|
||||
text_status:'',
|
||||
show_result:false,
|
||||
show_dialog_barcodes:false,
|
||||
error_downloads: {text:null,post_data:null,html:''},
|
||||
error:false,
|
||||
block_button:false,
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
kag: state => state.ApiClass.kag,
|
||||
data: state => state.ApiClass.data,
|
||||
url_api:state => state.ApiClass.url_api,
|
||||
|
||||
}),
|
||||
|
||||
|
||||
oper_current(){
|
||||
if(!this.data){
|
||||
return [];
|
||||
}
|
||||
if(!localStorage.id){
|
||||
return [];
|
||||
}
|
||||
|
||||
//console.log(this.data,localStorage.id);
|
||||
|
||||
|
||||
let arr=this.data.find(item => item.idoper== +localStorage.id);
|
||||
|
||||
|
||||
if(!arr || !arr.data_json || !arr.data_json.barcodes){
|
||||
//console.log("не найдено для сборки из главного массива data",arr);
|
||||
//localStorage.removeItem ('id');
|
||||
//console.log("возвращаю пусто",arr.data_json.barcodes);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
return arr;
|
||||
|
||||
},
|
||||
|
||||
|
||||
oper_barcode(){
|
||||
if(!this.data){
|
||||
return [];
|
||||
}
|
||||
if(!localStorage.id || !this.oper_current.data_json || !this.oper_current.data_json.barcodes){
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.oper_current.data_json.barcodes;
|
||||
},
|
||||
|
||||
barcodesNotCollect(){
|
||||
|
||||
//this.data_collect
|
||||
if(!this.data){
|
||||
return {name:'Не собрано',barcodes:[]};
|
||||
}
|
||||
if(!localStorage.id || !this.oper_current.data_json || !this.oper_current.data_json.barcodes){
|
||||
return [];
|
||||
}
|
||||
|
||||
let barcodes=structuredClone(this.oper_barcode); //из этого массива последовательно удаляем все что собрали, это несобранный остаток
|
||||
//let collect_barcodes = structuredClone(this.data_collect); //найдем нестыковки, когда собрано больше чем надо
|
||||
//console.log(barcodes);
|
||||
this.data_collect.forEach(
|
||||
barcode=>{
|
||||
//console.log(barcode);
|
||||
//найдем индекс и удалим из массива
|
||||
let index=barcodes.findIndex(item=>
|
||||
{
|
||||
if(typeof item === 'object' && JSON.stringify(item).indexOf(barcode)!=-1){
|
||||
return true;
|
||||
}else{
|
||||
if(item==barcode){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
barcodes.splice(index,1);
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
return [{name:'Не собрано',barcodes:barcodes},{name:'Излишек', excess:[]}];
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
methods:{
|
||||
|
||||
|
||||
...mapActions({
|
||||
|
||||
pushCollect: 'ApiClass/pushCollect',
|
||||
getCollect: 'ApiClass/getCollect',
|
||||
postingStatus: 'ApiClass/postingStatus',
|
||||
loadData:'ApiClass/loadData',
|
||||
}),
|
||||
|
||||
|
||||
deleteCollect(){
|
||||
|
||||
|
||||
// console.log({
|
||||
// oper:this.id,
|
||||
// firma:this.kag.firma,
|
||||
// ff:this.oper_current.data_collect.collect,
|
||||
//
|
||||
//
|
||||
// });
|
||||
|
||||
// if(typeof this.oper_current.data_collect!=='undefined' && typeof this.oper_current.data_collect.collect!=='undefined'){
|
||||
// this.data_collect=[];
|
||||
// this.oper_current.data_collect.collect=[];
|
||||
// }
|
||||
this.loadData(
|
||||
{ method:'post',
|
||||
url:this.url_api+'/api/delete_collect.php',
|
||||
json:{
|
||||
oper:this.id,
|
||||
firma:this.kag.firma,
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
).then(
|
||||
(r)=>{
|
||||
console.log(r);
|
||||
|
||||
if(typeof r.result!=='undefined' && parseInt(r.result)===1){
|
||||
this.show_dialog_delete=!this.show_dialog_delete;
|
||||
if(typeof this.oper_current.data_collect!=='undefined' && typeof this.oper_current.data_collect.collect!=='undefined'){
|
||||
this.data_collect=[];
|
||||
this.oper_current.data_collect.collect=[];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
Search(){
|
||||
if(!this.oper_barcode.length){
|
||||
return false;
|
||||
}
|
||||
if(this.search.length < this.kag.barcode_length && this.kag.barcode_length!=0){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.show_result=false;
|
||||
let r=this.oper_barcode.find(item => {
|
||||
// console.log(JSON.stringify(item));
|
||||
if(typeof item === 'object' && JSON.stringify(item).indexOf(this.search)!=-1){
|
||||
this.result_obj=item;//для озона передается сразу много баркодов. После перезапуска приложения или если пробить 2 баркода на одной посылке, то запишутся 2 сборки!!!
|
||||
this.result_s=this.search;
|
||||
return true;
|
||||
}else{
|
||||
if(item==this.search){
|
||||
this.result_obj={barcode:item};//просто записываем один баркод
|
||||
this.result_s=this.search;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
let r2=this.data_collect.find(item => {
|
||||
|
||||
if(typeof item === 'object' && JSON.stringify(item).indexOf(this.search)!=-1){
|
||||
// console.log(item,' 1');
|
||||
this.result_s=this.search;
|
||||
return true;
|
||||
}else{
|
||||
if(item==this.search){
|
||||
// console.log(item,' 2');
|
||||
this.result_s=this.search;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// console.log(r2);
|
||||
if(r && this.search.length && !r2){ //найден в несобранном и отсутсвует в собранном
|
||||
// console.log(r);
|
||||
//сделать запрос к api о текущем статусе отправления
|
||||
//https://marketplace.book-online.ru/api/posting_status/
|
||||
//{"kag": 219807, "posting_number": "49017365-0012-7"}
|
||||
|
||||
|
||||
this.postingStatus({
|
||||
kag:this.kag.kag,
|
||||
posting_number:r.posting_number || r,
|
||||
}).then(
|
||||
(res)=>{
|
||||
|
||||
//console.log(res);
|
||||
//this.result_s=r;
|
||||
//res=null;
|
||||
if(res){
|
||||
this.text_dialog="Найдено "+ this.result_s;
|
||||
this.text_btn="Добавить в сборку";
|
||||
this.text_status=res.status || 'статус не определен';
|
||||
this.show_dialog=true;
|
||||
this.search='';
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
this.error=true;
|
||||
this.error_downloads.text='Ошибка получения данных о статусе отправления!';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
).catch(
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
}
|
||||
if(r2 && this.search.length){ // найден в собранном, повтор
|
||||
this.text_dialog="Повтор "+ this.search;
|
||||
this.result_s=null;
|
||||
this.text_btn="Закрыть";
|
||||
this.show_dialog=true;
|
||||
this.search='';
|
||||
}
|
||||
if(!r2 && !r && this.search.length ){ // ничего не найдено, вывести в блоке под инпутом поиска
|
||||
this.text_dialog="Штрихкод "+ this.search +" не найден";
|
||||
this.result_s=null;
|
||||
this.show_dialog=false;
|
||||
this.show_result=true;
|
||||
this.search='';
|
||||
}
|
||||
|
||||
|
||||
//return r;
|
||||
|
||||
},
|
||||
|
||||
addResult(){
|
||||
|
||||
if(this.block_button){
|
||||
return false;//блокировать двойное нажатие
|
||||
}
|
||||
|
||||
|
||||
this.block_button=true; //блокировать двойное нажатие
|
||||
|
||||
|
||||
if(!this.result_s){
|
||||
this.search='';
|
||||
this.show_dialog=false;
|
||||
document.getElementById('main_search_barcodes').focus();
|
||||
document.getElementById('main_search_barcodes').select();
|
||||
return false;
|
||||
}
|
||||
|
||||
//отправить в сборку --------->
|
||||
|
||||
let options={
|
||||
'idoper':this.id,
|
||||
'barcode':this.result_s,
|
||||
'barcode_json':this.result_obj,
|
||||
'user':0,
|
||||
'date_query':this.oper_current.date_query,
|
||||
'kag':this.kag,
|
||||
};
|
||||
|
||||
this.pushCollect(options).then(
|
||||
(r)=>{
|
||||
console.log(r);
|
||||
if(!r || typeof r.result=='undefined' || r.result==0){
|
||||
|
||||
|
||||
if(r && typeof r.error!=='undefined'){
|
||||
this.error_downloads={text:r.error};
|
||||
}
|
||||
if(r){
|
||||
this.error_downloads.html='<div>API не отвечает! Ошибка получения данных!</div>'+r;
|
||||
}
|
||||
this.error=true;
|
||||
}else{
|
||||
this.data_collect.push(this.result_s);
|
||||
this.result_s=null;
|
||||
this.search='';
|
||||
this.show_dialog=false;
|
||||
this.error_downloads=null;
|
||||
this.error=false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.block_button=false;
|
||||
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
document.getElementById('main_search_barcodes').focus();
|
||||
document.getElementById('main_search_barcodes').select();
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
getCollect(){//обновим сборку, если случилась перезагрузка приложения
|
||||
|
||||
if(!this.oper_current || !this.oper_current.data_collect || !this.oper_current.data_collect.collect || !this.oper_current.data_collect.collect.length){
|
||||
// console.log("не дало зайти в акт");
|
||||
return [];
|
||||
}
|
||||
|
||||
let data=this.oper_current.data_collect.collect;
|
||||
this.data_collect=[];
|
||||
data.forEach(
|
||||
item=>{
|
||||
this.data_collect.push(item.barcode);
|
||||
}
|
||||
|
||||
);
|
||||
//this.data_collect=structuredClone(this.oper_current.data_collect.collect);//клонируем
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
mounted(){
|
||||
|
||||
//console.log("из mounted");
|
||||
this.getCollect();
|
||||
//this.getCollect({id:}).then( ).catch();
|
||||
|
||||
|
||||
},
|
||||
|
||||
watch:{
|
||||
show_dialog(){
|
||||
document.getElementById('main_search_barcodes').focus();
|
||||
document.getElementById('main_search_barcodes').select();
|
||||
|
||||
} ,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.error_downloads_m{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
.main{
|
||||
margin-top:15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:15px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.collect{
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:10px;
|
||||
|
||||
|
||||
}
|
||||
.collect__stat{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:15px;
|
||||
}
|
||||
|
||||
.collect__stat > div {
|
||||
display: grid;
|
||||
grid-template-columns: 200px 1fr ;
|
||||
grid-template-rows: 40px;
|
||||
gap:10px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.collect__value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
|
||||
}
|
||||
.collect__value_m{
|
||||
background: #f6f6f6;
|
||||
min-width: 100px;
|
||||
justify-content: center;
|
||||
}
|
||||
.collect__value_m1{
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.collect__value_m2{
|
||||
text-decoration: underline;
|
||||
color:#2342C0;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
.collect__value_m2:hover{
|
||||
color: #122262;
|
||||
}
|
||||
|
||||
.collect__search{
|
||||
border: 1px solid #9d9d9d;
|
||||
border-radius: 5px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.collect__dialog{
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:20px;
|
||||
}
|
||||
.collect__dialog_m{
|
||||
margin-top:20px ;
|
||||
}
|
||||
|
||||
.collect__bt{
|
||||
height:50px !important;
|
||||
background: #313f77 !important;
|
||||
color:var(--white) !important;
|
||||
font-size: 1.2em !important;
|
||||
}
|
||||
|
||||
.delete_collect{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:20px;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.delete_collect__bt{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap:10px;
|
||||
justify-content: space-between;
|
||||
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
144
www/first.loc/src/components/TSD/CollectBarcodes.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div class="notCollect">
|
||||
|
||||
<div >{{ not_collect.name }} {{ not_collect.barcodes.length }}</div>
|
||||
<div class="notCollect__table">
|
||||
<div class="notCollect_item" v-for="(item, index) in not_collect.barcodes">
|
||||
<div>
|
||||
<b>{{ index+1 }}</b>
|
||||
</div>
|
||||
|
||||
<div class="notCollect__values" v-html="ItemV(item)"></div>
|
||||
|
||||
</div>
|
||||
<div class="excess" v-if="excess_collect.excess.length">
|
||||
{{ excess_collect.name }}:
|
||||
<div class="notCollect_item" v-for="(item, index) in excess_collect.excess">
|
||||
<div>
|
||||
<b>{{ index+1 }}</b>
|
||||
</div>
|
||||
|
||||
<div class="notCollect__values" v-html="ItemV(item)"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name:'CollectBarcodes',
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
props:{
|
||||
barcodes_obj:Object,
|
||||
|
||||
},
|
||||
|
||||
methods:{
|
||||
|
||||
|
||||
ItemV(item){
|
||||
|
||||
if(typeof item === 'object'){
|
||||
let str='';
|
||||
for(const [key, value] of Object.entries(item)) {
|
||||
//console.log(key, value);
|
||||
str+='<div>'+key+':</div><div>'+value+'</div>';
|
||||
}
|
||||
|
||||
return str;
|
||||
}else{
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
computed:{
|
||||
|
||||
not_collect(){
|
||||
|
||||
return this.barcodes_obj[0];
|
||||
|
||||
},
|
||||
excess_collect(){
|
||||
|
||||
return this.barcodes_obj[1];
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.notCollect{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:20px;
|
||||
height:100vh;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
|
||||
.notCollect__table{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:10px;
|
||||
height:80vh;
|
||||
overflow-y: scroll;
|
||||
padding: 2px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #dadada;
|
||||
scrollbar-width:inherit;
|
||||
|
||||
}
|
||||
|
||||
.notCollect__values{
|
||||
display: grid !important;
|
||||
grid-template-columns: 120px 1fr ;
|
||||
gap:5px;
|
||||
}
|
||||
.notCollect__values >div {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.notCollect__table::-webkit-scrollbar {
|
||||
width: 20px;
|
||||
}
|
||||
.notCollect_item{
|
||||
background: #f8f7f7;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #dadada;
|
||||
display: grid;
|
||||
grid-template-columns: 30px 1fr ;
|
||||
|
||||
}
|
||||
|
||||
.notCollect_item > div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
235
www/first.loc/src/components/TSD/History.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div class="history history_m">
|
||||
|
||||
<div>
|
||||
Загруженные Акты
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="data && loads">
|
||||
<div class="history__table">
|
||||
|
||||
<div class="table__row" >
|
||||
<div>Код акта</div>
|
||||
<div>Дата</div>
|
||||
<div >К.п./C</div>
|
||||
<div style="margin-left:auto; "></div>
|
||||
</div>
|
||||
|
||||
<div class="table__row" :class="{Active: item.idoper==this.active_row}" v-for="item in data">
|
||||
<div>{{ item.idoper || 0 }}</div>
|
||||
<div>{{ item.date_q }}</div>
|
||||
<div >{{ infoBarcodes(item) }} / {{ collect(item) }}</div>
|
||||
<div style="margin-left:auto; "><my-button @click="selectAct(item.idoper)" class="btn">выбор</my-button></div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="info_" v-else-if="!loads" >
|
||||
идет загрузка ...
|
||||
|
||||
</div>
|
||||
<div class="info_" v-else >
|
||||
нет загруженных актов
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="data && !data.length && loads">
|
||||
Нет ни одного загруженного акта, войдите в "Загрузка"
|
||||
</div>
|
||||
|
||||
<div class="error_downloads" v-if="error">
|
||||
|
||||
<div>
|
||||
<b>Ошибка:</b>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ error_text }}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState,mapActions,mapMutations} from 'vuex'
|
||||
import UAParser from 'ua-parser-js';
|
||||
export default {
|
||||
name:'History',
|
||||
data() {
|
||||
return {
|
||||
data:null,
|
||||
active_row: localStorage.id || null,
|
||||
loads:false,
|
||||
error:false,
|
||||
error_text:'',
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
kag: state => state.ApiClass.kag,
|
||||
}),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
methods:{
|
||||
|
||||
...mapActions({
|
||||
|
||||
queryHistory: 'ApiClass/queryHistory',
|
||||
|
||||
}),
|
||||
|
||||
|
||||
|
||||
Query(){
|
||||
|
||||
let param={limit:10,kag:this.kag};
|
||||
this.loads=false;
|
||||
this.queryHistory(param).then(
|
||||
(r)=>{
|
||||
// console.log(r);
|
||||
this.data= r.data;
|
||||
this.loads=true;
|
||||
|
||||
|
||||
}
|
||||
).catch(
|
||||
()=>{
|
||||
this.loads=true;
|
||||
this.error=true;
|
||||
this.error_text='ошибка получения данных';
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
selectAct(id){
|
||||
//let parser = new UAParser();
|
||||
//console.log(parser.getResult());
|
||||
|
||||
localStorage.id = id;
|
||||
localStorage.menu = JSON.stringify({id:+id,kag:this.kag.kag});
|
||||
this.active_row = id;
|
||||
},
|
||||
|
||||
|
||||
collect(item){
|
||||
// if(item.)
|
||||
if(!item.data_collect || !item.data_collect.collect || !item.data_collect.collect.length){
|
||||
return 0;
|
||||
}
|
||||
return item.data_collect.collect.length;
|
||||
|
||||
},
|
||||
|
||||
|
||||
infoBarcodes(item){
|
||||
|
||||
if(!item.data_json || !item.data_json.barcodes){
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return item.data_json.barcodes.length;
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
created(){
|
||||
this.Query();
|
||||
|
||||
|
||||
},
|
||||
mounted(){
|
||||
this.loads=false;
|
||||
this.error=false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.info_{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 20px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
color: #112107;
|
||||
}
|
||||
.info_m{
|
||||
|
||||
margin-top: 20px;
|
||||
|
||||
|
||||
}
|
||||
.history{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap:15px;
|
||||
|
||||
}
|
||||
.history_m{
|
||||
margin-top:15px ;
|
||||
|
||||
}
|
||||
.history__table{
|
||||
|
||||
}
|
||||
.Active{
|
||||
border:1px solid #727272;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.table__row{
|
||||
padding: 0px 8px 0px 8px;
|
||||
border-radius: 8px;
|
||||
display: grid;
|
||||
grid-template-columns: 70px 70px 85px 1fr ;
|
||||
grid-template-rows: 40px;
|
||||
gap:10px;
|
||||
|
||||
}
|
||||
|
||||
.table__row > div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.8em;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.btn{
|
||||
border: 1px solid #9d9d9d;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
125
www/first.loc/src/components/TSD/MainMenu.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
|
||||
<div class="main">
|
||||
|
||||
<div> {{ CurrentKag.name || '' }}</div>
|
||||
|
||||
<div class="menu">
|
||||
|
||||
<my-button :class="{Active: menu.component=='History'} " @click="menu.component='History'" >Акты</my-button>
|
||||
<my-button :class="{Active: menu.component=='Collect'} " @click="menu.component='Collect'" >Сборка</my-button>
|
||||
<my-button :class="{Active: menu.component=='downloads'} " @click="menu.component='downloads'" >Загрузка</my-button>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<component v-if="kag" :is="menu.component"></component>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState,mapActions,mapMutations} from 'vuex'
|
||||
import Collect from "./Collect.vue";
|
||||
import downloads from "./downloads.vue";
|
||||
import History from "./History.vue";
|
||||
export default {
|
||||
name: 'MainMenu',
|
||||
components: {Collect,downloads,History},
|
||||
|
||||
data() {
|
||||
return {
|
||||
menu:{
|
||||
component:'History',
|
||||
} ,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
props:{
|
||||
id:String,
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
computed:{
|
||||
|
||||
...mapState({
|
||||
|
||||
data: state => state.ApiClass.data,
|
||||
karArr: state => state.ApiClass.kagArr,
|
||||
kag: state => state.ApiClass.kag,
|
||||
}),
|
||||
|
||||
CurrentKag(){
|
||||
return this.karArr.find(item=>item.firma==this.id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
methods:{
|
||||
|
||||
...mapMutations({
|
||||
kagSet:'ApiClass/kagSet',
|
||||
|
||||
|
||||
}),
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
mounted(){
|
||||
|
||||
this.kagSet(this.CurrentKag);//устанавливаем глобально выбранный kag
|
||||
|
||||
},
|
||||
created(){
|
||||
this.kagSet(null);
|
||||
|
||||
},
|
||||
watch:{
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
}
|
||||
.menu{
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
.menu > button{
|
||||
border: 1px solid #d3d3d3;
|
||||
}
|
||||
|
||||
.Active{
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 4px #cbd6ee;
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
213
www/first.loc/src/components/TSD/downloads.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div class="dl">
|
||||
<div>
|
||||
Дата: <my-input v-model="date_query" class="input_date" :type_input="'date'"></my-input>
|
||||
</div>
|
||||
<div>
|
||||
<my-button @click="Query" >Загрузить</my-button>
|
||||
</div>
|
||||
|
||||
<div class="error_downloads " v-if="error">
|
||||
|
||||
<div>
|
||||
<b>Ошибка:</b>
|
||||
</div>
|
||||
|
||||
<div v-html="error_downloads.text">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
{{ error_downloads.post_data }}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
<result-downloads v-if="data_oper" :data="data_oper" :date_query="date_query"></result-downloads>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState,mapActions,mapMutations} from 'vuex'
|
||||
import ResultDownloads from "./resultDownloads.vue";
|
||||
export default {
|
||||
name:'downloads',
|
||||
components: {ResultDownloads},
|
||||
data() {
|
||||
return {
|
||||
date_query:null,
|
||||
data_oper:null,
|
||||
error_downloads: {text:null,post_data:null},
|
||||
error:false,
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
...mapState({
|
||||
kag: state => state.ApiClass.kag,
|
||||
data: state => state.ApiClass.data,
|
||||
}),
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
...mapActions({
|
||||
|
||||
queryApi: 'ApiClass/queryApi',
|
||||
|
||||
}),
|
||||
...mapMutations({
|
||||
dataSet:'ApiClass/dataSet',
|
||||
|
||||
|
||||
}),
|
||||
Query(){
|
||||
//this.kag.firma=this.kag.firma.toString();
|
||||
//this.kag.kag=this.kag.kag.toString();
|
||||
let param={kag:this.kag, method:'/api/shipment_barcodes/',date_query:this.date_query};
|
||||
//console.log(param);
|
||||
this.queryApi(param).then(
|
||||
(r)=>{
|
||||
console.log(r);
|
||||
if(typeof r.error!=='undefined' ){
|
||||
this.error_downloads={text:r.error, post_data: ''};
|
||||
this.error=true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// if(!r || typeof r.data==='undefined' ){
|
||||
// this.error_downloads={text:'Ошибка данных с сервера api: cервер вернул пустой ответ', post_data: ''};
|
||||
// this.error=true;
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
|
||||
if(r.result==0){
|
||||
|
||||
|
||||
this.error_downloads={text:r.server_error, post_data: r.post_data};
|
||||
this.error=true;
|
||||
|
||||
}else{
|
||||
this.error_downloads= null;
|
||||
this.error=false;
|
||||
|
||||
|
||||
this.data_oper= r.data_json; //передается массив актов.
|
||||
//добавим сразу, если ничего нет
|
||||
if(!this.data || !this.data.length){
|
||||
localStorage.removeItem('id');
|
||||
this.dataSet(r.data_json);
|
||||
}else{
|
||||
|
||||
|
||||
|
||||
r.data_json.forEach(
|
||||
item=>{
|
||||
//делаем замену в загруженных актах, если найдем
|
||||
let findId=this.data.findIndex(item_=>
|
||||
+item_.idoper==+item.idoper
|
||||
);
|
||||
if(findId){
|
||||
this.data.splice(findId,1);
|
||||
this.data.unshift(item); //добавляем в начало
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
) ;
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
date_(){ //первоначальная дата для выбора
|
||||
let now = new Date();
|
||||
let date = new Date(now.getFullYear(),now.getMonth(),now.getDate()+1);
|
||||
let m=''+(date.getMonth()+1);
|
||||
if(m.length==1){
|
||||
m='0'+m;
|
||||
}
|
||||
let d=''+(date.getDate());
|
||||
if(d.length==1){
|
||||
d='0'+d;
|
||||
}
|
||||
return date.getFullYear()+'-'+m+'-'+d;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
mounted(){
|
||||
this.date_query=this.date_();
|
||||
},
|
||||
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dl{
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
}
|
||||
.error_downloads{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 20px;
|
||||
background: #d0b2b2;
|
||||
border-radius: 8px;
|
||||
color: #501717;
|
||||
}
|
||||
.error_downloads_m{
|
||||
|
||||
margin-top: 20px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
.input_date{
|
||||
border: 1px solid #9d9d9d;
|
||||
border-radius: 5px;
|
||||
max-width: 150px;
|
||||
}
|
||||
.bt{
|
||||
border: 1px solid #d3d3d3 !important;
|
||||
max-width: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
116
www/first.loc/src/components/TSD/resultDownloads.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
|
||||
<div>
|
||||
Загруженные акты {{ date_query }} :
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="data">
|
||||
|
||||
<div class="result_downloads">
|
||||
<div>
|
||||
Код акта
|
||||
</div>
|
||||
<div>
|
||||
Кол.п
|
||||
</div>
|
||||
<div>
|
||||
Выбор
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="result_downloads" v-for="item in data" >
|
||||
<div>
|
||||
{{ item.idoper || 0 }}
|
||||
</div>
|
||||
<div>
|
||||
{{ infoBarcodes(item) }}
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<my-button @click="selectAct(item.idoper)" class="btn">В сборку</my-button>
|
||||
|
||||
</div>
|
||||
<div>{{ result }}</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
export default {
|
||||
name:"resultDownloads",
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
result:'',
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
props:{
|
||||
data:[Object,Array],
|
||||
date_query:String,
|
||||
|
||||
},
|
||||
|
||||
methods:{
|
||||
selectAct(id){
|
||||
localStorage.id = id;
|
||||
console.log("выбор id "+localStorage.id);
|
||||
this.result='акт загружен, перейдите в сборку';
|
||||
},
|
||||
|
||||
infoBarcodes(item){
|
||||
|
||||
if(!item.data_json || !item.data_json.barcodes.length){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return item.data_json.barcodes.length;
|
||||
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.result_downloads{
|
||||
display: grid;
|
||||
grid-template-columns: 120px 60px 1fr ;
|
||||
gap:10px;
|
||||
|
||||
}
|
||||
|
||||
.btn{
|
||||
border: 1px solid #9d9d9d;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||