Initial import.

This commit is contained in:
flash 2024-08-25 01:50:52 +00:00
commit adb24dae9f
21 changed files with 1587 additions and 0 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
[Tt]humbs.db
[Dd]esktop.ini
.DS_Store
/.debug
/vendor
/aleister.cfg

30
LICENCE Normal file
View file

@ -0,0 +1,30 @@
Copyright (c) 2024, flashwave <me@flash.moe>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Aleister
Aleister is the API gateway server for Flashii.

32
aleister.php Normal file
View file

@ -0,0 +1,32 @@
<?php
namespace Aleister;
use Syokuhou\FileConfig;
define('CRW_STARTUP', microtime(true));
define('CRW_ROOT', __DIR__);
define('CRW_CLI', PHP_SAPI === 'cli');
define('CRW_DEBUG', is_file(CRW_ROOT . '/.debug'));
require_once CRW_ROOT . '/vendor/autoload.php';
error_reporting(CRW_DEBUG ? -1 : 0);
mb_internal_encoding('UTF-8');
date_default_timezone_set('GMT');
$cfg = FileConfig::fromFile(CRW_ROOT . '/aleister.cfg');
if($cfg->hasValues('sentry:dsn'))
(function($cfg) {
\Sentry\init([
'dsn' => $cfg->getString('dsn'),
'traces_sample_rate' => $cfg->getFloat('tracesRate', 0.2),
'profiles_sample_rate' => $cfg->getFloat('profilesRate', 0.2),
]);
set_exception_handler(function(\Throwable $ex) {
\Sentry\captureException($ex);
});
})($cfg->scopeTo('sentry'));
$ctx = new AleisterContext($cfg);

16
composer.json Normal file
View file

@ -0,0 +1,16 @@
{
"autoload": {
"psr-4": {
"Aleister\\": "src"
}
},
"require": {
"flashwave/index": "^0.2408.40014",
"flashwave/syokuhou": "^1.2",
"flashwave/aiwass": "^1.0",
"sentry/sdk": "^4.0"
},
"require-dev": {
"phpstan/phpstan": "^1.11"
}
}

852
composer.lock generated Normal file
View file

