Updated EEPROM to latest Index.
This commit is contained in:
parent
f1513d1c0f
commit
1d4e7f0918
27 changed files with 784 additions and 464 deletions
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"flashwave/index": "^0.2410",
|
"flashwave/index": "^0.2503",
|
||||||
"flashii/rpcii": "^3.0",
|
"flashii/rpcii": "^5.0",
|
||||||
"flashii/apii": "^0.3",
|
"flashii/apii": "^0.4",
|
||||||
"sentry/sdk": "^4.0",
|
"sentry/sdk": "^4.0",
|
||||||
"nesbot/carbon": "^3.7"
|
"nesbot/carbon": "^3.8"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^2.0"
|
"phpstan/phpstan": "^2.1"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
535
composer.lock
generated
535
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "7009c2a98d8882c938653f4455f52a82",
|
"content-hash": "2d07dcd636742c34625c576e67eaaf0b",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "carbonphp/carbon-doctrine-types",
|
"name": "carbonphp/carbon-doctrine-types",
|
||||||
|
@ -77,18 +77,20 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flashii/apii",
|
"name": "flashii/apii",
|
||||||
"version": "v0.3.0",
|
"version": "v0.4.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://patchii.net/flashii/apii-php.git",
|
"url": "https://patchii.net/flashii/apii-php.git",
|
||||||
"reference": "2d6c135faddd359341762afcb9c429e279d87059"
|
"reference": "d330e0792515dbae2832bd8e2ac761cc685175e9"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1"
|
"guzzlehttp/guzzle": "~7.9",
|
||||||
|
"php": ">=8.1",
|
||||||
|
"psr/http-client": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.12",
|
"phpstan/phpstan": "~2.1",
|
||||||
"phpunit/phpunit": "^10.5"
|
"phpunit/phpunit": "~10.5"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -110,24 +112,26 @@
|
||||||
],
|
],
|
||||||
"description": "Client library for the Flashii.net API.",
|
"description": "Client library for the Flashii.net API.",
|
||||||
"homepage": "https://api.flashii.net",
|
"homepage": "https://api.flashii.net",
|
||||||
"time": "2024-11-22T21:36:01+00:00"
|
"time": "2025-03-20T17:05:52+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flashii/rpcii",
|
"name": "flashii/rpcii",
|
||||||
"version": "v3.0.0",
|
"version": "v5.0.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://patchii.net/flashii/rpcii-php.git",
|
"url": "https://patchii.net/flashii/rpcii-php.git",
|
||||||
"reference": "25ac46d5dee60027032e175107e638dfb0b8f7f9"
|
"reference": "28c25e0a342173524f8894d03158663842e03252"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-msgpack": ">=2.2",
|
"ext-msgpack": ">=2.2",
|
||||||
"flashwave/index": "^0.2410",
|
"flashwave/index": "^0.2503",
|
||||||
"php": ">=8.4"
|
"guzzlehttp/guzzle": "~7.0",
|
||||||
|
"php": ">=8.4",
|
||||||
|
"psr/http-client": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpstan/phpstan": "^2.1",
|
||||||
"phpunit/phpunit": "^11.5"
|
"phpunit/phpunit": "^12.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -149,25 +153,27 @@
|
||||||
],
|
],
|
||||||
"description": "HTTP RPC client/server library.",
|
"description": "HTTP RPC client/server library.",
|
||||||
"homepage": "https://railgun.sh/rpcii",
|
"homepage": "https://railgun.sh/rpcii",
|
||||||
"time": "2025-01-17T00:05:22+00:00"
|
"time": "2025-03-21T19:38:29+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flashwave/index",
|
"name": "flashwave/index",
|
||||||
"version": "v0.2410.830205",
|
"version": "v0.2503.221959",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://patchii.net/flash/index.git",
|
"url": "https://patchii.net/flash/index.git",
|
||||||
"reference": "416c716b2efab9619d14be02a20cd6540e18a83f"
|
"reference": "d99562db163589cc51e32e10e851085230fb3181"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"php": ">=8.3",
|
"php": ">=8.4",
|
||||||
"twig/html-extra": "^3.13",
|
"psr/http-message": "^2.0",
|
||||||
"twig/twig": "^3.14"
|
"psr/http-server-handler": "^1.0",
|
||||||
|
"twig/html-extra": "^3.20",
|
||||||
|
"twig/twig": "^3.20"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^2.0",
|
"phpstan/phpstan": "^2.1",
|
||||||
"phpunit/phpunit": "^11.4"
|
"phpunit/phpunit": "^12.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-memcache": "Support for the Index\\Cache\\Memcached namespace (only if you can't use ext-memcached for some reason).",
|
"ext-memcache": "Support for the Index\\Cache\\Memcached namespace (only if you can't use ext-memcached for some reason).",
|
||||||
|
@ -204,7 +210,216 @@
|
||||||
],
|
],
|
||||||
"description": "Composer package for the common library for my projects.",
|
"description": "Composer package for the common library for my projects.",
|
||||||
"homepage": "https://railgun.sh/index",
|
"homepage": "https://railgun.sh/index",
|
||||||
"time": "2024-12-22T02:05:46+00:00"
|
"time": "2025-03-22T19:59:44+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/guzzle",
|
||||||
|
"version": "7.9.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
|
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
|
||||||
|
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"guzzlehttp/promises": "^1.5.3 || ^2.0.3",
|
||||||
|
"guzzlehttp/psr7": "^2.7.0",
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/http-client-implementation": "1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"guzzle/client-integration-tests": "3.0.2",
|
||||||
|
"php-http/message-factory": "^1.1",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20",
|
||||||
|
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-curl": "Required for CURL handler support",
|
||||||
|
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
|
||||||
|
"psr/log": "Required for using the Log middleware"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jeremy Lindblom",
|
||||||
|
"email": "jeremeamia@gmail.com",
|
||||||
|
"homepage": "https://github.com/jeremeamia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "George Mponos",
|
||||||
|
"email": "gmponos@gmail.com",
|
||||||
|
"homepage": "https://github.com/gmponos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com",
|
||||||
|
"homepage": "https://github.com/Nyholm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Márk Sági-Kazár",
|
||||||
|
"email": "mark.sagikazar@gmail.com",
|
||||||
|
"homepage": "https://github.com/sagikazarmark"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"email": "webmaster@tubo-world.de",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Guzzle is a PHP HTTP client library",
|
||||||
|
"keywords": [
|
||||||
|
"client",
|
||||||
|
"curl",
|
||||||
|
"framework",
|
||||||
|
"http",
|
||||||
|
"http client",
|
||||||
|
"psr-18",
|
||||||
|
"psr-7",
|
||||||
|
"rest",
|
||||||
|
"web service"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||||
|
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Nyholm",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-07-24T11:22:20+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/promises",
|
||||||
|
"version": "2.0.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/promises.git",
|
||||||
|
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||||
|
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\Promise\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com",
|
||||||
|
"homepage": "https://github.com/Nyholm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"email": "webmaster@tubo-world.de",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Guzzle promises library",
|
||||||
|
"keywords": [
|
||||||
|
"promise"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/guzzle/promises/issues",
|
||||||
|
"source": "https://github.com/guzzle/promises/tree/2.0.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Nyholm",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-10-17T10:06:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/psr7",
|
"name": "guzzlehttp/psr7",
|
||||||
|
@ -324,16 +539,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jean85/pretty-package-versions",
|
"name": "jean85/pretty-package-versions",
|
||||||
"version": "2.1.0",
|
"version": "2.1.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||||
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10"
|
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
|
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||||
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
|
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -343,8 +558,9 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"friendsofphp/php-cs-fixer": "^3.2",
|
"friendsofphp/php-cs-fixer": "^3.2",
|
||||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||||
"phpstan/phpstan": "^1.4",
|
"phpstan/phpstan": "^2.0",
|
||||||
"phpunit/phpunit": "^7.5|^8.5|^9.6",
|
"phpunit/phpunit": "^7.5|^8.5|^9.6",
|
||||||
|
"rector/rector": "^2.0",
|
||||||
"vimeo/psalm": "^4.3 || ^5.0"
|
"vimeo/psalm": "^4.3 || ^5.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
|
@ -377,22 +593,22 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0"
|
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
|
||||||
},
|
},
|
||||||
"time": "2024-11-18T16:19:46+00:00"
|
"time": "2025-03-19T14:43:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nesbot/carbon",
|
"name": "nesbot/carbon",
|
||||||
"version": "3.8.4",
|
"version": "3.8.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/CarbonPHP/carbon.git",
|
"url": "https://github.com/CarbonPHP/carbon.git",
|
||||||
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58"
|
"reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58",
|
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd",
|
||||||
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58",
|
"reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -468,8 +684,8 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"docs": "https://carbon.nesbot.com/docs",
|
"docs": "https://carbon.nesbot.com/docs",
|
||||||
"issues": "https://github.com/briannesbitt/Carbon/issues",
|
"issues": "https://github.com/CarbonPHP/carbon/issues",
|
||||||
"source": "https://github.com/briannesbitt/Carbon"
|
"source": "https://github.com/CarbonPHP/carbon"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -485,7 +701,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-27T09:25:35+00:00"
|
"time": "2025-02-20T17:33:38+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/clock",
|
"name": "psr/clock",
|
||||||
|
@ -535,6 +751,58 @@
|
||||||
},
|
},
|
||||||
"time": "2022-11-25T14:36:26+00:00"
|
"time": "2022-11-25T14:36:26+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-client",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-client.git",
|
||||||
|
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||||
|
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.0 || ^8.0",
|
||||||
|
"psr/http-message": "^1.0 || ^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Client\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for HTTP clients",
|
||||||
|
"homepage": "https://github.com/php-fig/http-client",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"http-client",
|
||||||
|
"psr",
|
||||||
|
"psr-18"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-client"
|
||||||
|
},
|
||||||
|
"time": "2023-09-23T14:17:50+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/http-factory",
|
"name": "psr/http-factory",
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
|
@ -643,6 +911,62 @@
|
||||||
},
|
},
|
||||||
"time": "2023-04-04T09:54:51+00:00"
|
"time": "2023-04-04T09:54:51+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-server-handler",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-server-handler.git",
|
||||||
|
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
|
||||||
|
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.0",
|
||||||
|
"psr/http-message": "^1.0 || ^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Server\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for HTTP server-side request handler",
|
||||||
|
"keywords": [
|
||||||
|
"handler",
|
||||||
|
"http",
|
||||||
|
"http-interop",
|
||||||
|
"psr",
|
||||||
|
"psr-15",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
|
||||||
|
},
|
||||||
|
"time": "2023-04-10T20:06:20+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/log",
|
"name": "psr/log",
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
|
@ -1024,16 +1348,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/mime",
|
"name": "symfony/mime",
|
||||||
"version": "v7.2.1",
|
"version": "v7.2.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/mime.git",
|
"url": "https://github.com/symfony/mime.git",
|
||||||
"reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283"
|
"reference": "87ca22046b78c3feaff04b337f33b38510fd686b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283",
|
"url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b",
|
||||||
"reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283",
|
"reference": "87ca22046b78c3feaff04b337f33b38510fd686b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1088,7 +1412,7 @@
|
||||||
"mime-type"
|
"mime-type"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/mime/tree/v7.2.1"
|
"source": "https://github.com/symfony/mime/tree/v7.2.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1104,7 +1428,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-07T08:50:44+00:00"
|
"time": "2025-02-19T08:51:20+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/options-resolver",
|
"name": "symfony/options-resolver",
|
||||||
|
@ -1496,82 +1820,6 @@
|
||||||
],
|
],
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "symfony/polyfill-php81",
|
|
||||||
"version": "v1.31.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
|
||||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
|
||||||
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.2"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"thanks": {
|
|
||||||
"url": "https://github.com/symfony/polyfill",
|
|
||||||
"name": "symfony/polyfill"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"bootstrap.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"Symfony\\Polyfill\\Php81\\": ""
|
|
||||||
},
|
|
||||||
"classmap": [
|
|
||||||
"Resources/stubs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Nicolas Grekas",
|
|
||||||
"email": "p@tchwork.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Symfony Community",
|
|
||||||
"homepage": "https://symfony.com/contributors"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
|
|
||||||
"homepage": "https://symfony.com",
|
|
||||||
"keywords": [
|
|
||||||
"compatibility",
|
|
||||||
"polyfill",
|
|
||||||
"portable",
|
|
||||||
"shim"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://symfony.com/sponsor",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/fabpot",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php83",
|
"name": "symfony/polyfill-php83",
|
||||||
"version": "v1.31.0",
|
"version": "v1.31.0",
|
||||||
|
@ -1650,16 +1898,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation",
|
"name": "symfony/translation",
|
||||||
"version": "v7.2.2",
|
"version": "v7.2.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/translation.git",
|
"url": "https://github.com/symfony/translation.git",
|
||||||
"reference": "e2674a30132b7cc4d74540d6c2573aa363f05923"
|
"reference": "283856e6981286cc0d800b53bd5703e8e363f05a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923",
|
"url": "https://api.github.com/repos/symfony/translation/zipball/283856e6981286cc0d800b53bd5703e8e363f05a",
|
||||||
"reference": "e2674a30132b7cc4d74540d6c2573aa363f05923",
|
"reference": "283856e6981286cc0d800b53bd5703e8e363f05a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1725,7 +1973,7 @@
|
||||||
"description": "Provides tools to internationalize your application",
|
"description": "Provides tools to internationalize your application",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/translation/tree/v7.2.2"
|
"source": "https://github.com/symfony/translation/tree/v7.2.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1741,7 +1989,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-07T08:18:10+00:00"
|
"time": "2025-02-13T10:27:23+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation-contracts",
|
"name": "symfony/translation-contracts",
|
||||||
|
@ -1823,20 +2071,20 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/html-extra",
|
"name": "twig/html-extra",
|
||||||
"version": "v3.18.0",
|
"version": "v3.20.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/html-extra.git",
|
"url": "https://github.com/twigphp/html-extra.git",
|
||||||
"reference": "c63b28e192c1b7c15bb60f81d2e48b140846239a"
|
"reference": "f7d54d4de1b64182af745cfb66777f699b599734"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/c63b28e192c1b7c15bb60f81d2e48b140846239a",
|
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/f7d54d4de1b64182af745cfb66777f699b599734",
|
||||||
"reference": "c63b28e192c1b7c15bb60f81d2e48b140846239a",
|
"reference": "f7d54d4de1b64182af745cfb66777f699b599734",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0.2",
|
"php": ">=8.1.0",
|
||||||
"symfony/deprecation-contracts": "^2.5|^3",
|
"symfony/deprecation-contracts": "^2.5|^3",
|
||||||
"symfony/mime": "^5.4|^6.4|^7.0",
|
"symfony/mime": "^5.4|^6.4|^7.0",
|
||||||
"twig/twig": "^3.13|^4.0"
|
"twig/twig": "^3.13|^4.0"
|
||||||
|
@ -1875,7 +2123,7 @@
|
||||||
"twig"
|
"twig"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/twigphp/html-extra/tree/v3.18.0"
|
"source": "https://github.com/twigphp/html-extra/tree/v3.20.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1887,28 +2135,27 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-29T10:29:59+00:00"
|
"time": "2025-01-31T20:45:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
"version": "v3.18.0",
|
"version": "v3.20.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/twigphp/Twig.git",
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50"
|
"reference": "3468920399451a384bef53cf7996965f7cd40183"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183",
|
||||||
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
"reference": "3468920399451a384bef53cf7996965f7cd40183",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0.2",
|
"php": ">=8.1.0",
|
||||||
"symfony/deprecation-contracts": "^2.5|^3",
|
"symfony/deprecation-contracts": "^2.5|^3",
|
||||||
"symfony/polyfill-ctype": "^1.8",
|
"symfony/polyfill-ctype": "^1.8",
|
||||||
"symfony/polyfill-mbstring": "^1.3",
|
"symfony/polyfill-mbstring": "^1.3"
|
||||||
"symfony/polyfill-php81": "^1.29"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^2.0",
|
"phpstan/phpstan": "^2.0",
|
||||||
|
@ -1955,7 +2202,7 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/twigphp/Twig/issues",
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
"source": "https://github.com/twigphp/Twig/tree/v3.18.0"
|
"source": "https://github.com/twigphp/Twig/tree/v3.20.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1967,22 +2214,22 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-12-29T10:51:50+00:00"
|
"time": "2025-02-13T08:34:43+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "2.1.1",
|
"version": "2.1.8",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7"
|
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||||
"reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
|
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2027,7 +2274,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-01-05T16:43:48+00:00"
|
"time": "2025-03-09T09:30:48+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
|
|
@ -28,7 +28,8 @@ class AuthContext {
|
||||||
|
|
||||||
public function createClient(Credentials $credentials): FlashiiClient {
|
public function createClient(Credentials $credentials): FlashiiClient {
|
||||||
return new FlashiiClient('EEPROM', $credentials, new FlashiiUrls(
|
return new FlashiiClient('EEPROM', $credentials, new FlashiiUrls(
|
||||||
$this->config->getString('api', FlashiiUrls::PROD_API_URL)
|
$this->config->getString('url', FlashiiUrls::PROD_URL),
|
||||||
|
$this->config->getString('api', FlashiiUrls::PROD_API_URL),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
namespace EEPROM\Auth;
|
namespace EEPROM\Auth;
|
||||||
|
|
||||||
use Index\Http\Routing\{HttpMiddleware,RouteHandler,RouteHandlerTrait};
|
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||||
|
use Index\Http\Routing\Processors\Preprocessor;
|
||||||
|
use Index\Http\Routing\{HttpMiddleware,RouteHandler,RouteHandlerCommon};
|
||||||
|
|
||||||
class AuthRoutes implements RouteHandler {
|
class AuthRoutes implements RouteHandler {
|
||||||
use RouteHandlerTrait;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AuthContext $authCtx
|
private AuthContext $authCtx
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[HttpMiddleware('/')]
|
#[Preprocessor('eeprom:attempt-auth')]
|
||||||
public function getIndex($response, $request) {
|
public function getIndex(HttpResponseBuilder $response, HttpRequest $request) {
|
||||||
$auth = $request->getHeaderLine('Authorization');
|
$auth = $request->getHeaderLine('Authorization');
|
||||||
if(empty($auth)) {
|
if(empty($auth)) {
|
||||||
$cookie = (string)$request->getCookie('msz_auth');
|
$cookie = (string)$request->getCookie('msz_auth');
|
||||||
|
@ -21,7 +23,7 @@ class AuthRoutes implements RouteHandler {
|
||||||
|
|
||||||
if(!empty($auth)) {
|
if(!empty($auth)) {
|
||||||
$parts = explode(' ', $auth, 2);
|
$parts = explode(' ', $auth, 2);
|
||||||
$method = strval($parts[0] ?? '');
|
$method = $parts[0];
|
||||||
$token = strval($parts[1] ?? '');
|
$token = strval($parts[1] ?? '');
|
||||||
|
|
||||||
$flashii = null;
|
$flashii = null;
|
||||||
|
|
|
@ -3,22 +3,23 @@ namespace EEPROM;
|
||||||
|
|
||||||
use Index\Db\DbConnection;
|
use Index\Db\DbConnection;
|
||||||
use Index\Db\Migration\{DbMigrationManager,DbMigrationRepo,FsDbMigrationRepo};
|
use Index\Db\Migration\{DbMigrationManager,DbMigrationRepo,FsDbMigrationRepo};
|
||||||
use Index\Http\Routing\{HttpMiddleware,RouteHandler,RouteHandlerTrait};
|
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||||
|
use Index\Http\Routing\Filters\PrefixFilter;
|
||||||
|
|
||||||
class DatabaseContext implements RouteHandler {
|
class DatabaseContext implements RouteHandler {
|
||||||
use RouteHandlerTrait;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public private(set) DbConnection $connection
|
public private(set) DbConnection $conn
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getQueryCount(): int {
|
public function getQueryCount(): int {
|
||||||
$result = $this->connection->query('SHOW SESSION STATUS LIKE "Questions"');
|
$result = $this->conn->query('SHOW SESSION STATUS LIKE "Questions"');
|
||||||
return $result->next() ? $result->getInteger(1) : 0;
|
return $result->next() ? $result->getInteger(1) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createMigrationManager(): DbMigrationManager {
|
public function createMigrationManager(): DbMigrationManager {
|
||||||
return new DbMigrationManager($this->connection, 'prm_' . DbMigrationManager::DEFAULT_TABLE);
|
return new DbMigrationManager($this->conn, 'prm_' . DbMigrationManager::DEFAULT_TABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createMigrationRepo(): DbMigrationRepo {
|
public function createMigrationRepo(): DbMigrationRepo {
|
||||||
|
@ -29,7 +30,7 @@ class DatabaseContext implements RouteHandler {
|
||||||
return sys_get_temp_dir() . '/eeprom-migrate-' . hash('sha256', PRM_ROOT) . '.lock';
|
return sys_get_temp_dir() . '/eeprom-migrate-' . hash('sha256', PRM_ROOT) . '.lock';
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpMiddleware('/')]
|
#[PrefixFilter('/')]
|
||||||
public function middleware() {
|
public function middleware() {
|
||||||
if(is_file($this->getMigrateLockPath()))
|
if(is_file($this->getMigrateLockPath()))
|
||||||
return 503;
|
return 503;
|
||||||
|
|
30
src/EEPROMAccessControlHandler.php
Normal file
30
src/EEPROMAccessControlHandler.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
namespace EEPROM;
|
||||||
|
|
||||||
|
use Index\Http\HttpUri;
|
||||||
|
use Index\Http\Routing\HandlerContext;
|
||||||
|
use Index\Http\Routing\Routes\RouteInfo;
|
||||||
|
use Index\Http\Routing\AccessControl\{AccessControl,SimpleAccessControlHandler};
|
||||||
|
|
||||||
|
class EEPROMAccessControlHandler extends SimpleAccessControlHandler {
|
||||||
|
public function __construct(
|
||||||
|
private array $origins,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
|
public function checkAccess(
|
||||||
|
HandlerContext $context,
|
||||||
|
AccessControl $accessControl,
|
||||||
|
HttpUri $origin,
|
||||||
|
?RouteInfo $routeInfo = null,
|
||||||
|
): string|bool {
|
||||||
|
if($accessControl->credentials) {
|
||||||
|
$host = '.' . $origin->host;
|
||||||
|
foreach($this->origins as $allowOrigin)
|
||||||
|
if(str_ends_with($host, '.' . $allowOrigin))
|
||||||
|
return (string)$origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
namespace EEPROM;
|
namespace EEPROM;
|
||||||
|
|
||||||
use EEPROM\Auth\AuthInfoWrapper;
|
use Index\Dependencies;
|
||||||
use Index\Config\Config;
|
use Index\Config\Config;
|
||||||
use Index\Db\DbConnection;
|
use Index\Db\DbConnection;
|
||||||
use RPCii\HmacVerificationProvider;
|
use RPCii\HmacVerificationProvider;
|
||||||
use RPCii\Server\HttpRpcServer;
|
use RPCii\Server\HttpRpcServer;
|
||||||
|
|
||||||
class EEPROMContext {
|
class EEPROMContext {
|
||||||
public private(set) DatabaseContext $database;
|
public private(set) Dependencies $deps;
|
||||||
|
|
||||||
|
public private(set) DatabaseContext $dbCtx;
|
||||||
public private(set) SnowflakeGenerator $snowflake;
|
public private(set) SnowflakeGenerator $snowflake;
|
||||||
|
|
||||||
public private(set) Auth\AuthContext $authCtx;
|
public private(set) Auth\AuthContext $authCtx;
|
||||||
|
@ -22,30 +24,26 @@ class EEPROMContext {
|
||||||
private Config $config,
|
private Config $config,
|
||||||
DbConnection $dbConn
|
DbConnection $dbConn
|
||||||
) {
|
) {
|
||||||
$this->database = new DatabaseContext($dbConn);
|
$this->deps = new Dependencies;
|
||||||
$this->snowflake = new SnowflakeGenerator;
|
$this->deps->register($this);
|
||||||
|
$this->deps->register($this->deps);
|
||||||
|
|
||||||
$this->authCtx = new Auth\AuthContext($config->scopeTo('apii'));
|
$this->deps->register($this->dbCtx = new DatabaseContext($dbConn));
|
||||||
$this->denylistCtx = new Denylist\DenylistContext($dbConn);
|
$this->deps->register($this->dbCtx->conn);
|
||||||
$this->poolsCtx = new Pools\PoolsContext($dbConn);
|
|
||||||
$this->storageCtx = new Storage\StorageContext($config->scopeTo('storage'), $dbConn, $this->snowflake);
|
$this->deps->register($this->snowflake = $this->deps->constructLazy(SnowflakeGenerator::class));
|
||||||
$this->tasksCtx = new Tasks\TasksContext($config->scopeTo('domain'), $dbConn, $this->snowflake);
|
|
||||||
$this->uploadsCtx = new Uploads\UploadsContext(
|
$this->deps->register($this->authCtx = $this->deps->constructLazy(Auth\AuthContext::class, $config->scopeTo('apii')));
|
||||||
$config->scopeTo('domain'), $dbConn, $this->snowflake, $this->poolsCtx, $this->storageCtx,
|
$this->deps->register($this->denylistCtx = $this->deps->constructLazy(Denylist\DenylistContext::class));
|
||||||
);
|
$this->deps->register($this->poolsCtx = $this->deps->constructLazy(Pools\PoolsContext::class));
|
||||||
|
$this->deps->register($this->storageCtx = $this->deps->constructLazy(Storage\StorageContext::class, $config->scopeTo('storage')));
|
||||||
|
$this->deps->register($this->tasksCtx = $this->deps->constructLazy(Tasks\TasksContext::class, $config->scopeTo('domain')));
|
||||||
|
$this->deps->register($this->uploadsCtx = $this->deps->constructLazy(Uploads\UploadsContext::class, $config->scopeTo('domain')));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createRouting(bool $isApiDomain): RoutingContext {
|
public function createRouting(bool $isApiDomain): RoutingContext {
|
||||||
$routingCtx = new RoutingContext($this->config->scopeTo('cors'));
|
$routingCtx = new RoutingContext($this->config->scopeTo('cors'));
|
||||||
$routingCtx->register($this->database);
|
$routingCtx->register($this->dbCtx);
|
||||||
|
|
||||||
$routingCtx->register($uploadsViewsRoutes = new Uploads\UploadsViewRoutes(
|
|
||||||
$this->poolsCtx,
|
|
||||||
$this->uploadsCtx,
|
|
||||||
$this->storageCtx,
|
|
||||||
$this->denylistCtx,
|
|
||||||
$isApiDomain,
|
|
||||||
));
|
|
||||||
|
|
||||||
if($isApiDomain) {
|
if($isApiDomain) {
|
||||||
$rpcServer = new HttpRpcServer;
|
$rpcServer = new HttpRpcServer;
|
||||||
|
@ -53,33 +51,16 @@ class EEPROMContext {
|
||||||
new HmacVerificationProvider(fn() => $this->config->getString('rpcii:secret'))
|
new HmacVerificationProvider(fn() => $this->config->getString('rpcii:secret'))
|
||||||
));
|
));
|
||||||
|
|
||||||
$rpcServer->register(new Pools\PoolsRpcHandler(
|
$rpcServer->register($this->deps->constructLazy(Pools\PoolsRpcHandler::class));
|
||||||
$this->poolsCtx,
|
$rpcServer->register($this->deps->constructLazy(Tasks\TasksRpcHandler::class));
|
||||||
));
|
$rpcServer->register($this->deps->constructLazy(Uploads\UploadsRpcHandler::class));
|
||||||
$rpcServer->register(new Tasks\TasksRpcHandler(
|
|
||||||
$this->tasksCtx,
|
|
||||||
$this->poolsCtx,
|
|
||||||
$this->uploadsCtx,
|
|
||||||
$this->storageCtx,
|
|
||||||
$this->denylistCtx,
|
|
||||||
));
|
|
||||||
$rpcServer->register(new Uploads\UploadsRpcHandler(
|
|
||||||
$this->poolsCtx,
|
|
||||||
$this->uploadsCtx,
|
|
||||||
$this->storageCtx,
|
|
||||||
));
|
|
||||||
|
|
||||||
$routingCtx->register(new Auth\AuthRoutes($this->authCtx));
|
$routingCtx->register($this->deps->constructLazy(Auth\AuthRoutes::class));
|
||||||
$routingCtx->register(new Tasks\TasksRoutes($this->tasksCtx));
|
$routingCtx->register($this->deps->constructLazy(Tasks\TasksRoutes::class));
|
||||||
$routingCtx->register(new Uploads\UploadsLegacyRoutes(
|
$routingCtx->register($this->deps->constructLazy(Uploads\UploadsLegacyRoutes::class));
|
||||||
$this->authCtx,
|
$routingCtx->register($this->deps->constructLazy(LandingRoutes::class));
|
||||||
$this->poolsCtx,
|
} else
|
||||||
$this->uploadsCtx,
|
$routingCtx->register($this->deps->constructLazy(Uploads\UploadsViewRoutes::class));
|
||||||
$this->storageCtx,
|
|
||||||
$this->denylistCtx,
|
|
||||||
));
|
|
||||||
$routingCtx->register(new LandingRoutes($this->database));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $routingCtx;
|
return $routingCtx;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,23 @@
|
||||||
<?php
|
<?php
|
||||||
namespace EEPROM;
|
namespace EEPROM;
|
||||||
|
|
||||||
use Index\Http\{HttpErrorHandler,HttpResponseBuilder,HttpRequest};
|
use Index\Http\HttpResponse;
|
||||||
|
use Index\Http\Routing\HandlerContext;
|
||||||
|
use Index\Http\Routing\ErrorHandling\ErrorHandler;
|
||||||
|
use Index\Http\Streams\Stream;
|
||||||
|
|
||||||
class EEPROMErrorHandler implements HttpErrorHandler {
|
class EEPROMErrorHandler implements ErrorHandler {
|
||||||
public function handle(HttpResponseBuilder $response, HttpRequest $request, int $code, string $message): void {
|
public function handle(HandlerContext $context): void {
|
||||||
$response->setContent(sprintf('<!doctype html><title>%1$03d %2$s</title><strong>%1$03d %2$s</strong>', $code, $message));
|
if(!$context->response->needsBody)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$context->response->setTypeHTML();
|
||||||
|
$context->response->body = Stream::createStream(sprintf(
|
||||||
|
'<!doctype html><title>%1$03d %2$s</title><strong>%1$03d %2$s</strong>',
|
||||||
|
$context->response->statusCode,
|
||||||
|
$context->response->reasonPhrase === ''
|
||||||
|
? HttpResponse::defaultReasonPhase($context->response->statusCode)
|
||||||
|
: $context->response->reasonPhrase
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,24 @@
|
||||||
namespace EEPROM;
|
namespace EEPROM;
|
||||||
|
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerTrait};
|
use Index\Http\HttpResponseBuilder;
|
||||||
|
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||||
|
use Index\Http\Routing\Routes\ExactRoute;
|
||||||
|
|
||||||
class LandingRoutes implements RouteHandler {
|
class LandingRoutes implements RouteHandler {
|
||||||
use RouteHandlerTrait;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private DatabaseContext $dbCtx
|
private DatabaseContext $dbCtx
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[HttpGet('/')]
|
#[ExactRoute('GET', '/')]
|
||||||
public function getIndex($response) {
|
public function getIndex(HttpResponseBuilder $response) {
|
||||||
$response->accelRedirect('/index.html');
|
$response->accelRedirect('/index.html');
|
||||||
$response->setContentType('text/html; charset=utf-8');
|
$response->setContentType('text/html; charset=utf-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/stats.json')]
|
#[ExactRoute('GET', '/stats.json')]
|
||||||
public function getStats() {
|
public function getStats() {
|
||||||
$stats = new stdClass;
|
$stats = new stdClass;
|
||||||
$stats->uploads = 0;
|
$stats->uploads = 0;
|
||||||
|
@ -26,13 +28,13 @@ class LandingRoutes implements RouteHandler {
|
||||||
$stats->types = 0;
|
$stats->types = 0;
|
||||||
$stats->members = 0;
|
$stats->members = 0;
|
||||||
|
|
||||||
$result = $this->dbCtx->connection->query('SELECT COUNT(*), COUNT(DISTINCT user_id) FROM prm_uploads');
|
$result = $this->dbCtx->conn->query('SELECT COUNT(*), COUNT(DISTINCT user_id) FROM prm_uploads');
|
||||||
if($result->next()) {
|
if($result->next()) {
|
||||||
$stats->uploads = $result->getInteger(0);
|
$stats->uploads = $result->getInteger(0);
|
||||||
$stats->members = $result->getInteger(1);
|
$stats->members = $result->getInteger(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->dbCtx->connection->query('SELECT COUNT(*), SUM(file_size), COUNT(DISTINCT file_type) FROM prm_files');
|
$result = $this->dbCtx->conn->query('SELECT COUNT(*), SUM(file_size), COUNT(DISTINCT file_type) FROM prm_files');
|
||||||
if($result->next()) {
|
if($result->next()) {
|
||||||
$stats->files = $result->getInteger(0);
|
$stats->files = $result->getInteger(0);
|
||||||
$stats->size = $result->getInteger(1);
|
$stats->size = $result->getInteger(1);
|
||||||
|
@ -42,8 +44,8 @@ class LandingRoutes implements RouteHandler {
|
||||||
return $stats;
|
return $stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpGet('/eeprom.js')]
|
#[ExactRoute('GET', '/eeprom.js')]
|
||||||
public function getEepromJs($response) {
|
public function getEepromJs(HttpResponseBuilder $response) {
|
||||||
$response->accelRedirect('/scripts/eepromv1.js');
|
$response->accelRedirect('/scripts/eepromv1.js');
|
||||||
$response->setContentType('application/javascript; charset=utf-8');
|
$response->setContentType('application/javascript; charset=utf-8');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
namespace EEPROM\Pools;
|
namespace EEPROM\Pools;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use EEPROM\Storage\StorageRecord;
|
||||||
use EEPROM\Uploads\UploadVariantInfo;
|
use EEPROM\Uploads\UploadVariantInfo;
|
||||||
|
use Index\XArray;
|
||||||
use Index\Db\DbConnection;
|
use Index\Db\DbConnection;
|
||||||
|
|
||||||
class PoolsContext {
|
class PoolsContext {
|
||||||
|
@ -34,7 +36,7 @@ class PoolsContext {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPoolFromVariant(UploadVariantInfo $variantInfo): StorageRecord {
|
public function getPoolFromVariant(UploadVariantInfo $variantInfo): PoolInfo {
|
||||||
return $this->pools->getPool($variantInfo->fileId, PoolInfoGetField::Id);
|
return $this->pools->getPool($variantInfo->fileId, PoolInfoGetField::Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +71,10 @@ class PoolsContext {
|
||||||
if($removeStaleRule instanceof Rules\RemoveStaleRule && $removeStaleRule->inactiveForSeconds > 0)
|
if($removeStaleRule instanceof Rules\RemoveStaleRule && $removeStaleRule->inactiveForSeconds > 0)
|
||||||
$body['idle_time'] = $removeStaleRule->inactiveForSeconds;
|
$body['idle_time'] = $removeStaleRule->inactiveForSeconds;
|
||||||
|
|
||||||
$ensureVariantRules = XArray::select($rules->getRules(Rules\EnsureVariantRule::TYPE), fn($ensureVariantRule) => $ensureVariantRule->variant);
|
$ensureVariantRules = XArray::select(
|
||||||
|
$rules->getRules(Rules\EnsureVariantRule::TYPE),
|
||||||
|
fn($ensureVariantRule) => $ensureVariantRule->variant
|
||||||
|
);
|
||||||
if(!empty($ensureVariantRules))
|
if(!empty($ensureVariantRules))
|
||||||
$body['variants'] = $ensureVariantRules;
|
$body['variants'] = $ensureVariantRules;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ class PoolsData {
|
||||||
PoolInfoGetField::Id => 'pool_id = ?',
|
PoolInfoGetField::Id => 'pool_id = ?',
|
||||||
PoolInfoGetField::Name => 'pool_name = ?',
|
PoolInfoGetField::Name => 'pool_name = ?',
|
||||||
PoolInfoGetField::UploadId => 'pool_id = (SELECT pool_id FROM prm_uploads WHERE upload_id = ?)',
|
PoolInfoGetField::UploadId => 'pool_id = (SELECT pool_id FROM prm_uploads WHERE upload_id = ?)',
|
||||||
default => throw new InvalidArgumentException('$field is not an acceptable value'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$stmt = $this->cache->get(<<<SQL
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
@ -90,8 +89,10 @@ class PoolsData {
|
||||||
SELECT rule_id, pool_id, rule_type, rule_params
|
SELECT rule_id, pool_id, rule_type, rule_params
|
||||||
FROM prm_pools_rules
|
FROM prm_pools_rules
|
||||||
SQL;
|
SQL;
|
||||||
if($hasPoolInfo)
|
if($hasPoolInfo) {
|
||||||
$query .= sprintf(' %s pool_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
++$args;
|
||||||
|
$query .= ' WHERE pool_id = ?';
|
||||||
|
}
|
||||||
if($hasTypes)
|
if($hasTypes)
|
||||||
$query .= sprintf(' %s rule_type IN (%s)', ++$args > 1 ? 'AND' : 'WHERE', DbTools::prepareListString($types));
|
$query .= sprintf(' %s rule_type IN (%s)', ++$args > 1 ? 'AND' : 'WHERE', DbTools::prepareListString($types));
|
||||||
|
|
||||||
|
|
|
@ -2,42 +2,19 @@
|
||||||
namespace EEPROM;
|
namespace EEPROM;
|
||||||
|
|
||||||
use Index\Config\Config;
|
use Index\Config\Config;
|
||||||
use Index\Http\HttpRequest;
|
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||||
use Index\Http\Routing\{HttpRouter,Router,RouteHandler};
|
use Index\Http\Routing\{Router,RouteHandler};
|
||||||
|
use Index\Http\Routing\Filters\FilterInfo;
|
||||||
|
|
||||||
class RoutingContext {
|
class RoutingContext {
|
||||||
private HttpRouter $router;
|
public private(set) Router $router;
|
||||||
|
|
||||||
public function __construct(private Config $config) {
|
public function __construct(Config $config) {
|
||||||
$this->router = new HttpRouter(
|
$this->router = new Router(
|
||||||
errorHandler: new EEPROMErrorHandler,
|
errorHandler: new EEPROMErrorHandler,
|
||||||
|
accessControlHandler: new EEPROMAccessControlHandler($config->getArray('origins')),
|
||||||
);
|
);
|
||||||
$this->router->use('/', $this->middleware(...));
|
$this->router->filter(FilterInfo::prefix('/', fn(HttpResponseBuilder $response) => $response->setPoweredBy('EEPROM')));
|
||||||
}
|
|
||||||
|
|
||||||
private function middleware($response, $request) {
|
|
||||||
$response->setPoweredBy('EEPROM');
|
|
||||||
|
|
||||||
if($request->hasHeader('Origin')) {
|
|
||||||
$origin = $request->getHeaderLine('Origin');
|
|
||||||
$response->setHeader('Access-Control-Allow-Origin', $origin);
|
|
||||||
$response->setHeader('Vary', 'Origin');
|
|
||||||
$host = parse_url($origin, PHP_URL_HOST);
|
|
||||||
if(is_string($host)) {
|
|
||||||
$host = '.' . $host;
|
|
||||||
$allowCookieOrigins = $this->config->getArray('origins');
|
|
||||||
foreach($allowCookieOrigins as $allowCookieOrigin)
|
|
||||||
if(str_ends_with($host, '.' . $allowCookieOrigin)) {
|
|
||||||
$response->setHeader('Access-Control-Allow-Credentials', 'true');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRouter(): Router {
|
|
||||||
return $this->router;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(RouteHandler $handler): void {
|
public function register(RouteHandler $handler): void {
|
||||||
|
|
|
@ -79,18 +79,13 @@ class StorageFiles {
|
||||||
$target = $this->getLocalPath($hash, true);
|
$target = $this->getLocalPath($hash, true);
|
||||||
|
|
||||||
if(is_file($target)) {
|
if(is_file($target)) {
|
||||||
if($mode === StorageImportMode::Move || $mode === StorageImportMode::Upload)
|
if($mode === StorageImportMode::Move)
|
||||||
unlink($path);
|
unlink($path);
|
||||||
} else {
|
} else {
|
||||||
if($mode === StorageImportMode::Move)
|
if($mode === StorageImportMode::Move)
|
||||||
$success = rename($path, $target);
|
$success = rename($path, $target);
|
||||||
elseif($mode === StorageImportMode::Copy)
|
elseif($mode === StorageImportMode::Copy)
|
||||||
$success = copy($path, $target);
|
$success = copy($path, $target);
|
||||||
elseif($mode === StorageImportMode::Upload)
|
|
||||||
$success = move_uploaded_file($path, $target);
|
|
||||||
else
|
|
||||||
$success = false;
|
|
||||||
|
|
||||||
if(!$success)
|
if(!$success)
|
||||||
throw new RuntimeException('unable to move or copy $path using given $mode');
|
throw new RuntimeException('unable to move or copy $path using given $mode');
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,4 @@ namespace EEPROM\Storage;
|
||||||
enum StorageImportMode {
|
enum StorageImportMode {
|
||||||
case Copy;
|
case Copy;
|
||||||
case Move;
|
case Move;
|
||||||
case Upload;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,12 +37,13 @@ class StorageRecords {
|
||||||
SQL;
|
SQL;
|
||||||
|
|
||||||
$args = 0;
|
$args = 0;
|
||||||
if($orphaned !== null)
|
if($orphaned !== null) {
|
||||||
|
++$args;
|
||||||
$query .= sprintf(
|
$query .= sprintf(
|
||||||
' %s uf.file_id %s NULL',
|
' WHERE uf.file_id %s NULL',
|
||||||
++$args > 1 ? 'OR' : 'WHERE',
|
|
||||||
$orphaned ? 'IS' : 'IS NOT'
|
$orphaned ? 'IS' : 'IS NOT'
|
||||||
);
|
);
|
||||||
|
}
|
||||||
if($denied !== null)
|
if($denied !== null)
|
||||||
$query .= sprintf(
|
$query .= sprintf(
|
||||||
' %s dl.deny_hash %s NULL',
|
' %s dl.deny_hash %s NULL',
|
||||||
|
@ -63,7 +64,6 @@ class StorageRecords {
|
||||||
$field = match($field) {
|
$field = match($field) {
|
||||||
StorageRecordGetFileField::Id => 'file_id',
|
StorageRecordGetFileField::Id => 'file_id',
|
||||||
StorageRecordGetFileField::Hash => 'file_hash',
|
StorageRecordGetFileField::Hash => 'file_hash',
|
||||||
default => throw new InvalidArgumentException('$field is not an acceptable value'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$stmt = $this->cache->get(<<<SQL
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
@ -93,14 +93,14 @@ class StorageRecords {
|
||||||
if($size < 0)
|
if($size < 0)
|
||||||
throw new InvalidArgumentException('$size may not be negative');
|
throw new InvalidArgumentException('$size may not be negative');
|
||||||
|
|
||||||
$fileId = $this->snowflake->nextHash($hash);
|
$fileId = (string)$this->snowflake->nextHash($hash);
|
||||||
|
|
||||||
$stmt = $this->cache->get(<<<SQL
|
$stmt = $this->cache->get(<<<SQL
|
||||||
INSERT INTO prm_files (
|
INSERT INTO prm_files (
|
||||||
file_id, file_hash, file_type, file_size
|
file_id, file_hash, file_type, file_size
|
||||||
) VALUES (?, ?, ?, ?)
|
) VALUES (?, ?, ?, ?)
|
||||||
SQL);
|
SQL);
|
||||||
$stmt->nextParameter($fileId); // generate snowflake
|
$stmt->nextParameter($fileId);
|
||||||
$stmt->nextParameter($hash);
|
$stmt->nextParameter($hash);
|
||||||
$stmt->nextParameter($type);
|
$stmt->nextParameter($type);
|
||||||
$stmt->nextParameter($size);
|
$stmt->nextParameter($size);
|
||||||
|
@ -120,7 +120,6 @@ class StorageRecords {
|
||||||
$field = match($field) {
|
$field = match($field) {
|
||||||
StorageRecordGetFileField::Id => 'file_id',
|
StorageRecordGetFileField::Id => 'file_id',
|
||||||
StorageRecordGetFileField::Hash => 'file_hash',
|
StorageRecordGetFileField::Hash => 'file_hash',
|
||||||
default => throw new InvalidArgumentException('$field is not an acceptable value'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$stmt = $this->cache->get(<<<SQL
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
@ -142,7 +141,6 @@ class StorageRecords {
|
||||||
$field = match($field) {
|
$field = match($field) {
|
||||||
StorageRecordGetFileField::Id => '?',
|
StorageRecordGetFileField::Id => '?',
|
||||||
StorageRecordGetFileField::Hash => '(SELECT file_id FROM prm_files WHERE file_hash = ?)',
|
StorageRecordGetFileField::Hash => '(SELECT file_id FROM prm_files WHERE file_hash = ?)',
|
||||||
default => throw new InvalidArgumentException('$field is not an acceptable value'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$stmt = $this->cache->get(<<<SQL
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
|
|
@ -28,7 +28,7 @@ class TaskInfo {
|
||||||
secret: $result->getString(1),
|
secret: $result->getString(1),
|
||||||
userId: $result->getString(2),
|
userId: $result->getString(2),
|
||||||
poolId: $result->getString(3),
|
poolId: $result->getString(3),
|
||||||
state: TaskState::tryFrom($result->getString(4)) ?? TaskState::Ready,
|
state: TaskState::tryFrom($result->getString(4)) ?? TaskState::Pending,
|
||||||
remoteAddress: $result->getString(5),
|
remoteAddress: $result->getString(5),
|
||||||
name: $result->getString(6),
|
name: $result->getString(6),
|
||||||
size: $result->getInteger(7),
|
size: $result->getInteger(7),
|
||||||
|
|
|
@ -3,6 +3,7 @@ namespace EEPROM\Tasks;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use Stringable;
|
||||||
use EEPROM\SnowflakeGenerator;
|
use EEPROM\SnowflakeGenerator;
|
||||||
use EEPROM\Pools\PoolInfo;
|
use EEPROM\Pools\PoolInfo;
|
||||||
use Index\XString;
|
use Index\XString;
|
||||||
|
@ -29,8 +30,10 @@ class TasksData {
|
||||||
task_name, task_size, task_type, task_hash, UNIX_TIMESTAMP(task_created)
|
task_name, task_size, task_type, task_hash, UNIX_TIMESTAMP(task_created)
|
||||||
FROM prm_tasks
|
FROM prm_tasks
|
||||||
SQL;
|
SQL;
|
||||||
if($hasState)
|
if($hasState) {
|
||||||
$query .= sprintf(' %s task_state = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
++$args;
|
||||||
|
$query .= ' WHERE task_state = ?';
|
||||||
|
}
|
||||||
|
|
||||||
$stmt = $this->cache->get($query);
|
$stmt = $this->cache->get($query);
|
||||||
if($hasState)
|
if($hasState)
|
||||||
|
@ -77,7 +80,7 @@ class TasksData {
|
||||||
string $type,
|
string $type,
|
||||||
string $hash
|
string $hash
|
||||||
): TaskInfo {
|
): TaskInfo {
|
||||||
$taskId = $this->snowflake->nextRandom();
|
$taskId = (string)$this->snowflake->nextRandom();
|
||||||
|
|
||||||
$stmt = $this->cache->get(<<<SQL
|
$stmt = $this->cache->get(<<<SQL
|
||||||
INSERT INTO prm_tasks (
|
INSERT INTO prm_tasks (
|
||||||
|
|
|
@ -3,39 +3,36 @@ namespace EEPROM\Tasks;
|
||||||
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Index\{ByteFormat,XNumber};
|
use Index\{ByteFormat,XNumber};
|
||||||
use Index\Http\StringHttpContent;
|
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||||
use Index\Http\Routing\{HttpPut,RouteHandler,RouteHandlerTrait};
|
use Index\Http\Routing\{HttpPut,RouteHandler,RouteHandlerCommon};
|
||||||
|
use Index\Http\Routing\Routes\PatternRoute;
|
||||||
|
|
||||||
class TasksRoutes implements RouteHandler {
|
class TasksRoutes implements RouteHandler {
|
||||||
use RouteHandlerTrait;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private TasksContext $tasksCtx,
|
private TasksContext $tasksCtx,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[HttpPut('/uploads/([A-Za-z0-9]+)(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
#[PatternRoute('PUT', '/uploads/([A-Za-z0-9]+)(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
||||||
public function putUpload($response, $request, string $taskId, string $variant = '', string $extension = '') {
|
public function putUpload(
|
||||||
|
HttpResponseBuilder $response,
|
||||||
|
HttpRequest $request,
|
||||||
|
string $taskId,
|
||||||
|
string $variant = '',
|
||||||
|
string $extension = '',
|
||||||
|
) {
|
||||||
if($request->getHeaderLine('Content-Type') !== 'application/octet-stream') {
|
if($request->getHeaderLine('Content-Type') !== 'application/octet-stream') {
|
||||||
$response->setStatusCode(400);
|
$response->statusCode = 400;
|
||||||
return [
|
return [
|
||||||
'error' => 'bad_type',
|
'error' => 'bad_type',
|
||||||
'english' => 'Content-Type must be application/octet-stream.',
|
'english' => 'Content-Type must be application/octet-stream.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = $request->getContent();
|
$content = (string)$request->getBody();
|
||||||
if($content instanceof StringHttpContent) {
|
|
||||||
$content = (string)$content;
|
|
||||||
} else {
|
|
||||||
$response->setStatusCode(400);
|
|
||||||
return [
|
|
||||||
'error' => 'bad_content',
|
|
||||||
'english' => 'Request content could not be parsed as expected.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strlen($taskId) < 5) {
|
if(strlen($taskId) < 5) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'not_found',
|
'error' => 'not_found',
|
||||||
'english' => 'Requested upload task does not exist.',
|
'english' => 'Requested upload task does not exist.',
|
||||||
|
@ -43,8 +40,8 @@ class TasksRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($variant !== '') {
|
if($variant !== '') {
|
||||||
$response->setStatusCode(405);
|
$response->statusCode = 405;
|
||||||
$response->setHeader('Allow', 'HEAD, GET');
|
$response->setAllow(['HEAD', 'GET']);
|
||||||
return [
|
return [
|
||||||
'error' => 'method_not_allowed',
|
'error' => 'method_not_allowed',
|
||||||
'english' => 'PUT requests are not supported for upload variants.',
|
'english' => 'PUT requests are not supported for upload variants.',
|
||||||
|
@ -53,7 +50,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
|
|
||||||
$length = (int)$request->getHeaderLine('Content-Length');
|
$length = (int)$request->getHeaderLine('Content-Length');
|
||||||
if($length < 1) {
|
if($length < 1) {
|
||||||
$response->setStatusCode(411);
|
$response->statusCode = 411;
|
||||||
return [
|
return [
|
||||||
'error' => 'length_required',
|
'error' => 'length_required',
|
||||||
'english' => 'A Content-Length header with the size of the request body is required.',
|
'english' => 'A Content-Length header with the size of the request body is required.',
|
||||||
|
@ -61,7 +58,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strlen($content) !== $length) {
|
if(strlen($content) !== $length) {
|
||||||
$response->setStatusCode(400);
|
$response->statusCode = 400;
|
||||||
return [
|
return [
|
||||||
'error' => 'length_mismatch',
|
'error' => 'length_mismatch',
|
||||||
'english' => 'Length specified in Content-Length differs from actual request body length.',
|
'english' => 'Length specified in Content-Length differs from actual request body length.',
|
||||||
|
@ -69,7 +66,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($length > TasksContext::CHUNK_SIZE) {
|
if($length > TasksContext::CHUNK_SIZE) {
|
||||||
$response->setStatusCode(413);
|
$response->statusCode = 413;
|
||||||
return [
|
return [
|
||||||
'error' => 'content_too_large',
|
'error' => 'content_too_large',
|
||||||
'english' => sprintf(
|
'english' => sprintf(
|
||||||
|
@ -84,7 +81,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
|
|
||||||
if($request->hasHeader('X-Content-Index')) {
|
if($request->hasHeader('X-Content-Index')) {
|
||||||
if($request->hasParam('index')) {
|
if($request->hasParam('index')) {
|
||||||
$response->setStatusCode(400);
|
$response->statusCode = 400;
|
||||||
return [
|
return [
|
||||||
'error' => 'bad_param',
|
'error' => 'bad_param',
|
||||||
'english' => 'the index query parameter may not be used at the same time as the X-Content-Index header.',
|
'english' => 'the index query parameter may not be used at the same time as the X-Content-Index header.',
|
||||||
|
@ -93,19 +90,11 @@ class TasksRoutes implements RouteHandler {
|
||||||
|
|
||||||
$index = (int)filter_var($request->getHeaderLine('X-Content-Index'), FILTER_SANITIZE_NUMBER_INT);
|
$index = (int)filter_var($request->getHeaderLine('X-Content-Index'), FILTER_SANITIZE_NUMBER_INT);
|
||||||
} elseif($request->hasParam('index')) {
|
} elseif($request->hasParam('index')) {
|
||||||
if($request->hasHeader('X-Content-Index')) {
|
$index = (int)$request->getFilteredParam('index', FILTER_SANITIZE_NUMBER_INT);
|
||||||
$response->setStatusCode(400);
|
|
||||||
return [
|
|
||||||
'error' => 'bad_param',
|
|
||||||
'english' => 'the X-Content-Index header may not be used at the same time as the index query parameter.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$index = (int)$request->getParam('index', FILTER_SANITIZE_NUMBER_INT);
|
|
||||||
} else $index = 0;
|
} else $index = 0;
|
||||||
|
|
||||||
if($index < 0) {
|
if($index < 0) {
|
||||||
$response->setStatusCode(400);
|
$response->statusCode = 400;
|
||||||
return [
|
return [
|
||||||
'error' => 'bad_index',
|
'error' => 'bad_index',
|
||||||
'english' => 'index parameter must be greater than or equal to 0.',
|
'english' => 'index parameter must be greater than or equal to 0.',
|
||||||
|
@ -113,19 +102,19 @@ class TasksRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
$taskSecret = substr($taskId, -16);
|
$taskSecret = substr($taskId, -16);
|
||||||
$taskId = XNumber::fromBase62(substr($taskId, 0, -16));
|
$taskId = (string)XNumber::fromBase62(substr($taskId, 0, -16));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
|
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'not_found',
|
'error' => 'not_found',
|
||||||
'english' => 'Requested upload task does not exist.',
|
'english' => 'Requested upload task does not exist.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if(!$taskInfo->verifySecret($taskSecret)) {
|
if(!$taskInfo->verifySecret($taskSecret)) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'not_found',
|
'error' => 'not_found',
|
||||||
'english' => 'Requested upload task does not exist.',
|
'english' => 'Requested upload task does not exist.',
|
||||||
|
@ -133,7 +122,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($taskInfo->state !== TaskState::Pending) {
|
if($taskInfo->state !== TaskState::Pending) {
|
||||||
$response->setStatusCode(409);
|
$response->statusCode = 409;
|
||||||
return [
|
return [
|
||||||
'error' => 'not_pending',
|
'error' => 'not_pending',
|
||||||
'english' => 'Requested upload task is no longer accepting data.',
|
'english' => 'Requested upload task is no longer accepting data.',
|
||||||
|
@ -142,7 +131,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
|
|
||||||
$offset = TasksContext::CHUNK_SIZE * $index;
|
$offset = TasksContext::CHUNK_SIZE * $index;
|
||||||
if($offset >= $taskInfo->size) {
|
if($offset >= $taskInfo->size) {
|
||||||
$response->setStatusCode(400);
|
$response->statusCode = 400;
|
||||||
return [
|
return [
|
||||||
'error' => 'offset_too_large',
|
'error' => 'offset_too_large',
|
||||||
'english' => 'Provided offset is greater than file size.',
|
'english' => 'Provided offset is greater than file size.',
|
||||||
|
@ -152,7 +141,7 @@ class TasksRoutes implements RouteHandler {
|
||||||
$path = $this->tasksCtx->getTaskWorkingPath($taskInfo);
|
$path = $this->tasksCtx->getTaskWorkingPath($taskInfo);
|
||||||
if(!is_file($path)) {
|
if(!is_file($path)) {
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('working file despawned during PUT request: %s', $path));
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('working file despawned during PUT request: %s', $path));
|
||||||
$response->setStatusCode(500);
|
$response->statusCode = 500;
|
||||||
return [
|
return [
|
||||||
'error' => 'server_error',
|
'error' => 'server_error',
|
||||||
'english' => 'Working file no longer exist on the server, the upload process cannot continue.',
|
'english' => 'Working file no longer exist on the server, the upload process cannot continue.',
|
||||||
|
@ -162,52 +151,50 @@ class TasksRoutes implements RouteHandler {
|
||||||
$handle = fopen($path, 'rb+');
|
$handle = fopen($path, 'rb+');
|
||||||
if($handle === false) {
|
if($handle === false) {
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to open during PUT request: %s', $path));
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to open during PUT request: %s', $path));
|
||||||
$response->setStatusCode(500);
|
$response->statusCode = 500;
|
||||||
return [
|
return [
|
||||||
'error' => 'server_error',
|
'error' => 'server_error',
|
||||||
'english' => 'Was not able to open working file, the upload process cannot continue.',
|
'english' => 'Was not able to open working file, the upload process cannot continue.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if(fseek($handle, $offset, SEEK_SET) < 0) {
|
||||||
if(fseek($handle, $offset, SEEK_SET) < 0) {
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('was unable to seek during PUT request: %s', $path));
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('was unable to seek during PUT request: %s', $path));
|
$response->statusCode = 500;
|
||||||
$response->setStatusCode(500);
|
return [
|
||||||
return [
|
'error' => 'server_error',
|
||||||
'error' => 'server_error',
|
'english' => 'Was not able to seek to provided offset, the upload process cannot continue.',
|
||||||
'english' => 'Was not able to seek to provided offset, the upload process cannot continue.',
|
];
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fwrite($handle, $content, $length) === false) {
|
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('was unable to write during PUT request: %s', $path));
|
|
||||||
$response->setStatusCode(500);
|
|
||||||
return [
|
|
||||||
'error' => 'server_error',
|
|
||||||
'english' => 'Was not able to seek to write to file, the upload process cannot continue.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if(!fflush($handle)) {
|
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to flush file contents during PUT request: %s', $path));
|
|
||||||
$response->setStatusCode(500);
|
|
||||||
return [
|
|
||||||
'error' => 'server_error',
|
|
||||||
'english' => 'Was not able to flush file properly, the upload process cannot continue.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!fclose($handle)) {
|
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to close file contents during PUT request: %s', $path));
|
|
||||||
$response->setStatusCode(500);
|
|
||||||
return [
|
|
||||||
'error' => 'server_error',
|
|
||||||
'english' => 'Was not able to close file properly, the upload process cannot continue.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->setStatusCode(202);
|
if(fwrite($handle, $content, $length) === false) {
|
||||||
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('was unable to write during PUT request: %s', $path));
|
||||||
|
$response->statusCode = 500;
|
||||||
|
return [
|
||||||
|
'error' => 'server_error',
|
||||||
|
'english' => 'Was not able to seek to write to file, the upload process cannot continue.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!fflush($handle)) {
|
||||||
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to flush file contents during PUT request: %s', $path));
|
||||||
|
$response->statusCode = 500;
|
||||||
|
return [
|
||||||
|
'error' => 'server_error',
|
||||||
|
'english' => 'Was not able to flush file properly, the upload process cannot continue.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!fclose($handle)) {
|
||||||
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to close file contents during PUT request: %s', $path));
|
||||||
|
$response->statusCode = 500;
|
||||||
|
return [
|
||||||
|
'error' => 'server_error',
|
||||||
|
'english' => 'Was not able to close file properly, the upload process cannot continue.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->statusCode = 202;
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,11 +65,8 @@ final class TasksRpcHandler implements RpcHandler {
|
||||||
if($signature === null)
|
if($signature === null)
|
||||||
return 'srpp';
|
return 'srpp';
|
||||||
|
|
||||||
if(!str_contains($signature, '=')) {
|
if(!str_contains($signature, '='))
|
||||||
$signature = UriBase64::decode($signature);
|
$signature = UriBase64::decode($signature);
|
||||||
if(!is_string($signature))
|
|
||||||
$signature = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$signature = array_column(XArray::select(
|
$signature = array_column(XArray::select(
|
||||||
explode(';', $signature),
|
explode(';', $signature),
|
||||||
|
@ -94,7 +91,7 @@ final class TasksRpcHandler implements RpcHandler {
|
||||||
if($sAlgo !== 'S256')
|
if($sAlgo !== 'S256')
|
||||||
return 'sanv';
|
return 'sanv';
|
||||||
|
|
||||||
if(abs($cTime - $sTime) >= 30)
|
if(abs($cTime - (int)$sTime) >= 30)
|
||||||
return 'stnv';
|
return 'stnv';
|
||||||
|
|
||||||
if(strlen($sSalt) < 20)
|
if(strlen($sSalt) < 20)
|
||||||
|
@ -182,15 +179,13 @@ final class TasksRpcHandler implements RpcHandler {
|
||||||
return 'wfcf';
|
return 'wfcf';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if(!ftruncate($data, $fileSize)) {
|
||||||
if(!ftruncate($data, $fileSize)) {
|
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to reserve %d bytes for task working file: %s', $fileSize, $path));
|
||||||
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to reserve %d bytes for task working file: %s', $fileSize, $path));
|
return 'wfrf';
|
||||||
return 'wfrf';
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if(!fclose($data))
|
|
||||||
return 'wfff';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!fclose($data))
|
||||||
|
return 'wfff';
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
return 'utcf';
|
return 'utcf';
|
||||||
}
|
}
|
||||||
|
@ -210,7 +205,7 @@ final class TasksRpcHandler implements RpcHandler {
|
||||||
#[RpcQuery('eeprom:tasks:get')]
|
#[RpcQuery('eeprom:tasks:get')]
|
||||||
public function actionTasksGet(string $taskId): string|array {
|
public function actionTasksGet(string $taskId): string|array {
|
||||||
$taskSecret = substr($taskId, -16);
|
$taskSecret = substr($taskId, -16);
|
||||||
$taskId = XNumber::fromBase62(substr($taskId, 0, -16));
|
$taskId = (string)XNumber::fromBase62(substr($taskId, 0, -16));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
|
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
|
||||||
|
@ -256,7 +251,7 @@ final class TasksRpcHandler implements RpcHandler {
|
||||||
#[RpcAction('eeprom:tasks:finish')]
|
#[RpcAction('eeprom:tasks:finish')]
|
||||||
public function actionsTasksFinish(string $taskId) {
|
public function actionsTasksFinish(string $taskId) {
|
||||||
$taskSecret = substr($taskId, -16);
|
$taskSecret = substr($taskId, -16);
|
||||||
$taskId = XNumber::fromBase62(substr($taskId, 0, -16));
|
$taskId = (string)XNumber::fromBase62(substr($taskId, 0, -16));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
|
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
|
||||||
|
|
|
@ -77,7 +77,7 @@ class UploadsContext {
|
||||||
'hash' => $fileInfo->hashHexString,
|
'hash' => $fileInfo->hashHexString,
|
||||||
'created' => $uploadInfo->createdAt->toIso8601ZuluString(),
|
'created' => $uploadInfo->createdAt->toIso8601ZuluString(),
|
||||||
'accessed' => $uploadInfo->accessedAt->toIso8601ZuluString(),
|
'accessed' => $uploadInfo->accessedAt->toIso8601ZuluString(),
|
||||||
'expires' => $uploadInfo->accessedAt->add(2, 'weeks')->toIso8601ZuluString(),
|
'expires' => $uploadInfo->accessedAt->add('weeks', 2)->toIso8601ZuluString(),
|
||||||
|
|
||||||
// These can never be reached, and in situation where they technically could it's because of an outdated local record
|
// These can never be reached, and in situation where they technically could it's because of an outdated local record
|
||||||
'deleted' => null,
|
'deleted' => null,
|
||||||
|
|
|
@ -13,7 +13,7 @@ class UploadsData {
|
||||||
private DbStatementCache $cache;
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private DbConnection $dbConn,
|
DbConnection $dbConn,
|
||||||
private SnowflakeGenerator $snowflake
|
private SnowflakeGenerator $snowflake
|
||||||
) {
|
) {
|
||||||
$this->cache = new DbStatementCache($dbConn);
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
|
@ -48,8 +48,10 @@ class UploadsData {
|
||||||
SQL;
|
SQL;
|
||||||
|
|
||||||
$args = 0;
|
$args = 0;
|
||||||
if($hasPoolInfo)
|
if($hasPoolInfo) {
|
||||||
$query .= sprintf(' %s u.pool_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
++$args;
|
||||||
|
$query .= ' WHERE u.pool_id = ?';
|
||||||
|
}
|
||||||
if($hasUserId)
|
if($hasUserId)
|
||||||
$query .= sprintf(' %s u.user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
$query .= sprintf(' %s u.user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||||
if($hasVariantExists)
|
if($hasVariantExists)
|
||||||
|
|
|
@ -5,12 +5,19 @@ use RuntimeException;
|
||||||
use EEPROM\Auth\AuthContext;
|
use EEPROM\Auth\AuthContext;
|
||||||
use EEPROM\Denylist\{DenylistContext,DenylistReason};
|
use EEPROM\Denylist\{DenylistContext,DenylistReason};
|
||||||
use EEPROM\Pools\PoolsContext;
|
use EEPROM\Pools\PoolsContext;
|
||||||
|
use EEPROM\Pools\Rules\{ConstrainSizeRule,EnsureVariantRule,EnsureVariantRuleThumb};
|
||||||
use EEPROM\Storage\{StorageContext,StorageImportMode,StorageRecordGetFileField};
|
use EEPROM\Storage\{StorageContext,StorageImportMode,StorageRecordGetFileField};
|
||||||
use Index\{ByteFormat,XNumber};
|
use Index\{ByteFormat,XArray,XNumber};
|
||||||
use Index\Http\Routing\{HttpDelete,HttpOptions,HttpPost,RouteHandler,RouteHandlerTrait};
|
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||||
|
use Index\Http\Content\MultipartFormContent;
|
||||||
|
use Index\Http\Content\Multipart\FileMultipartFormData;
|
||||||
|
use Index\Http\Routing\{HttpDelete,HttpOptions,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||||
|
use Index\Http\Routing\AccessControl\AccessControl;
|
||||||
|
use Index\Http\Routing\Processors\Before;
|
||||||
|
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||||
|
|
||||||
class UploadsLegacyRoutes implements RouteHandler {
|
class UploadsLegacyRoutes implements RouteHandler {
|
||||||
use RouteHandlerTrait;
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AuthContext $authCtx,
|
private AuthContext $authCtx,
|
||||||
|
@ -20,21 +27,111 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
private DenylistContext $denylistCtx
|
private DenylistContext $denylistCtx
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[HttpOptions('/uploads')]
|
#[AccessControl]
|
||||||
public function optionsUpload($response, $request): int {
|
#[PatternRoute('GET', '/uploads/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
||||||
$response->setHeader('Access-Control-Allow-Headers', 'Authorization');
|
public function getUpload(
|
||||||
$response->setHeader('Access-Control-Allow-Methods', 'POST');
|
HttpResponseBuilder $response,
|
||||||
|
HttpRequest $request,
|
||||||
|
string $uploadId,
|
||||||
|
string $variant = '',
|
||||||
|
string $extension = ''
|
||||||
|
) {
|
||||||
|
if(strlen($uploadId) < 5)
|
||||||
|
return 404;
|
||||||
|
|
||||||
return 204;
|
$variantQuery = false;
|
||||||
|
if($variant === '' && $request->hasParam('v')) {
|
||||||
|
$variantQuery = true;
|
||||||
|
$variant = (string)$request->getParam('v');
|
||||||
|
}
|
||||||
|
|
||||||
|
$isV1Json = ($variantQuery || $variant === '') && $extension === 'json';
|
||||||
|
if($isV1Json)
|
||||||
|
$variant = '';
|
||||||
|
|
||||||
|
if(strlen($uploadId) === 32) {
|
||||||
|
$uploadId = $this->uploadsCtx->uploads->resolveLegacyId($uploadId) ?? $uploadId;
|
||||||
|
$uploadSecret = null;
|
||||||
|
} else {
|
||||||
|
$uploadSecret = substr($uploadId, -4);
|
||||||
|
$uploadId = XNumber::fromBase62(substr($uploadId, 0, -4));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$uploadInfo = $this->uploadsCtx->uploads->getUpload(uploadId: $uploadId, secret: $uploadSecret);
|
||||||
|
} catch(RuntimeException $ex) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$variantInfo = $this->uploadsCtx->uploads->getUploadVariant($uploadInfo, $variant);
|
||||||
|
} catch(RuntimeException $ex) {
|
||||||
|
if($variant === '')
|
||||||
|
return 404;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$poolInfo = $this->poolsCtx->pools->getPool($uploadInfo->poolId);
|
||||||
|
$originalInfo = $this->storageCtx->records->getFile(
|
||||||
|
$this->uploadsCtx->uploads->getUploadVariant($uploadInfo, '')->fileId
|
||||||
|
);
|
||||||
|
|
||||||
|
$rule = XArray::first(
|
||||||
|
$this->poolsCtx->getPoolRules($poolInfo)->getRules(EnsureVariantRule::TYPE),
|
||||||
|
fn($rule) => $rule->variant === $variant
|
||||||
|
);
|
||||||
|
if($rule instanceof EnsureVariantRule && $rule->onAccess) {
|
||||||
|
if($rule->params instanceof EnsureVariantRuleThumb) {
|
||||||
|
$storageInfo = $this->storageCtx->createThumbnailFromRule($originalInfo, $rule->params);
|
||||||
|
$variantInfo = $this->uploadsCtx->uploads->createUploadVariant($uploadInfo, $rule->variant, $storageInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(RuntimeException $ex) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isset($variantInfo))
|
||||||
|
return 404;
|
||||||
|
|
||||||
|
if(!isset($storageInfo))
|
||||||
|
try {
|
||||||
|
$storageInfo = $this->storageCtx->records->getFile($variantInfo->fileId);
|
||||||
|
} catch(RuntimeException $ex) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->uploadsCtx->uploads->updateUpload(
|
||||||
|
$uploadInfo,
|
||||||
|
accessedAt: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
$denyInfo = $this->denylistCtx->getDenylistEntry($storageInfo);
|
||||||
|
if($denyInfo !== null)
|
||||||
|
return $denyInfo->isCopyrightTakedown ? 451 : 410;
|
||||||
|
|
||||||
|
if($isV1Json)
|
||||||
|
return $this->uploadsCtx->convertToClientJsonV1($uploadInfo, $variantInfo, $storageInfo);
|
||||||
|
|
||||||
|
$fileName = $uploadInfo->name;
|
||||||
|
if($variantInfo->variant !== '') // FAIRLY SIGNIFICANT TODO: obviously this should support more file exts than .jpg...
|
||||||
|
$fileName = sprintf('%s-%s.jpg', pathinfo($fileName, PATHINFO_FILENAME), $variantInfo->variant);
|
||||||
|
|
||||||
|
$contentType = $variantInfo->type ?? $storageInfo->type;
|
||||||
|
if($contentType === 'application/octet-stream' || str_starts_with($contentType, 'text/'))
|
||||||
|
$contentType = 'text/plain';
|
||||||
|
|
||||||
|
$response->accelRedirect($this->storageCtx->files->getRemotePath($storageInfo));
|
||||||
|
$response->setContentType($contentType);
|
||||||
|
$response->setFileName(addslashes($fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpPost('/uploads')]
|
#[AccessControl(credentials: true, allowHeaders: ['Authorization'])]
|
||||||
public function postUpload($response, $request) {
|
#[Before('input:multipart')]
|
||||||
if(!$request->isFormContent())
|
#[Before('eeprom:attempt-auth')]
|
||||||
return 400;
|
#[ExactRoute('POST', '/uploads')]
|
||||||
|
public function postUpload(HttpResponseBuilder $response, HttpRequest $request, MultipartFormContent $content) {
|
||||||
if(!$this->authCtx->info->authed) {
|
if(!$this->authCtx->info->authed) {
|
||||||
$response->setStatusCode(401);
|
$response->statusCode = 401;
|
||||||
return [
|
return [
|
||||||
'error' => 'auth',
|
'error' => 'auth',
|
||||||
'english' => "You must be logged in to upload files. If you are and you're getting this error anyway, try refreshing the page!",
|
'english' => "You must be logged in to upload files. If you are and you're getting this error anyway, try refreshing the page!",
|
||||||
|
@ -42,29 +139,25 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->authCtx->info->restricted) {
|
if($this->authCtx->info->restricted) {
|
||||||
$response->setStatusCode(403);
|
$response->statusCode = 403;
|
||||||
return [
|
return [
|
||||||
'error' => 'restricted',
|
'error' => 'restricted',
|
||||||
'english' => 'You are currently banned and are not allowed to upload or manage files.',
|
'english' => 'You are currently banned and are not allowed to upload or manage files.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = $request->getContent();
|
$file = $content->getParamData('file');
|
||||||
|
if(!($file instanceof FileMultipartFormData)) {
|
||||||
try {
|
$response->statusCode = 400;
|
||||||
$file = $content->getUploadedFile('file');
|
|
||||||
} catch(RuntimeException $ex) {
|
|
||||||
$response->setStatusCode(400);
|
|
||||||
return [
|
return [
|
||||||
'error' => 'file',
|
'error' => 'file',
|
||||||
'english' => 'File upload is missing from request body.',
|
'english' => 'File upload is missing from request body.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$localFile = $file->getLocalFileName();
|
$fileSize = $file->getSize();
|
||||||
$fileSize = filesize($localFile);
|
|
||||||
if($fileSize < 1) {
|
if($fileSize < 1) {
|
||||||
$response->setStatusCode(400);
|
$response->statusCode = 400;
|
||||||
return [
|
return [
|
||||||
'error' => 'empty',
|
'error' => 'empty',
|
||||||
'english' => 'The file you attempted to upload is empty.',
|
'english' => 'The file you attempted to upload is empty.',
|
||||||
|
@ -72,16 +165,16 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$poolInfo = $this->poolsCtx->pools->getPool((string)$content->getParam('src', FILTER_VALIDATE_INT));
|
$poolInfo = $this->poolsCtx->pools->getPool((string)$content->getFilteredParam('src', FILTER_VALIDATE_INT));
|
||||||
if($poolInfo->protected) {
|
if($poolInfo->protected) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'pool_incompatible',
|
'error' => 'pool_incompatible',
|
||||||
'english' => 'The target upload pool is not compatible with this endpoint.',
|
'english' => 'The target upload pool is not compatible with this endpoint.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'pool',
|
'error' => 'pool',
|
||||||
'english' => 'The target upload pool does not exist.',
|
'english' => 'The target upload pool does not exist.',
|
||||||
|
@ -92,8 +185,8 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
|
|
||||||
// If there's no constrain_size rule on this pool its likely not meant to be used through this API!
|
// If there's no constrain_size rule on this pool its likely not meant to be used through this API!
|
||||||
$maxSizeRule = $rules->getRule('constrain_size');
|
$maxSizeRule = $rules->getRule('constrain_size');
|
||||||
if($maxSizeRule === null) {
|
if(!($maxSizeRule instanceof ConstrainSizeRule)) {
|
||||||
$response->setStatusCode(500);
|
$response->statusCode = 500;
|
||||||
return [
|
return [
|
||||||
'error' => 'size_rule_missing',
|
'error' => 'size_rule_missing',
|
||||||
'english' => 'A pool size constraint rule is required any this pool does not specify one.',
|
'english' => 'A pool size constraint rule is required any this pool does not specify one.',
|
||||||
|
@ -104,10 +197,10 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
if($maxSizeRule->allowLegacyMultiplier)
|
if($maxSizeRule->allowLegacyMultiplier)
|
||||||
$maxFileSize *= $this->authCtx->info->legacyMultiplier;
|
$maxFileSize *= $this->authCtx->info->legacyMultiplier;
|
||||||
|
|
||||||
if($file->getSize() !== $fileSize || $fileSize > $maxFileSize) {
|
if($fileSize > $maxFileSize) {
|
||||||
$response->setStatusCode(413);
|
$response->statusCode = 413;
|
||||||
$response->setHeader('Access-Control-Expose-Headers', 'X-EEPROM-Max-Size');
|
$response->setHeader('Access-Control-Expose-Headers', 'X-EEPROM-Max-Size');
|
||||||
$response->setHeader('X-EEPROM-Max-Size', $maxFileSize);
|
$response->setHeader('X-EEPROM-Max-Size', (string)$maxFileSize);
|
||||||
return [
|
return [
|
||||||
'error' => 'size',
|
'error' => 'size',
|
||||||
'english' => sprintf(
|
'english' => sprintf(
|
||||||
|
@ -120,11 +213,13 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$hash = hash_file('sha256', $localFile, true);
|
$tmpFile = tempnam(sys_get_temp_dir(), 'eeprom-upload-');
|
||||||
|
$file->moveTo($tmpFile);
|
||||||
|
$hash = hash_file('sha256', $tmpFile, true);
|
||||||
|
|
||||||
$denyInfo = $this->denylistCtx->getDenylistEntry($hash);
|
$denyInfo = $this->denylistCtx->getDenylistEntry($hash);
|
||||||
if($denyInfo !== null) {
|
if($denyInfo !== null) {
|
||||||
$response->setStatusCode(451);
|
$response->statusCode = 451;
|
||||||
return [
|
return [
|
||||||
'error' => 'takedown',
|
'error' => 'takedown',
|
||||||
'english' => sprintf(
|
'english' => sprintf(
|
||||||
|
@ -139,17 +234,17 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileName = $file->getSuggestedFileName();
|
$fileName = $file->getClientFilename();
|
||||||
$mediaType = (string)$file->getSuggestedMediaType();
|
$mediaType = $file->getClientMediaType();
|
||||||
if($mediaType === '/' || $mediaType === 'application/octet-stream')
|
if($mediaType === null || $mediaType === 'application/octet-stream')
|
||||||
$mediaType = mime_content_type($localFile);
|
$mediaType = mime_content_type($tmpFile);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$storageInfo = $this->storageCtx->records->getFile($hash, StorageRecordGetFileField::Hash);
|
$storageInfo = $this->storageCtx->records->getFile($hash, StorageRecordGetFileField::Hash);
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
$storageInfo = $this->storageCtx->importFile(
|
$storageInfo = $this->storageCtx->importFile(
|
||||||
$file->getLocalFileName(),
|
$tmpFile,
|
||||||
StorageImportMode::Upload,
|
StorageImportMode::Move,
|
||||||
$mediaType
|
$mediaType
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -172,14 +267,14 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
$poolInfo,
|
$poolInfo,
|
||||||
$this->authCtx->info->userId,
|
$this->authCtx->info->userId,
|
||||||
$fileName,
|
$fileName,
|
||||||
$request->getRemoteAddress()
|
$request->remoteAddress
|
||||||
);
|
);
|
||||||
$variantInfo = $this->uploadsCtx->uploads->createUploadVariant(
|
$variantInfo = $this->uploadsCtx->uploads->createUploadVariant(
|
||||||
$uploadInfo, '', $storageInfo, $mediaType
|
$uploadInfo, '', $storageInfo, $mediaType
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->setStatusCode(201);
|
$response->statusCode = 201;
|
||||||
$response->setHeader('Content-Type', 'application/json; charset=utf-8');
|
$response->setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
|
||||||
return $this->uploadsCtx->convertToClientJsonV1($uploadInfo, $variantInfo, $storageInfo, [
|
return $this->uploadsCtx->convertToClientJsonV1($uploadInfo, $variantInfo, $storageInfo, [
|
||||||
|
@ -187,10 +282,12 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[HttpDelete('/uploads/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
#[AccessControl(credentials: true, allowHeaders: ['Authorization'])]
|
||||||
public function deleteUpload($response, $request, string $uploadId) {
|
#[Before('eeprom:attempt-auth')]
|
||||||
|
#[PatternRoute('DELETE', '/uploads/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
||||||
|
public function deleteUpload(HttpResponseBuilder $response, HttpRequest $request, string $uploadId) {
|
||||||
if(!$this->authCtx->info->authed) {
|
if(!$this->authCtx->info->authed) {
|
||||||
$response->setStatusCode(401);
|
$response->statusCode = 401;
|
||||||
return [
|
return [
|
||||||
'error' => 'auth',
|
'error' => 'auth',
|
||||||
'english' => "You must be logged in to delete uploaded files. If you are and you're getting this error anyway, try refreshing the page!",
|
'english' => "You must be logged in to delete uploaded files. If you are and you're getting this error anyway, try refreshing the page!",
|
||||||
|
@ -198,7 +295,7 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strlen($uploadId) < 5) {
|
if(strlen($uploadId) < 5) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'none',
|
'error' => 'none',
|
||||||
'english' => 'That file does not exist.',
|
'english' => 'That file does not exist.',
|
||||||
|
@ -216,7 +313,7 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
try {
|
try {
|
||||||
$uploadInfo = $this->uploadsCtx->uploads->getUpload(uploadId: $uploadId, secret: $uploadSecret);
|
$uploadInfo = $this->uploadsCtx->uploads->getUpload(uploadId: $uploadId, secret: $uploadSecret);
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
$response->setStatusCode(404);
|
$response->statusCode = 404;
|
||||||
return [
|
return [
|
||||||
'error' => 'none',
|
'error' => 'none',
|
||||||
'english' => 'That file does not exist.',
|
'english' => 'That file does not exist.',
|
||||||
|
@ -224,7 +321,7 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->authCtx->info->restricted) {
|
if($this->authCtx->info->restricted) {
|
||||||
$response->setStatusCode(403);
|
$response->statusCode = 403;
|
||||||
return [
|
return [
|
||||||
'error' => 'restricted',
|
'error' => 'restricted',
|
||||||
'english' => 'You are currently banned and are not allowed to upload or manage files.',
|
'english' => 'You are currently banned and are not allowed to upload or manage files.',
|
||||||
|
@ -232,7 +329,7 @@ class UploadsLegacyRoutes implements RouteHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->authCtx->info->userId !== $uploadInfo->userId) {
|
if($this->authCtx->info->userId !== $uploadInfo->userId) {
|
||||||
$response->setStatusCode(403);
|
$response->statusCode = 403;
|
||||||
return [
|
return [
|
||||||
'error' => 'owner',
|
'error' => 'owner',
|
||||||
'english' => "You aren't allowed to delete files uploaded by others.",
|
'english' => "You aren't allowed to delete files uploaded by others.",
|
||||||
|
|
|
@ -71,7 +71,7 @@ final class UploadsRpcHandler implements RpcHandler {
|
||||||
return [
|
return [
|
||||||
'result' => $this->uploadsCtx->extractUpload(
|
'result' => $this->uploadsCtx->extractUpload(
|
||||||
$this->uploadsCtx->uploads->getUpload(
|
$this->uploadsCtx->uploads->getUpload(
|
||||||
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
|
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
|
||||||
secret: substr($uploadId, -4),
|
secret: substr($uploadId, -4),
|
||||||
userId: $userId
|
userId: $userId
|
||||||
),
|
),
|
||||||
|
@ -93,7 +93,7 @@ final class UploadsRpcHandler implements RpcHandler {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
|
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
|
||||||
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
|
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
|
||||||
secret: substr($uploadId, -4),
|
secret: substr($uploadId, -4),
|
||||||
);
|
);
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
|
@ -120,7 +120,7 @@ final class UploadsRpcHandler implements RpcHandler {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
|
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
|
||||||
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
|
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
|
||||||
secret: substr($uploadId, -4),
|
secret: substr($uploadId, -4),
|
||||||
);
|
);
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
|
@ -149,7 +149,7 @@ final class UploadsRpcHandler implements RpcHandler {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
|
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
|
||||||
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
|
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
|
||||||
secret: substr($uploadId, -4),
|
secret: substr($uploadId, -4),
|
||||||
);
|
);
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
|
|
|
@ -7,39 +7,30 @@ use EEPROM\Pools\PoolsContext;
|
||||||
use EEPROM\Pools\Rules\{EnsureVariantRule,EnsureVariantRuleThumb};
|
use EEPROM\Pools\Rules\{EnsureVariantRule,EnsureVariantRuleThumb};
|
||||||
use EEPROM\Storage\StorageContext;
|
use EEPROM\Storage\StorageContext;
|
||||||
use Index\{XArray,XNumber};
|
use Index\{XArray,XNumber};
|
||||||
use Index\Http\Routing\{HandlerAttribute,HttpGet,HttpOptions,Router,RouteHandler};
|
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||||
|
use Index\Http\Routing\{Router,RouteHandler,RouteHandlerCommon};
|
||||||
|
use Index\Http\Routing\AccessControl\AccessControl;
|
||||||
|
use Index\Http\Routing\Routes\PatternRoute;
|
||||||
|
|
||||||
class UploadsViewRoutes implements RouteHandler {
|
class UploadsViewRoutes implements RouteHandler {
|
||||||
|
use RouteHandlerCommon;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private PoolsContext $poolsCtx,
|
private PoolsContext $poolsCtx,
|
||||||
private UploadsContext $uploadsCtx,
|
private UploadsContext $uploadsCtx,
|
||||||
private StorageContext $storageCtx,
|
private StorageContext $storageCtx,
|
||||||
private DenylistContext $denylistCtx,
|
private DenylistContext $denylistCtx
|
||||||
private bool $isApiDomain
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function registerRoutes(Router $router): void {
|
#[AccessControl]
|
||||||
if($this->isApiDomain)
|
#[PatternRoute('GET', '/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
||||||
$router = $router->scopeTo('/uploads');
|
public function getUpload(
|
||||||
|
HttpResponseBuilder $response,
|
||||||
HandlerAttribute::register($router, $this);
|
HttpRequest $request,
|
||||||
}
|
string $uploadId,
|
||||||
|
string $variant = '',
|
||||||
#[HttpOptions('/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
string $extension = ''
|
||||||
public function optionsUpload($response, $request, string $uploadId, string $variant = '', string $extension = ''): int {
|
) {
|
||||||
if($this->isApiDomain && $variant === '') {
|
|
||||||
$response->setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type, Content-Length, X-Content-Index');
|
|
||||||
$response->setHeader('Access-Control-Allow-Methods', 'HEAD, GET, PUT, DELETE');
|
|
||||||
$response->setHeader('Access-Control-Max-Age', '300');
|
|
||||||
} else {
|
|
||||||
$response->setHeader('Access-Control-Allow-Methods', 'HEAD, GET');
|
|
||||||
}
|
|
||||||
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[HttpGet('/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
|
|
||||||
public function getUpload($response, $request, string $uploadId, string $variant = '', string $extension = '') {
|
|
||||||
if(strlen($uploadId) < 5)
|
if(strlen($uploadId) < 5)
|
||||||
return 404;
|
return 404;
|
||||||
|
|
||||||
|
@ -49,10 +40,6 @@ class UploadsViewRoutes implements RouteHandler {
|
||||||
$variant = (string)$request->getParam('v');
|
$variant = (string)$request->getParam('v');
|
||||||
}
|
}
|
||||||
|
|
||||||
$isV1Json = $this->isApiDomain && ($variantQuery || $variant === '') && $extension === 'json';
|
|
||||||
if($isV1Json)
|
|
||||||
$variant = '';
|
|
||||||
|
|
||||||
if(strlen($uploadId) === 32) {
|
if(strlen($uploadId) === 32) {
|
||||||
$uploadId = $this->uploadsCtx->uploads->resolveLegacyId($uploadId) ?? $uploadId;
|
$uploadId = $this->uploadsCtx->uploads->resolveLegacyId($uploadId) ?? $uploadId;
|
||||||
$uploadSecret = null;
|
$uploadSecret = null;
|
||||||
|
@ -113,9 +100,6 @@ class UploadsViewRoutes implements RouteHandler {
|
||||||
if($denyInfo !== null)
|
if($denyInfo !== null)
|
||||||
return $denyInfo->isCopyrightTakedown ? 451 : 410;
|
return $denyInfo->isCopyrightTakedown ? 451 : 410;
|
||||||
|
|
||||||
if($isV1Json)
|
|
||||||
return $this->uploadsCtx->convertToClientJsonV1($uploadInfo, $variantInfo, $storageInfo);
|
|
||||||
|
|
||||||
$fileName = $uploadInfo->name;
|
$fileName = $uploadInfo->name;
|
||||||
if($variantInfo->variant !== '') // FAIRLY SIGNIFICANT TODO: obviously this should support more file exts than .jpg...
|
if($variantInfo->variant !== '') // FAIRLY SIGNIFICANT TODO: obviously this should support more file exts than .jpg...
|
||||||
$fileName = sprintf('%s-%s.jpg', pathinfo($fileName, PATHINFO_FILENAME), $variantInfo->variant);
|
$fileName = sprintf('%s-%s.jpg', pathinfo($fileName, PATHINFO_FILENAME), $variantInfo->variant);
|
||||||
|
|
|
@ -2,20 +2,20 @@
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../eeprom.php';
|
require_once __DIR__ . '/../eeprom.php';
|
||||||
|
|
||||||
$lockPath = $eeprom->database->getMigrateLockPath();
|
$lockPath = $eeprom->dbCtx->getMigrateLockPath();
|
||||||
if(is_file($lockPath))
|
if(is_file($lockPath))
|
||||||
die('A migration script is already running.' . PHP_EOL);
|
die('A migration script is already running.' . PHP_EOL);
|
||||||
|
|
||||||
touch($lockPath);
|
touch($lockPath);
|
||||||
try {
|
try {
|
||||||
echo 'Creating migration manager...' . PHP_EOL;
|
echo 'Creating migration manager...' . PHP_EOL;
|
||||||
$manager = $eeprom->database->createMigrationManager();
|
$manager = $eeprom->dbCtx->createMigrationManager();
|
||||||
|
|
||||||
echo 'Preparing to run migrations...' . PHP_EOL;
|
echo 'Preparing to run migrations...' . PHP_EOL;
|
||||||
$manager->init();
|
$manager->init();
|
||||||
|
|
||||||
echo 'Creating migration repository...' . PHP_EOL;
|
echo 'Creating migration repository...' . PHP_EOL;
|
||||||
$repo = $eeprom->database->createMigrationRepo();
|
$repo = $eeprom->dbCtx->createMigrationRepo();
|
||||||
|
|
||||||
echo 'Running migrations...' . PHP_EOL;
|
echo 'Running migrations...' . PHP_EOL;
|
||||||
$completed = $manager->processMigrations($repo);
|
$completed = $manager->processMigrations($repo);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../eeprom.php';
|
require_once __DIR__ . '/../eeprom.php';
|
||||||
|
|
||||||
$lockPath = $eeprom->database->getMigrateLockPath();
|
$lockPath = $eeprom->dbCtx->getMigrateLockPath();
|
||||||
if(is_file($lockPath)) {
|
if(is_file($lockPath)) {
|
||||||
printf('Removing migration lock...%s', PHP_EOL);
|
printf('Removing migration lock...%s', PHP_EOL);
|
||||||
unlink($lockPath);
|
unlink($lockPath);
|
||||||
|
|
|
@ -4,14 +4,14 @@ use Index\Db\Migration\FsDbMigrationRepo;
|
||||||
|
|
||||||
require_once __DIR__ . '/../eeprom.php';
|
require_once __DIR__ . '/../eeprom.php';
|
||||||
|
|
||||||
$repo = $eeprom->database->createMigrationRepo();
|
$repo = $eeprom->dbCtx->createMigrationRepo();
|
||||||
if(!($repo instanceof FsDbMigrationRepo)) {
|
if(!($repo instanceof FsDbMigrationRepo)) {
|
||||||
echo 'Migration repository type does not support creation of templates.' . PHP_EOL;
|
echo 'Migration repository type does not support creation of templates.' . PHP_EOL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$baseName = implode(' ', array_slice($argv, 1));
|
$baseName = implode(' ', array_slice($argv, 1));
|
||||||
$manager = $eeprom->database->createMigrationManager();
|
$manager = $eeprom->dbCtx->createMigrationManager();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$names = $manager->createNames($baseName);
|
$names = $manager->createNames($baseName);
|
||||||
|
|
Reference in a new issue