flashii/eeprom
Archived
3
0
Fork 1

Updated EEPROM to latest Index.

This commit is contained in:
flash 2025-03-22 20:08:22 +00:00
parent f1513d1c0f
commit 1d4e7f0918
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
27 changed files with 784 additions and 464 deletions

View file

@ -1,13 +1,13 @@
{
"require": {
"flashwave/index": "^0.2410",
"flashii/rpcii": "^3.0",
"flashii/apii": "^0.3",
"flashwave/index": "^0.2503",
"flashii/rpcii": "^5.0",
"flashii/apii": "^0.4",
"sentry/sdk": "^4.0",
"nesbot/carbon": "^3.7"
"nesbot/carbon": "^3.8"
},
"require-dev": {
"phpstan/phpstan": "^2.0"
"phpstan/phpstan": "^2.1"
},
"autoload": {
"psr-4": {

535
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7009c2a98d8882c938653f4455f52a82",
"content-hash": "2d07dcd636742c34625c576e67eaaf0b",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@ -77,18 +77,20 @@
},
{
"name": "flashii/apii",
"version": "v0.3.0",
"version": "v0.4.0",
"source": {
"type": "git",
"url": "https://patchii.net/flashii/apii-php.git",
"reference": "2d6c135faddd359341762afcb9c429e279d87059"
"reference": "d330e0792515dbae2832bd8e2ac761cc685175e9"
},
"require": {
"php": ">=8.1"
"guzzlehttp/guzzle": "~7.9",
"php": ">=8.1",
"psr/http-client": "^1.0"
},
"require-dev": {
"phpstan/phpstan": "^1.12",
"phpunit/phpunit": "^10.5"
"phpstan/phpstan": "~2.1",
"phpunit/phpunit": "~10.5"
},
"type": "library",
"autoload": {
@ -110,24 +112,26 @@
],
"description": "Client library for the Flashii.net API.",
"homepage": "https://api.flashii.net",
"time": "2024-11-22T21:36:01+00:00"
"time": "2025-03-20T17:05:52+00:00"
},
{
"name": "flashii/rpcii",
"version": "v3.0.0",
"version": "v5.0.1",
"source": {
"type": "git",
"url": "https://patchii.net/flashii/rpcii-php.git",
"reference": "25ac46d5dee60027032e175107e638dfb0b8f7f9"
"reference": "28c25e0a342173524f8894d03158663842e03252"
},
"require": {
"ext-msgpack": ">=2.2",
"flashwave/index": "^0.2410",
"php": ">=8.4"
"flashwave/index": "^0.2503",
"guzzlehttp/guzzle": "~7.0",
"php": ">=8.4",
"psr/http-client": "^1.0"
},
"require-dev": {
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5"
"phpunit/phpunit": "^12.0"
},
"type": "library",
"autoload": {
@ -149,25 +153,27 @@
],
"description": "HTTP RPC client/server library.",
"homepage": "https://railgun.sh/rpcii",
"time": "2025-01-17T00:05:22+00:00"
"time": "2025-03-21T19:38:29+00:00"
},
{
"name": "flashwave/index",
"version": "v0.2410.830205",
"version": "v0.2503.221959",
"source": {
"type": "git",
"url": "https://patchii.net/flash/index.git",
"reference": "416c716b2efab9619d14be02a20cd6540e18a83f"
"reference": "d99562db163589cc51e32e10e851085230fb3181"
},
"require": {
"ext-mbstring": "*",
"php": ">=8.3",
"twig/html-extra": "^3.13",
"twig/twig": "^3.14"
"php": ">=8.4",
"psr/http-message": "^2.0",
"psr/http-server-handler": "^1.0",
"twig/html-extra": "^3.20",
"twig/twig": "^3.20"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^11.4"
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^12.0"
},
"suggest": {
"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.",
"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",
@ -324,16 +539,16 @@
},
{
"name": "jean85/pretty-package-versions",
"version": "2.1.0",
"version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/Jean85/pretty-package-versions.git",
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10"
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
"shasum": ""
},
"require": {
@ -343,8 +558,9 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"jean85/composer-provided-replaced-stub-package": "^1.0",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^7.5|^8.5|^9.6",
"rector/rector": "^2.0",
"vimeo/psalm": "^4.3 || ^5.0"
},
"type": "library",
@ -377,22 +593,22 @@
],
"support": {
"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",
"version": "3.8.4",
"version": "3.8.6",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58"
"reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58",
"reference": "129700ed449b1f02d70272d2ac802357c8c30c58",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd",
"reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd",
"shasum": ""
},
"require": {
@ -468,8 +684,8 @@
],
"support": {
"docs": "https://carbon.nesbot.com/docs",
"issues": "https://github.com/briannesbitt/Carbon/issues",
"source": "https://github.com/briannesbitt/Carbon"
"issues": "https://github.com/CarbonPHP/carbon/issues",
"source": "https://github.com/CarbonPHP/carbon"
},
"funding": [
{
@ -485,7 +701,7 @@
"type": "tidelift"
}
],
"time": "2024-12-27T09:25:35+00:00"
"time": "2025-02-20T17:33:38+00:00"
},
{
"name": "psr/clock",
@ -535,6 +751,58 @@
},
"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",
"version": "1.1.0",
@ -643,6 +911,62 @@
},
"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",
"version": "3.0.2",
@ -1024,16 +1348,16 @@
},
{
"name": "symfony/mime",
"version": "v7.2.1",
"version": "v7.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283"
"reference": "87ca22046b78c3feaff04b337f33b38510fd686b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283",
"reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283",
"url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b",
"reference": "87ca22046b78c3feaff04b337f33b38510fd686b",
"shasum": ""
},
"require": {
@ -1088,7 +1412,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.2.1"
"source": "https://github.com/symfony/mime/tree/v7.2.4"
},
"funding": [
{
@ -1104,7 +1428,7 @@
"type": "tidelift"
}
],
"time": "2024-12-07T08:50:44+00:00"
"time": "2025-02-19T08:51:20+00:00"
},
{
"name": "symfony/options-resolver",
@ -1496,82 +1820,6 @@
],
"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",
"version": "v1.31.0",
@ -1650,16 +1898,16 @@
},
{
"name": "symfony/translation",
"version": "v7.2.2",
"version": "v7.2.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "e2674a30132b7cc4d74540d6c2573aa363f05923"
"reference": "283856e6981286cc0d800b53bd5703e8e363f05a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923",
"reference": "e2674a30132b7cc4d74540d6c2573aa363f05923",
"url": "https://api.github.com/repos/symfony/translation/zipball/283856e6981286cc0d800b53bd5703e8e363f05a",
"reference": "283856e6981286cc0d800b53bd5703e8e363f05a",
"shasum": ""
},
"require": {
@ -1725,7 +1973,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/translation/tree/v7.2.2"
"source": "https://github.com/symfony/translation/tree/v7.2.4"
},
"funding": [
{
@ -1741,7 +1989,7 @@
"type": "tidelift"
}
],
"time": "2024-12-07T08:18:10+00:00"
"time": "2025-02-13T10:27:23+00:00"
},
{
"name": "symfony/translation-contracts",
@ -1823,20 +2071,20 @@
},
{
"name": "twig/html-extra",
"version": "v3.18.0",
"version": "v3.20.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/html-extra.git",
"reference": "c63b28e192c1b7c15bb60f81d2e48b140846239a"
"reference": "f7d54d4de1b64182af745cfb66777f699b599734"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/c63b28e192c1b7c15bb60f81d2e48b140846239a",
"reference": "c63b28e192c1b7c15bb60f81d2e48b140846239a",
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/f7d54d4de1b64182af745cfb66777f699b599734",
"reference": "f7d54d4de1b64182af745cfb66777f699b599734",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"php": ">=8.1.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/mime": "^5.4|^6.4|^7.0",
"twig/twig": "^3.13|^4.0"
@ -1875,7 +2123,7 @@
"twig"
],
"support": {
"source": "https://github.com/twigphp/html-extra/tree/v3.18.0"
"source": "https://github.com/twigphp/html-extra/tree/v3.20.0"
},
"funding": [
{
@ -1887,28 +2135,27 @@
"type": "tidelift"
}
],
"time": "2024-12-29T10:29:59+00:00"
"time": "2025-01-31T20:45:36+00:00"
},
{
"name": "twig/twig",
"version": "v3.18.0",
"version": "v3.20.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50"
"reference": "3468920399451a384bef53cf7996965f7cd40183"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183",
"reference": "3468920399451a384bef53cf7996965f7cd40183",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"php": ">=8.1.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php81": "^1.29"
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
@ -1955,7 +2202,7 @@
],
"support": {
"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": [
{
@ -1967,22 +2214,22 @@
"type": "tidelift"
}
],
"time": "2024-12-29T10:51:50+00:00"
"time": "2025-02-13T08:34:43+00:00"
}
],
"packages-dev": [
{
"name": "phpstan/phpstan",
"version": "2.1.1",
"version": "2.1.8",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7"
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
"reference": "cd6e973e04b4c2b94c86e8612b5a65f0da0e08e7",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
"shasum": ""
},
"require": {
@ -2027,7 +2274,7 @@
"type": "github"
}
],
"time": "2025-01-05T16:43:48+00:00"
"time": "2025-03-09T09:30:48+00:00"
}
],
"aliases": [],