@ -0,0 +1,852 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f16579c0b9bf3a7b6091a2eec61e2354",
"packages": [
{
"name": "flashwave/aiwass",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://patchii.net/flashii/aiwass.git",
"reference": "de27da54b603f0fdc06dab89341908e73ea7a880"
},
"require": {
"ext-msgpack": ">=2.2",
"flashwave/index": "^0.2408.40014",
"php": ">=8.3"
},
"require-dev": {
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^11.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Aiwass\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"bsd-3-clause-clear"
],
"authors": [
{
"name": "flashwave",
"email": "packagist@flash.moe",
"homepage": "https://flash.moe",
"role": "mom"
}
],
"description": "Shared HTTP RPC client/server library.",
"homepage": "https://railgun.sh/aiwass",
"time": "2024-08-16T15:59:19+00:00"
},
{
"name": "flashwave/index",
"version": "v0.2408.182001",
"source": {
"type": "git",
"url": "https://patchii.net/flash/index.git",
"reference": "5a1fdcccedf818897a3468d5457875fabfb2ce28"
},
"require": {
"ext-mbstring": "*",
"php": ">=8.3"
},
"require-dev": {
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^11.2"
},
"suggest": {
"ext-mysqli": "Support for the Index\\Data\\MariaDB namespace (both mysqlnd and libmysql are supported).",
"ext-sqlite3": "Support for the Index\\Data\\SQLite namespace."
},
"type": "library",
"autoload": {
"psr-4": {
"Index\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"bsd-3-clause-clear"
],
"authors": [
{
"name": "flashwave",
"email": "packagist@flash.moe",
"homepage": "https://flash.moe",
"role": "mom"
}
],
"description": "Composer package for the common library for my projects.",
"homepage": "https://railgun.sh/index",
"time": "2024-08-18T20:01:21+00:00"
},
{
"name": "flashwave/syokuhou",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://patchii.net/flash/syokuhou.git",
"reference": "129a46c0d917382f9bc195cce278be51984eb87d"
},
"require": {
"flashwave/index": "^0.2408.40014",
"php": ">=8.3"
},
"require-dev": {
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^11.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Syokuhou\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"bsd-3-clause-clear"
],
"authors": [
{
"name": "flashwave",
"email": "packagist@flash.moe",
"homepage": "https://flash.moe",
"role": "mom"
}
],
"description": "Configuration library for PHP.",
"homepage": "https://railgun.sh/syokuhou",
"time": "2024-08-04T01:07:23+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "1.0",
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "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": "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"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.7.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
"type": "tidelift"
}
],
"time": "2024-07-18T11:15:46+00:00"
},
{
"name": "jean85/pretty-package-versions",
"version": "2.0.6",
"source": {
"type": "git",
"url": "https://github.com/Jean85/pretty-package-versions.git",
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4",
"reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0.0",
"php": "^7.1|^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"jean85/composer-provided-replaced-stub-package": "^1.0",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^7.5|^8.5|^9.4",
"vimeo/psalm": "^4.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Jean85\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alessandro Lai",
"email": "alessandro.lai85@gmail.com"
}
],
"description": "A library to get pretty versions strings of installed dependencies",
"keywords": [
"composer",
"package",
"release",
"versions"
],
"support": {
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6"
},
"time": "2024-03-08T09:58:59+00:00"
},
{
"name": "psr/http-factory",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
"php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"message",
"psr",
"psr-17",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory"
},
"time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/2.0"
},
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "psr/log",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
},
"time": "2021-07-14T16:46:02+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"support": {
"issues": "https://github.com/ralouphie/getallheaders/issues",
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "sentry/sdk",
"version": "4.0.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-php-sdk.git",
"reference": "fcbca864e8d1dc712f3ecfaa95ea89d024fb2e53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/fcbca864e8d1dc712f3ecfaa95ea89d024fb2e53",
"reference": "fcbca864e8d1dc712f3ecfaa95ea89d024fb2e53",
"shasum": ""
},
"require": {
"sentry/sentry": "^4.0"
},
"type": "metapackage",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sentry",
"email": "accounts@sentry.io"
}
],
"description": "This is a meta package of sentry/sentry. We recommend using sentry/sentry directly.",
"homepage": "http://sentry.io",
"keywords": [
"crash-reporting",
"crash-reports",
"error-handler",
"error-monitoring",
"log",
"logging",
"sentry"
],
"support": {
"issues": "https://github.com/getsentry/sentry-php-sdk/issues",
"source": "https://github.com/getsentry/sentry-php-sdk/tree/4.0.0"
},
"funding": [
{
"url": "https://sentry.io/",
"type": "custom"
},
{
"url": "https://sentry.io/pricing/",
"type": "custom"
}
],
"time": "2023-11-06T10:23:19+00:00"
},
{
"name": "sentry/sentry",
"version": "4.9.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-php.git",
"reference": "788ec170f51ebb22f2809a1e3f78b19ccd39b70d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/788ec170f51ebb22f2809a1e3f78b19ccd39b70d",
"reference": "788ec170f51ebb22f2809a1e3f78b19ccd39b70d",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
"jean85/pretty-package-versions": "^1.5|^2.0.4",
"php": "^7.2|^8.0",
"psr/log": "^1.0|^2.0|^3.0",
"symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0"
},
"conflict": {
"raven/raven": "*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.4",
"guzzlehttp/promises": "^1.0|^2.0",
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
"monolog/monolog": "^1.6|^2.0|^3.0",
"phpbench/phpbench": "^1.0",
"phpstan/phpstan": "^1.3",
"phpunit/phpunit": "^8.5.14|^9.4",
"symfony/phpunit-bridge": "^5.2|^6.0|^7.0",
"vimeo/psalm": "^4.17"
},
"suggest": {
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
},
"type": "library",
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Sentry\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sentry",
"email": "accounts@sentry.io"
}
],
"description": "PHP SDK for Sentry (http://sentry.io)",
"homepage": "http://sentry.io",
"keywords": [
"crash-reporting",
"crash-reports",
"error-handler",
"error-monitoring",
"log",
"logging",
"profiling",
"sentry",
"tracing"
],
"support": {
"issues": "https://github.com/getsentry/sentry-php/issues",
"source": "https://github.com/getsentry/sentry-php/tree/4.9.0"
},
"funding": [
{
"url": "https://sentry.io/",
"type": "custom"
},
{
"url": "https://sentry.io/pricing/",
"type": "custom"
}
],
"time": "2024-08-08T14:40:50+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"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": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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-04-18T09:32:20+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55",
"reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides an improved replacement for the array_replace PHP function",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.1.1"
},
"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-05-31T14:57:53+00:00"
}
],
"packages-dev": [
{
"name": "phpstan/phpstan",
"version": "1.11.10",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "640410b32995914bde3eed26fa89552f9c2c082f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f",
"reference": "640410b32995914bde3eed26fa89552f9c2c082f",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2024-08-08T09:02:50+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

9
phpstan.neon Normal file
View file

@ -0,0 +1,9 @@
parameters:
level: 5
paths:
- src
bootstrapFiles:
- aleister.php
dynamicConstantNames:
- CRW_CLI
- CRW_DEBUG

21
public/index.php Normal file
View file

@ -0,0 +1,21 @@
<?php
namespace Aleister;
require_once __DIR__ . '/../aleister.php';
set_exception_handler(function(\Throwable $ex) {
\Sentry\captureException($ex);
ob_clean();
http_response_code(500);
header('Content-Type: text/plain; charset=utf-8');
if(CRW_DEBUG)
echo (string)$ex;
else
echo 'Internal Server Error';
exit;
});
$ctx->getRouter()->dispatch();

2
public/robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View file

@ -0,0 +1,58 @@
<?php
namespace Aleister;
use JsonSerializable;
use Index\Bencode\IBencodeSerialisable;
use Index\Http\HttpResponseBuilder;
use Index\Http\ContentHandling\IContentHandler;
class AleisterContentHandler implements IContentHandler {
public function __construct(
private HttpAcceptHeader $accept
) {}
public function hasAcceptableType(): bool {
return $this->accept->getMostPreferred([
'application/json',
'text/html', // pretty printed JSON
]) !== null;
}
public function match(mixed $content): bool {
return true;
}
public function handle(HttpResponseBuilder $response, mixed $content): void {
// mostly for debug and info pages, so we'll ignore the accept header for strings
if(is_string($content)) {
if(stripos($content, '<!doctype ') === 0)
$response->setTypeHTML('utf-8');
else
$response->setTypePlain('utf-8');
$response->setContent($content);
return;
}
if($content instanceof JsonSerializable)
$content = $content->jsonSerialize();
elseif($content instanceof IBencodeSerialisable)
$content = $content->bencodeSerialise();
// default to JSON
if(!$response->hasContentType())
$response->setTypeJson();
$flags = JSON_UNESCAPED_UNICODE
| JSON_UNESCAPED_SLASHES
| JSON_THROW_ON_ERROR
| JSON_PRESERVE_ZERO_FRACTION
| JSON_INVALID_UTF8_SUBSTITUTE;
// pretty print if text/html is acceptable, someone is likely poking using a browser
if($this->accept->isAcceptable('text/html'))
$flags |= JSON_PRETTY_PRINT;
$response->setContent(json_encode($content, $flags));
}
}

92
src/AleisterContext.php Normal file
View file

@ -0,0 +1,92 @@
<?php
namespace Aleister;
use RuntimeException;
use Aleister\RpcModels\Hanyuu\OAuth2AuthInfo;
use Index\Http\Routing\{HttpRouter,IRouter,IRouteHandler};
use Syokuhou\IConfig;
class AleisterContext {
private RpcClientWrapper $rpcWrapper;
private HttpRouter $router;
private OAuth2AuthInfo $authInfo;
public function __construct(
private IConfig $config
) {
$this->authInfo = new OAuth2AuthInfo(['error' => 'none']);
$this->rpcWrapper = new RpcClientWrapper;
$this->rpcWrapper->createHmacConfig($config, 'hanyuu');
$this->router = new HttpRouter(
errorHandler: 'plain',
registerDefaultContentHandlers: false
);
$this->router->use('/', fn($resp) => $resp->setPoweredBy('Flashii'));
$this->router->use('/', $this->handleAcceptHeader(...));
$this->router->use('/', $this->handleAuthzHeader(...));
$this->router->get('/', fn() => '<!doctype html>Hello! Someday this page will probably redirect to documentation, but none exists yet.');
$this->router->scopeTo('/oauth2')->register(new OAuth2\OAuth2Routes($this));
$this->router->scopeTo('/v1')->register(new V1\V1Routes(new V1\V1Context($this)));
}
public function getRpcClient(): RpcClientWrapper {
return $this->rpcWrapper;
}
public function getRouter(): IRouter {
return $this->router;
}
public function getAuthInfo(): OAuth2AuthInfo {
return $this->authInfo;
}
public function handleAcceptHeader($response, $request) {
$accept = HttpAcceptHeader::parse($request->getHeaderLine('Accept'));
$contentHandler = new AleisterContentHandler($accept);
if(!$contentHandler->hasAcceptableType())
return 406;
$this->router->setErrorHandler(new AleisterErrorHandler($contentHandler));
$this->router->registerContentHandler($contentHandler);
}
public function handleAuthzHeader($response, $request): void {
$authInfo = null;
$authzHeader = explode(' ', (string)$request->getHeaderLine('Authorization'), 2);
if(count($authzHeader) > 1) {
$authzMethod = array_shift($authzHeader);
$authzInfo = array_shift($authzHeader);
if(strcasecmp($authzMethod, 'basic') === 0) {
$authzInfo = base64_decode($authzInfo);
if($authzInfo !== false) {
$authzInfo = explode(':', $authzInfo, 2);
if(count($authzInfo) > 0)
try {
$authInfo = $this->rpcWrapper->procedure(
'hanyuu:oauth2:attemptAppAuth',
['clientId' => array_shift($authzInfo), 'clientSecret' => array_shift($authzInfo) ?? '']
);
} catch(RuntimeException $ex) {}
}
} elseif(strcasecmp($authzMethod, 'bearer') === 0) {
try {
$authInfo = $this->rpcWrapper->procedure(
'hanyuu:oauth2:getTokenInfo',
['type' => 'Bearer', 'token' => $authzInfo]
);
} catch(RuntimeException $ex) {}
}
}
if($authInfo !== null)
$this->authInfo = new OAuth2AuthInfo($authInfo);
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Aleister;
use Index\Http\{HttpResponseBuilder,HttpRequest};
use Index\Http\ErrorHandling\IErrorHandler;
class AleisterErrorHandler implements IErrorHandler {
public function __construct(
private AleisterContentHandler $contentHandler,
) {}
public function handle(HttpResponseBuilder $response, HttpRequest $request, int $code, string $message): void {
$this->contentHandler->handle($response, [
'code' => sprintf('http:%s', $code),
'message' => $message,
]);
}
}

59
src/HttpAcceptHeader.php Normal file
View file

@ -0,0 +1,59 @@
<?php
namespace Aleister;
use Index\{MediaType,XString};
class HttpAcceptHeader {
public function __construct(
private array $accept,
) {
usort($this->accept, fn($a, $b) => $b->getQuality() <=> $a->getQuality());
}
public function isAcceptable(string $option, bool $strict = false): bool {
if(empty($this->accept))
return true;
foreach($this->accept as $type)
if($type->equals($option) && $type->getQuality() > 0
&& (!$strict || ($type->getCategory() !== '*' && $type->getKind() !== '*')))
return true;
return false;
}
public function getMostPreferred(array $options = []): ?string {
if(empty($options))
return null;
if(empty($this->accept))
return $options[array_key_first($options)];
$prefer = null;
$weight = 0;
foreach($options as $option)
foreach($this->accept as $type) {
$quality = $type->getQuality();
if($quality <= 0 || $quality <= $weight || !$type->equals($option))
continue;
$prefer = $option;
$weight = $quality;
}
return $prefer;
}
public static function parse(string $header): HttpAcceptHeader {
$accept = [];
$types = explode(',', $header);
foreach($types as $type) {
$type = trim($type);
if($type !== '')
$accept[] = MediaType::parse($type);
}
return new HttpAcceptHeader($accept);
}
}

190
src/OAuth2/OAuth2Routes.php Normal file
View file

@ -0,0 +1,190 @@
<?php
namespace Aleister\OAuth2;
use RuntimeException;
use Aleister\AleisterContext;
use Aleister\RpcModels\Hanyuu\{OAuth2AuthInfo,OAuth2RfcModel};
use Index\Http\Routing\{HttpGet,HttpOptions,HttpPost,RouteHandler};
class OAuth2Routes extends RouteHandler {
public const REQ_AUTH_ERRORS = [
'invalid_request', 'invalid_client', 'invalid_grant', 'unauthorized_client',
'unsupported_grant_type', 'invalid_scope', 'server_error', 'temporarily_unavailable',
];
public function __construct(
private AleisterContext $ctx
) {}
private static function filter($response, array $result, bool $authzHeader = false): array {
if(array_key_exists('error', $result)) {
if($authzHeader) {
$wwwAuth = sprintf('Basic realm="%s"', $_SERVER['HTTP_HOST']);
foreach($result as $name => $value)
$wwwAuth .= sprintf(', %s="%s"', $name, rawurlencode($value));
$response->setStatusCode(401);
$response->setHeader('WWW-Authenticate', $wwwAuth);
} else
$response->setStatusCode(400);
}
return $result;
}
#[HttpPost('/request-authorise')]
public function postRequestAuthorise($response, $request) {
$response->setHeader('Cache-Control', 'no-store');
$authInfo = $this->ctx->getAuthInfo();
if($authInfo->getError() === 'secret')
return self::filter($response, [
'error' => 'invalid_client',
'error_description' => 'Provided client secret is not correct for this application.',
], authzHeader: true);
if(!$request->isFormContent())
return self::filter($response, [
'error' => 'invalid_request',
'error_description' => 'Your request must use content type application/x-www-form-urlencoded.',
]);
$rpc = $this->ctx->getRpcClient();
$content = $request->getContent();
if($authInfo->getMethod() === '')
try {
$authInfo = new OAuth2AuthInfo($rpc->procedure('hanyuu:oauth2:attemptAppAuth', [
'clientId' => (string)$content->getParam('client_id'),
]));
} catch(RuntimeException $ex) {}
if(!$authInfo->isAppUser())
return self::filter($response, [
'error' => 'invalid_client',
'error_description' => 'App authentication failed.',
]);
try {
$reqInfo = new OAuth2RfcModel($rpc->procedure('hanyuu:oauth2:createAuthoriseRequest', [
'appId' => $authInfo->getAppId(),
'scope' => (string)$content->getParam('scope'),
]));
} catch(RuntimeException $ex) {}
if($reqInfo === null || ($reqInfo->hasError() && !in_array($reqInfo->getError(), self::REQ_AUTH_ERRORS)))
return self::filter($response, [
'error' => 'server_error',
'error_description' => 'Authorisation server gave unexpected response.',
]);
return self::filter($response, $reqInfo->getRaw());
}
#[HttpOptions('/token')]
#[HttpPost('/token')]
public function postToken($response, $request) {
$response->setHeader('Cache-Control', 'no-store');
$originHeaders = ['Origin', 'X-Origin', 'Referer'];
$origins = [];
foreach($originHeaders as $originHeader) {
$originHeader = $request->getHeaderFirstLine($originHeader);
if($originHeader !== '' && !in_array($originHeader, $origins))
$origins[] = $originHeader;
}
if(!empty($origins)) {
// TODO: check if none of the provided origins is on a blocklist or something
// different origins being specified for each header should probably also be considered suspect...
$response->setHeader('Access-Control-Allow-Origin', $origins[0]);
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, POST');
$response->setHeader('Access-Control-Allow-Headers', 'Authorization');
$response->setHeader('Access-Control-Expose-Headers', 'Vary');
foreach($originHeaders as $originHeader)
$response->setHeader('Vary', $originHeader);
}
if($request->getMethod() === 'OPTIONS')
return 204;
if(!$request->isFormContent())
return self::filter($response, [
'error' => 'invalid_request',
'error_description' => 'Your request must use content type application/x-www-form-urlencoded.',
]);
$rpc = $this->ctx->getRpcClient();
$content = $request->getContent();
$authInfo = $this->ctx->getAuthInfo();
if($authInfo->getMethod() === '')
try {
$authInfo = new OAuth2AuthInfo($rpc->procedure('hanyuu:oauth2:attemptAppAuth', [
'clientId' => (string)$content->getParam('client_id'),
'clientSecret' => (string)$content->getParam('client_secret')
]));
} catch(RuntimeException $ex) {
$authInfo = null;
}
if($authInfo === null || !$authInfo->isAppUser())
return self::filter($response, [
'error' => 'invalid_client',
'error_description' => 'App authentication failed.',
]);
if($authInfo->getError() === 'secret')
return self::filter($response, [
'error' => 'invalid_client',
'error_description' => 'Provided client secret is not correct for this application.'
], authzHeader: true);
try {
$name = '';
$args = ['appId' => $authInfo->getAppId(), 'isAuthed' => $authInfo->isAuthed()];
switch($content->getParam('grant_type')) {
case 'authorization_code':
$name = 'authorisationCode';
$args['code'] = (string)$content->getParam('code');
$args['codeVerifier'] = (string)$content->getParam('code_verifier');
break;
case 'refresh_token':
$name = 'refreshToken';
$args['refreshToken'] = (string)$content->getParam('refresh_token');
if($content->hasParam('scope'))
$args['scope'] = (string)$content->getParam('scope');
break;
case 'client_credentials':
$name = 'clientCredentials';
if($content->hasParam('scope'))
$args['scope'] = (string)$content->getParam('scope');
break;
case 'urn:ietf:params:oauth:grant-type:device_code':
case 'device_code':
$name = 'deviceCode';
$args['deviceCode'] = (string)$content->getParam('device_code');
break;
}
if($name === '')
return self::filter($response, [
'error' => 'unsupported_grant_type',
'error_description' => 'Requested grant type is not supported by this server.',
]);
return self::filter($response, $rpc->procedure('hanyuu:oauth2:createBearerToken:' . $name, $args));
} catch(RuntimeException $ex) {
return self::filter($response, [
'error' => 'server_error',
'error_description' => 'Authorisation server gave unexpected response.',
]);
}
}
}

47
src/RpcClientWrapper.php Normal file
View file

@ -0,0 +1,47 @@
<?php
namespace Aleister;
use RuntimeException;
use Aiwass\Client\{IRpcClient,RpcClient,RpcClientScoped};
use Syokuhou\IConfig;
class RpcClientWrapper implements IRpcClient {
private array $clients = [];
public function createHmacConfig(IConfig $config, string $prefix): void {
$config = $config->scopeTo($prefix);
$this->createHmac(
$prefix . ':',
$config->getString('endpoint'),
fn() => $config->getString('secret')
);
}
public function createHmac(string $prefix, string $url, $getSecretKey): void {
$this->register($prefix, RpcClient::createHmac($url, $getSecretKey));
}
public function register(string $prefix, IRpcClient $client): void {
$this->clients[$prefix] = $client;
}
public function scopeTo(string $prefix): IRpcClient {
return new RpcClientScoped($this, $prefix);
}
public function query(string $action, array $params): mixed {
foreach($this->clients as $prefix => $client)
if(str_starts_with($action, $prefix))
return $client->query($action, $params);
throw new RuntimeException(sprintf('Unable to handle this query: %s', $action));
}
public function procedure(string $action, array $params): mixed {
foreach($this->clients as $prefix => $client)
if(str_starts_with($action, $prefix))
return $client->procedure($action, $params);
throw new RuntimeException(sprintf('Unable to handle this procedure: %s', $action));
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Aleister\RpcModels\Hanyuu;
use Aleister\RpcModels\RpcModel;
class OAuth2AuthInfo extends RpcModel {
public function getMethod(): string {
return $this->getString('method');
}
public function isAuthed(): bool {
return $this->getBoolean('authed');
}
public function getAppId(): string {
return $this->getString('app_id');
}
public function hasUserId(): bool {
return $this->hasValue('user_id');
}
public function isAppUser(): bool {
return $this->getMethod() === 'basic' || $this->getUserId() === '0';
}
public function getUserId(): string {
return $this->getString('user_id');
}
public function getScope(): string {
return $this->getString('scope');
}
public function getExpiresIn(): int {
return $this->getInteger('expires_in');
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Aleister\RpcModels\Hanyuu;
use Aleister\RpcModels\RpcModel;
class OAuth2RfcModel extends RpcModel {
public function hasErrorDescription(): bool {
return $this->hasValue('error_description');
}
public function getErrorDescription(): string {
return $this->getString('error_description');
}
public function hasErrorUri(): bool {
return $this->hasValue('error_uri');
}
public function getErrorUri(): string {
return $this->getString('error_uri');
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Aleister\RpcModels;
abstract class RpcModel {
protected array $info;
public function __construct(array $info) {
$this->info = $info;
}
public function hasError(): bool {
return $this->hasValue('error');
}
public function getError(): string {
return $this->getString('error');
}
public function getRaw(): array {
return $this->info;
}
protected function hasValue(string $name): bool {
return array_key_exists($name, $this->info);
}
protected function getValue(string $name, mixed $fallback = null): mixed {
return array_key_exists($name, $this->info) ? $this->info[$name] : $fallback;
}
protected function getArray(string $name): array {
return array_key_exists($name, $this->info) && is_array($this->info[$name])
? $this->info[$name] : [];
}
protected function getBoolean(string $name): bool {
return array_key_exists($name, $this->info) && is_bool($this->info[$name])
&& $this->info[$name];
}
protected function getInteger(string $name): int {
return array_key_exists($name, $this->info) && is_int($this->info[$name])
? $this->info[$name] : 0;
}
protected function getFloat(string $name): float {
return array_key_exists($name, $this->info) && is_float($this->info[$name])
? $this->info[$name] : 0.0;
}
protected function getString(string $name): string {
return array_key_exists($name, $this->info) && is_string($this->info[$name])
? $this->info[$name] : '';
}
}

20
src/V1/V1Context.php Normal file
View file

@ -0,0 +1,20 @@
<?php
namespace Aleister\V1;
use Aleister\AleisterContext;
use Aleister\RpcModels\Hanyuu\OAuth2AuthInfo;
use Aiwass\Client\IRpcClient;
class V1Context {
public function __construct(
private AleisterContext $ctx
) {}
public function getRpcClient(): IRpcClient {
return $this->ctx->getRpcClient();
}
public function getAuthInfo(): OAuth2AuthInfo {
return $this->ctx->getAuthInfo();
}
}

16
src/V1/V1Routes.php Normal file
View file

@ -0,0 +1,16 @@
<?php
namespace Aleister\V1;
use Aleister\RpcModels\Hanyuu\OAuth2AuthInfo;
use Aiwass\Client\IRpcClient;
use Index\Http\Routing\{IRouter,IRouteHandler};
class V1Routes implements IRouteHandler {
public function __construct(
private V1Context $ctx
) {}
public function registerRoutes(IRouter $router): void {
$router->get('/', fn() => ['status' => 'operational']);
}
}