View file

@ -28,7 +28,8 @@ class AuthContext {
public function createClient(Credentials $credentials): FlashiiClient {
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),
));
}

View file

@ -1,17 +1,19 @@
<?php
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 {
use RouteHandlerTrait;
use RouteHandlerCommon;
public function __construct(
private AuthContext $authCtx
) {}
#[HttpMiddleware('/')]
public function getIndex($response, $request) {
#[Preprocessor('eeprom:attempt-auth')]
public function getIndex(HttpResponseBuilder $response, HttpRequest $request) {
$auth = $request->getHeaderLine('Authorization');
if(empty($auth)) {
$cookie = (string)$request->getCookie('msz_auth');
@ -21,7 +23,7 @@ class AuthRoutes implements RouteHandler {
if(!empty($auth)) {
$parts = explode(' ', $auth, 2);
$method = strval($parts[0] ?? '');
$method = $parts[0];
$token = strval($parts[1] ?? '');
$flashii = null;

View file

@ -3,22 +3,23 @@ namespace EEPROM;
use Index\Db\DbConnection;
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 {
use RouteHandlerTrait;
use RouteHandlerCommon;
public function __construct(
public private(set) DbConnection $connection
public private(set) DbConnection $conn
) {}
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;
}
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 {
@ -29,7 +30,7 @@ class DatabaseContext implements RouteHandler {
return sys_get_temp_dir() . '/eeprom-migrate-' . hash('sha256', PRM_ROOT) . '.lock';
}
#[HttpMiddleware('/')]
#[PrefixFilter('/')]
public function middleware() {
if(is_file($this->getMigrateLockPath()))
return 503;

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

View file

@ -1,14 +1,16 @@
<?php
namespace EEPROM;
use EEPROM\Auth\AuthInfoWrapper;
use Index\Dependencies;
use Index\Config\Config;
use Index\Db\DbConnection;
use RPCii\HmacVerificationProvider;
use RPCii\Server\HttpRpcServer;
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) Auth\AuthContext $authCtx;
@ -22,30 +24,26 @@ class EEPROMContext {
private Config $config,
DbConnection $dbConn
) {
$this->database = new DatabaseContext($dbConn);
$this->snowflake = new SnowflakeGenerator;
$this->deps = new Dependencies;
$this->deps->register($this);
$this->deps->register($this->deps);
$this->authCtx = new Auth\AuthContext($config->scopeTo('apii'));
$this->denylistCtx = new Denylist\DenylistContext($dbConn);
$this->poolsCtx = new Pools\PoolsContext($dbConn);
$this->storageCtx = new Storage\StorageContext($config->scopeTo('storage'), $dbConn, $this->snowflake);
$this->tasksCtx = new Tasks\TasksContext($config->scopeTo('domain'), $dbConn, $this->snowflake);
$this->uploadsCtx = new Uploads\UploadsContext(
$config->scopeTo('domain'), $dbConn, $this->snowflake, $this->poolsCtx, $this->storageCtx,
);
$this->deps->register($this->dbCtx = new DatabaseContext($dbConn));
$this->deps->register($this->dbCtx->conn);
$this->deps->register($this->snowflake = $this->deps->constructLazy(SnowflakeGenerator::class));
$this->deps->register($this->authCtx = $this->deps->constructLazy(Auth\AuthContext::class, $config->scopeTo('apii')));
$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 {
$routingCtx = new RoutingContext($this->config->scopeTo('cors'));
$routingCtx->register($this->database);
$routingCtx->register($uploadsViewsRoutes = new Uploads\UploadsViewRoutes(
$this->poolsCtx,
$this->uploadsCtx,
$this->storageCtx,
$this->denylistCtx,
$isApiDomain,
));
$routingCtx->register($this->dbCtx);
if($isApiDomain) {
$rpcServer = new HttpRpcServer;
@ -53,33 +51,16 @@ class EEPROMContext {
new HmacVerificationProvider(fn() => $this->config->getString('rpcii:secret'))
));
$rpcServer->register(new Pools\PoolsRpcHandler(
$this->poolsCtx,
));
$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,
));
$rpcServer->register($this->deps->constructLazy(Pools\PoolsRpcHandler::class));
$rpcServer->register($this->deps->constructLazy(Tasks\TasksRpcHandler::class));
$rpcServer->register($this->deps->constructLazy(Uploads\UploadsRpcHandler::class));
$routingCtx->register(new Auth\AuthRoutes($this->authCtx));
$routingCtx->register(new Tasks\TasksRoutes($this->tasksCtx));
$routingCtx->register(new Uploads\UploadsLegacyRoutes(
$this->authCtx,
$this->poolsCtx,
$this->uploadsCtx,
$this->storageCtx,
$this->denylistCtx,
));
$routingCtx->register(new LandingRoutes($this->database));
}
$routingCtx->register($this->deps->constructLazy(Auth\AuthRoutes::class));
$routingCtx->register($this->deps->constructLazy(Tasks\TasksRoutes::class));
$routingCtx->register($this->deps->constructLazy(Uploads\UploadsLegacyRoutes::class));
$routingCtx->register($this->deps->constructLazy(LandingRoutes::class));
} else
$routingCtx->register($this->deps->constructLazy(Uploads\UploadsViewRoutes::class));
return $routingCtx;
}

View file

@ -1,10 +1,23 @@
<?php
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 {
public function handle(HttpResponseBuilder $response, HttpRequest $request, int $code, string $message): void {
$response->setContent(sprintf('<!doctype html><title>%1$03d %2$s</title><strong>%1$03d %2$s</strong>', $code, $message));
class EEPROMErrorHandler implements ErrorHandler {
public function handle(HandlerContext $context): void {
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
));
}
}

View file

@ -2,22 +2,24 @@
namespace EEPROM;
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 {
use RouteHandlerTrait;
use RouteHandlerCommon;
public function __construct(
private DatabaseContext $dbCtx
) {}
#[HttpGet('/')]
public function getIndex($response) {
#[ExactRoute('GET', '/')]
public function getIndex(HttpResponseBuilder $response) {
$response->accelRedirect('/index.html');
$response->setContentType('text/html; charset=utf-8');
}
#[HttpGet('/stats.json')]
#[ExactRoute('GET', '/stats.json')]
public function getStats() {
$stats = new stdClass;
$stats->uploads = 0;
@ -26,13 +28,13 @@ class LandingRoutes implements RouteHandler {
$stats->types = 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()) {
$stats->uploads = $result->getInteger(0);
$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()) {
$stats->files = $result->getInteger(0);
$stats->size = $result->getInteger(1);
@ -42,8 +44,8 @@ class LandingRoutes implements RouteHandler {
return $stats;
}
#[HttpGet('/eeprom.js')]
public function getEepromJs($response) {
#[ExactRoute('GET', '/eeprom.js')]
public function getEepromJs(HttpResponseBuilder $response) {
$response->accelRedirect('/scripts/eepromv1.js');
$response->setContentType('application/javascript; charset=utf-8');
}

View file

@ -2,7 +2,9 @@
namespace EEPROM\Pools;
use RuntimeException;
use EEPROM\Storage\StorageRecord;
use EEPROM\Uploads\UploadVariantInfo;
use Index\XArray;
use Index\Db\DbConnection;
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);
}
@ -69,7 +71,10 @@ class PoolsContext {
if($removeStaleRule instanceof Rules\RemoveStaleRule && $removeStaleRule->inactiveForSeconds > 0)
$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))
$body['variants'] = $ensureVariantRules;

View file

@ -49,7 +49,6 @@ class PoolsData {
PoolInfoGetField::Id => 'pool_id = ?',
PoolInfoGetField::Name => 'pool_name = ?',
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
@ -90,8 +89,10 @@ class PoolsData {
SELECT rule_id, pool_id, rule_type, rule_params
FROM prm_pools_rules
SQL;
if($hasPoolInfo)
$query .= sprintf(' %s pool_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasPoolInfo) {
++$args;
$query .= ' WHERE pool_id = ?';
}
if($hasTypes)
$query .= sprintf(' %s rule_type IN (%s)', ++$args > 1 ? 'AND' : 'WHERE', DbTools::prepareListString($types));

View file

@ -2,42 +2,19 @@
namespace EEPROM;
use Index\Config\Config;
use Index\Http\HttpRequest;
use Index\Http\Routing\{HttpRouter,Router,RouteHandler};
use Index\Http\{HttpResponseBuilder,HttpRequest};
use Index\Http\Routing\{Router,RouteHandler};
use Index\Http\Routing\Filters\FilterInfo;
class RoutingContext {
private HttpRouter $router;
public private(set) Router $router;
public function __construct(private Config $config) {
$this->router = new HttpRouter(
public function __construct(Config $config) {
$this->router = new Router(
errorHandler: new EEPROMErrorHandler,
accessControlHandler: new EEPROMAccessControlHandler($config->getArray('origins')),
);
$this->router->use('/', $this->middleware(...));
}
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;
$this->router->filter(FilterInfo::prefix('/', fn(HttpResponseBuilder $response) => $response->setPoweredBy('EEPROM')));
}
public function register(RouteHandler $handler): void {

View file

@ -79,18 +79,13 @@ class StorageFiles {
$target = $this->getLocalPath($hash, true);
if(is_file($target)) {
if($mode === StorageImportMode::Move || $mode === StorageImportMode::Upload)
if($mode === StorageImportMode::Move)
unlink($path);
} else {
if($mode === StorageImportMode::Move)
$success = rename($path, $target);
elseif($mode === StorageImportMode::Copy)
$success = copy($path, $target);
elseif($mode === StorageImportMode::Upload)
$success = move_uploaded_file($path, $target);
else
$success = false;
if(!$success)
throw new RuntimeException('unable to move or copy $path using given $mode');
}

View file

@ -4,5 +4,4 @@ namespace EEPROM\Storage;
enum StorageImportMode {
case Copy;
case Move;
case Upload;
}

View file

@ -37,12 +37,13 @@ class StorageRecords {
SQL;
$args = 0;
if($orphaned !== null)
if($orphaned !== null) {
++$args;
$query .= sprintf(
' %s uf.file_id %s NULL',
++$args > 1 ? 'OR' : 'WHERE',
' WHERE uf.file_id %s NULL',
$orphaned ? 'IS' : 'IS NOT'
);
}
if($denied !== null)
$query .= sprintf(
' %s dl.deny_hash %s NULL',
@ -63,7 +64,6 @@ class StorageRecords {
$field = match($field) {
StorageRecordGetFileField::Id => 'file_id',
StorageRecordGetFileField::Hash => 'file_hash',
default => throw new InvalidArgumentException('$field is not an acceptable value'),
};
$stmt = $this->cache->get(<<<SQL
@ -93,14 +93,14 @@ class StorageRecords {
if($size < 0)
throw new InvalidArgumentException('$size may not be negative');
$fileId = $this->snowflake->nextHash($hash);
$fileId = (string)$this->snowflake->nextHash($hash);
$stmt = $this->cache->get(<<<SQL
INSERT INTO prm_files (
file_id, file_hash, file_type, file_size
) VALUES (?, ?, ?, ?)
SQL);
$stmt->nextParameter($fileId); // generate snowflake
$stmt->nextParameter($fileId);
$stmt->nextParameter($hash);
$stmt->nextParameter($type);
$stmt->nextParameter($size);
@ -120,7 +120,6 @@ class StorageRecords {
$field = match($field) {
StorageRecordGetFileField::Id => 'file_id',
StorageRecordGetFileField::Hash => 'file_hash',
default => throw new InvalidArgumentException('$field is not an acceptable value'),
};
$stmt = $this->cache->get(<<<SQL
@ -142,7 +141,6 @@ class StorageRecords {
$field = match($field) {
StorageRecordGetFileField::Id => '?',
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

View file

@ -28,7 +28,7 @@ class TaskInfo {
secret: $result->getString(1),
userId: $result->getString(2),
poolId: $result->getString(3),
state: TaskState::tryFrom($result->getString(4)) ?? TaskState::Ready,
state: TaskState::tryFrom($result->getString(4)) ?? TaskState::Pending,
remoteAddress: $result->getString(5),
name: $result->getString(6),
size: $result->getInteger(7),

View file

@ -3,6 +3,7 @@ namespace EEPROM\Tasks;
use InvalidArgumentException;
use RuntimeException;
use Stringable;
use EEPROM\SnowflakeGenerator;
use EEPROM\Pools\PoolInfo;
use Index\XString;
@ -29,8 +30,10 @@ class TasksData {
task_name, task_size, task_type, task_hash, UNIX_TIMESTAMP(task_created)
FROM prm_tasks
SQL;
if($hasState)
$query .= sprintf(' %s task_state = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasState) {
++$args;
$query .= ' WHERE task_state = ?';
}
$stmt = $this->cache->get($query);
if($hasState)
@ -77,7 +80,7 @@ class TasksData {
string $type,
string $hash
): TaskInfo {
$taskId = $this->snowflake->nextRandom();
$taskId = (string)$this->snowflake->nextRandom();
$stmt = $this->cache->get(<<<SQL
INSERT INTO prm_tasks (

View file

@ -3,39 +3,36 @@ namespace EEPROM\Tasks;
use RuntimeException;
use Index\{ByteFormat,XNumber};
use Index\Http\StringHttpContent;
use Index\Http\Routing\{HttpPut,RouteHandler,RouteHandlerTrait};
use Index\Http\{HttpResponseBuilder,HttpRequest};
use Index\Http\Routing\{HttpPut,RouteHandler,RouteHandlerCommon};
use Index\Http\Routing\Routes\PatternRoute;
class TasksRoutes implements RouteHandler {
use RouteHandlerTrait;
use RouteHandlerCommon;
public function __construct(
private TasksContext $tasksCtx,
) {}
#[HttpPut('/uploads/([A-Za-z0-9]+)(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
public function putUpload($response, $request, string $taskId, string $variant = '', string $extension = '') {
#[PatternRoute('PUT', '/uploads/([A-Za-z0-9]+)(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
public function putUpload(
HttpResponseBuilder $response,
HttpRequest $request,
string $taskId,
string $variant = '',
string $extension = '',
) {
if($request->getHeaderLine('Content-Type') !== 'application/octet-stream') {
$response->setStatusCode(400);
$response->statusCode = 400;
return [
'error' => 'bad_type',
'english' => 'Content-Type must be application/octet-stream.',
];
}
$content = $request->getContent();
if($content instanceof StringHttpContent) {
$content = (string)$content;
} else {
$response->setStatusCode(400);
return [
'error' => 'bad_content',
'english' => 'Request content could not be parsed as expected.',
];
}
$content = (string)$request->getBody();
if(strlen($taskId) < 5) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'not_found',
'english' => 'Requested upload task does not exist.',
@ -43,8 +40,8 @@ class TasksRoutes implements RouteHandler {
}
if($variant !== '') {
$response->setStatusCode(405);
$response->setHeader('Allow', 'HEAD, GET');
$response->statusCode = 405;
$response->setAllow(['HEAD', 'GET']);
return [
'error' => 'method_not_allowed',
'english' => 'PUT requests are not supported for upload variants.',
@ -53,7 +50,7 @@ class TasksRoutes implements RouteHandler {
$length = (int)$request->getHeaderLine('Content-Length');
if($length < 1) {
$response->setStatusCode(411);
$response->statusCode = 411;
return [
'error' => 'length_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) {
$response->setStatusCode(400);
$response->statusCode = 400;
return [
'error' => 'length_mismatch',
'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) {
$response->setStatusCode(413);
$response->statusCode = 413;
return [
'error' => 'content_too_large',
'english' => sprintf(
@ -84,7 +81,7 @@ class TasksRoutes implements RouteHandler {
if($request->hasHeader('X-Content-Index')) {
if($request->hasParam('index')) {
$response->setStatusCode(400);
$response->statusCode = 400;
return [
'error' => 'bad_param',
'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);
} elseif($request->hasParam('index')) {
if($request->hasHeader('X-Content-Index')) {
$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);
$index = (int)$request->getFilteredParam('index', FILTER_SANITIZE_NUMBER_INT);
} else $index = 0;
if($index < 0) {
$response->setStatusCode(400);
$response->statusCode = 400;
return [
'error' => 'bad_index',
'english' => 'index parameter must be greater than or equal to 0.',
@ -113,19 +102,19 @@ class TasksRoutes implements RouteHandler {
}
$taskSecret = substr($taskId, -16);
$taskId = XNumber::fromBase62(substr($taskId, 0, -16));
$taskId = (string)XNumber::fromBase62(substr($taskId, 0, -16));
try {
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
} catch(RuntimeException $ex) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'not_found',
'english' => 'Requested upload task does not exist.',
];
}
if(!$taskInfo->verifySecret($taskSecret)) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'not_found',
'english' => 'Requested upload task does not exist.',
@ -133,7 +122,7 @@ class TasksRoutes implements RouteHandler {
}
if($taskInfo->state !== TaskState::Pending) {
$response->setStatusCode(409);
$response->statusCode = 409;
return [
'error' => 'not_pending',
'english' => 'Requested upload task is no longer accepting data.',
@ -142,7 +131,7 @@ class TasksRoutes implements RouteHandler {
$offset = TasksContext::CHUNK_SIZE * $index;
if($offset >= $taskInfo->size) {
$response->setStatusCode(400);
$response->statusCode = 400;
return [
'error' => 'offset_too_large',
'english' => 'Provided offset is greater than file size.',
@ -152,7 +141,7 @@ class TasksRoutes implements RouteHandler {
$path = $this->tasksCtx->getTaskWorkingPath($taskInfo);
if(!is_file($path)) {
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('working file despawned during PUT request: %s', $path));
$response->setStatusCode(500);
$response->statusCode = 500;
return [
'error' => 'server_error',
'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+');
if($handle === false) {
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to open during PUT request: %s', $path));
$response->setStatusCode(500);
$response->statusCode = 500;
return [
'error' => 'server_error',
'english' => 'Was not able to open working file, the upload process cannot continue.',
];
}
try {
if(fseek($handle, $offset, SEEK_SET) < 0) {
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('was unable to seek during PUT request: %s', $path));
$response->setStatusCode(500);
return [
'error' => 'server_error',
'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.',
];
}
if(fseek($handle, $offset, SEEK_SET) < 0) {
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('was unable to seek during PUT request: %s', $path));
$response->statusCode = 500;
return [
'error' => 'server_error',
'english' => 'Was not able to seek to provided offset, 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 '';
}
}

View file

@ -65,11 +65,8 @@ final class TasksRpcHandler implements RpcHandler {
if($signature === null)
return 'srpp';
if(!str_contains($signature, '=')) {
if(!str_contains($signature, '='))
$signature = UriBase64::decode($signature);
if(!is_string($signature))
$signature = '';
}
$signature = array_column(XArray::select(
explode(';', $signature),
@ -94,7 +91,7 @@ final class TasksRpcHandler implements RpcHandler {
if($sAlgo !== 'S256')
return 'sanv';
if(abs($cTime - $sTime) >= 30)
if(abs($cTime - (int)$sTime) >= 30)
return 'stnv';
if(strlen($sSalt) < 20)
@ -182,15 +179,13 @@ final class TasksRpcHandler implements RpcHandler {
return 'wfcf';
}
try {
if(!ftruncate($data, $fileSize)) {
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to reserve %d bytes for task working file: %s', $fileSize, $path));
return 'wfrf';
}
} finally {
if(!fclose($data))
return 'wfff';
if(!ftruncate($data, $fileSize)) {
$this->tasksCtx->tasks->setTaskError($taskInfo, sprintf('failed to reserve %d bytes for task working file: %s', $fileSize, $path));
return 'wfrf';
}
if(!fclose($data))
return 'wfff';
} catch(RuntimeException $ex) {
return 'utcf';
}
@ -210,7 +205,7 @@ final class TasksRpcHandler implements RpcHandler {
#[RpcQuery('eeprom:tasks:get')]
public function actionTasksGet(string $taskId): string|array {
$taskSecret = substr($taskId, -16);
$taskId = XNumber::fromBase62(substr($taskId, 0, -16));
$taskId = (string)XNumber::fromBase62(substr($taskId, 0, -16));
try {
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);
@ -256,7 +251,7 @@ final class TasksRpcHandler implements RpcHandler {
#[RpcAction('eeprom:tasks:finish')]
public function actionsTasksFinish(string $taskId) {
$taskSecret = substr($taskId, -16);
$taskId = XNumber::fromBase62(substr($taskId, 0, -16));
$taskId = (string)XNumber::fromBase62(substr($taskId, 0, -16));
try {
$taskInfo = $this->tasksCtx->tasks->getTask($taskId);

View file

@ -77,7 +77,7 @@ class UploadsContext {
'hash' => $fileInfo->hashHexString,
'created' => $uploadInfo->createdAt->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
'deleted' => null,

View file

@ -13,7 +13,7 @@ class UploadsData {
private DbStatementCache $cache;
public function __construct(
private DbConnection $dbConn,
DbConnection $dbConn,
private SnowflakeGenerator $snowflake
) {
$this->cache = new DbStatementCache($dbConn);
@ -48,8 +48,10 @@ class UploadsData {
SQL;
$args = 0;
if($hasPoolInfo)
$query .= sprintf(' %s u.pool_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasPoolInfo) {
++$args;
$query .= ' WHERE u.pool_id = ?';
}
if($hasUserId)
$query .= sprintf(' %s u.user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
if($hasVariantExists)

View file

@ -5,12 +5,19 @@ use RuntimeException;
use EEPROM\Auth\AuthContext;
use EEPROM\Denylist\{DenylistContext,DenylistReason};
use EEPROM\Pools\PoolsContext;
use EEPROM\Pools\Rules\{ConstrainSizeRule,EnsureVariantRule,EnsureVariantRuleThumb};
use EEPROM\Storage\{StorageContext,StorageImportMode,StorageRecordGetFileField};
use Index\{ByteFormat,XNumber};
use Index\Http\Routing\{HttpDelete,HttpOptions,HttpPost,RouteHandler,RouteHandlerTrait};
use Index\{ByteFormat,XArray,XNumber};
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 {
use RouteHandlerTrait;
use RouteHandlerCommon;
public function __construct(
private AuthContext $authCtx,
@ -20,21 +27,111 @@ class UploadsLegacyRoutes implements RouteHandler {
private DenylistContext $denylistCtx
) {}
#[HttpOptions('/uploads')]
public function optionsUpload($response, $request): int {
$response->setHeader('Access-Control-Allow-Headers', 'Authorization');
$response->setHeader('Access-Control-Allow-Methods', 'POST');
#[AccessControl]
#[PatternRoute('GET', '/uploads/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
public function getUpload(
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')]
public function postUpload($response, $request) {
if(!$request->isFormContent())
return 400;
#[AccessControl(credentials: true, allowHeaders: ['Authorization'])]
#[Before('input:multipart')]
#[Before('eeprom:attempt-auth')]
#[ExactRoute('POST', '/uploads')]
public function postUpload(HttpResponseBuilder $response, HttpRequest $request, MultipartFormContent $content) {
if(!$this->authCtx->info->authed) {
$response->setStatusCode(401);
$response->statusCode = 401;
return [
'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!",
@ -42,29 +139,25 @@ class UploadsLegacyRoutes implements RouteHandler {
}
if($this->authCtx->info->restricted) {
$response->setStatusCode(403);
$response->statusCode = 403;
return [
'error' => 'restricted',
'english' => 'You are currently banned and are not allowed to upload or manage files.',
];
}
$content = $request->getContent();
try {
$file = $content->getUploadedFile('file');
} catch(RuntimeException $ex) {
$response->setStatusCode(400);
$file = $content->getParamData('file');
if(!($file instanceof FileMultipartFormData)) {
$response->statusCode = 400;
return [
'error' => 'file',
'english' => 'File upload is missing from request body.',
];
}
$localFile = $file->getLocalFileName();
$fileSize = filesize($localFile);
$fileSize = $file->getSize();
if($fileSize < 1) {
$response->setStatusCode(400);
$response->statusCode = 400;
return [
'error' => 'empty',
'english' => 'The file you attempted to upload is empty.',
@ -72,16 +165,16 @@ class UploadsLegacyRoutes implements RouteHandler {
}
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) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'pool_incompatible',
'english' => 'The target upload pool is not compatible with this endpoint.',
];
}
} catch(RuntimeException $ex) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'pool',
'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!
$maxSizeRule = $rules->getRule('constrain_size');
if($maxSizeRule === null) {
$response->setStatusCode(500);
if(!($maxSizeRule instanceof ConstrainSizeRule)) {
$response->statusCode = 500;
return [
'error' => 'size_rule_missing',
'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)
$maxFileSize *= $this->authCtx->info->legacyMultiplier;
if($file->getSize() !== $fileSize || $fileSize > $maxFileSize) {
$response->setStatusCode(413);
if($fileSize > $maxFileSize) {
$response->statusCode = 413;
$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 [
'error' => 'size',
'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);
if($denyInfo !== null) {
$response->setStatusCode(451);
$response->statusCode = 451;
return [
'error' => 'takedown',
'english' => sprintf(
@ -139,17 +234,17 @@ class UploadsLegacyRoutes implements RouteHandler {
];
}
$fileName = $file->getSuggestedFileName();
$mediaType = (string)$file->getSuggestedMediaType();
if($mediaType === '/' || $mediaType === 'application/octet-stream')
$mediaType = mime_content_type($localFile);
$fileName = $file->getClientFilename();
$mediaType = $file->getClientMediaType();
if($mediaType === null || $mediaType === 'application/octet-stream')
$mediaType = mime_content_type($tmpFile);
try {
$storageInfo = $this->storageCtx->records->getFile($hash, StorageRecordGetFileField::Hash);
} catch(RuntimeException $ex) {
$storageInfo = $this->storageCtx->importFile(
$file->getLocalFileName(),
StorageImportMode::Upload,
$tmpFile,
StorageImportMode::Move,
$mediaType
);
}
@ -172,14 +267,14 @@ class UploadsLegacyRoutes implements RouteHandler {
$poolInfo,
$this->authCtx->info->userId,
$fileName,
$request->getRemoteAddress()
$request->remoteAddress
);
$variantInfo = $this->uploadsCtx->uploads->createUploadVariant(
$uploadInfo, '', $storageInfo, $mediaType
);
}
$response->setStatusCode(201);
$response->statusCode = 201;
$response->setHeader('Content-Type', 'application/json; charset=utf-8');
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\-_]+))?')]
public function deleteUpload($response, $request, string $uploadId) {
#[AccessControl(credentials: true, allowHeaders: ['Authorization'])]
#[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) {
$response->setStatusCode(401);
$response->statusCode = 401;
return [
'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!",
@ -198,7 +295,7 @@ class UploadsLegacyRoutes implements RouteHandler {
}
if(strlen($uploadId) < 5) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'none',
'english' => 'That file does not exist.',
@ -216,7 +313,7 @@ class UploadsLegacyRoutes implements RouteHandler {
try {
$uploadInfo = $this->uploadsCtx->uploads->getUpload(uploadId: $uploadId, secret: $uploadSecret);
} catch(RuntimeException $ex) {
$response->setStatusCode(404);
$response->statusCode = 404;
return [
'error' => 'none',
'english' => 'That file does not exist.',
@ -224,7 +321,7 @@ class UploadsLegacyRoutes implements RouteHandler {
}
if($this->authCtx->info->restricted) {
$response->setStatusCode(403);
$response->statusCode = 403;
return [
'error' => 'restricted',
'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) {
$response->setStatusCode(403);
$response->statusCode = 403;
return [
'error' => 'owner',
'english' => "You aren't allowed to delete files uploaded by others.",

View file

@ -71,7 +71,7 @@ final class UploadsRpcHandler implements RpcHandler {
return [
'result' => $this->uploadsCtx->extractUpload(
$this->uploadsCtx->uploads->getUpload(
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
secret: substr($uploadId, -4),
userId: $userId
),
@ -93,7 +93,7 @@ final class UploadsRpcHandler implements RpcHandler {
try {
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
secret: substr($uploadId, -4),
);
} catch(RuntimeException $ex) {
@ -120,7 +120,7 @@ final class UploadsRpcHandler implements RpcHandler {
try {
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
secret: substr($uploadId, -4),
);
} catch(RuntimeException $ex) {
@ -149,7 +149,7 @@ final class UploadsRpcHandler implements RpcHandler {
try {
$uploadInfo = $this->uploadsCtx->uploads->getUpload(
uploadId: XNumber::fromBase62(substr($uploadId, 0, -4)),
uploadId: (string)XNumber::fromBase62(substr($uploadId, 0, -4)),
secret: substr($uploadId, -4),
);
} catch(RuntimeException $ex) {

View file

@ -7,39 +7,30 @@ use EEPROM\Pools\PoolsContext;
use EEPROM\Pools\Rules\{EnsureVariantRule,EnsureVariantRuleThumb};
use EEPROM\Storage\StorageContext;
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 {
use RouteHandlerCommon;
public function __construct(
private PoolsContext $poolsCtx,
private UploadsContext $uploadsCtx,
private StorageContext $storageCtx,
private DenylistContext $denylistCtx,
private bool $isApiDomain
private DenylistContext $denylistCtx
) {}
public function registerRoutes(Router $router): void {
if($this->isApiDomain)
$router = $router->scopeTo('/uploads');
HandlerAttribute::register($router, $this);
}
#[HttpOptions('/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
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 = '') {
#[AccessControl]
#[PatternRoute('GET', '/([A-Za-z0-9]+|[A-Za-z0-9\-_]{32})(?:-([a-z0-9]+))?(?:\.([A-Za-z0-9\-_]+))?')]
public function getUpload(
HttpResponseBuilder $response,
HttpRequest $request,
string $uploadId,
string $variant = '',
string $extension = ''
) {
if(strlen($uploadId) < 5)
return 404;
@ -49,10 +40,6 @@ class UploadsViewRoutes implements RouteHandler {
$variant = (string)$request->getParam('v');
}
$isV1Json = $this->isApiDomain && ($variantQuery || $variant === '') && $extension === 'json';
if($isV1Json)
$variant = '';
if(strlen($uploadId) === 32) {
$uploadId = $this->uploadsCtx->uploads->resolveLegacyId($uploadId) ?? $uploadId;
$uploadSecret = null;
@ -113,9 +100,6 @@ class UploadsViewRoutes implements RouteHandler {
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);

View file

@ -2,20 +2,20 @@
<?php
require_once __DIR__ . '/../eeprom.php';
$lockPath = $eeprom->database->getMigrateLockPath();
$lockPath = $eeprom->dbCtx->getMigrateLockPath();
if(is_file($lockPath))
die('A migration script is already running.' . PHP_EOL);
touch($lockPath);
try {
echo 'Creating migration manager...' . PHP_EOL;
$manager = $eeprom->database->createMigrationManager();
$manager = $eeprom->dbCtx->createMigrationManager();
echo 'Preparing to run migrations...' . PHP_EOL;
$manager->init();
echo 'Creating migration repository...' . PHP_EOL;
$repo = $eeprom->database->createMigrationRepo();
$repo = $eeprom->dbCtx->createMigrationRepo();
echo 'Running migrations...' . PHP_EOL;
$completed = $manager->processMigrations($repo);

View file

@ -2,7 +2,7 @@
<?php
require_once __DIR__ . '/../eeprom.php';
$lockPath = $eeprom->database->getMigrateLockPath();
$lockPath = $eeprom->dbCtx->getMigrateLockPath();
if(is_file($lockPath)) {
printf('Removing migration lock...%s', PHP_EOL);
unlink($lockPath);

View file

@ -4,14 +4,14 @@ use Index\Db\Migration\FsDbMigrationRepo;
require_once __DIR__ . '/../eeprom.php';
$repo = $eeprom->database->createMigrationRepo();
$repo = $eeprom->dbCtx->createMigrationRepo();
if(!($repo instanceof FsDbMigrationRepo)) {
echo 'Migration repository type does not support creation of templates.' . PHP_EOL;
return;
}
$baseName = implode(' ', array_slice($argv, 1));
$manager = $eeprom->database->createMigrationManager();
$manager = $eeprom->dbCtx->createMigrationManager();
try {
$names = $manager->createNames($baseName);