Compare commits

..

2 commits

Author SHA1 Message Date
ef5535cb39 Added skin route for Minecraft Beta 1.7.3. 2024-02-21 16:32:21 +00:00
37c3764c41 Index framework updates. 2024-02-21 16:08:45 +00:00
21 changed files with 249 additions and 247 deletions

130
composer.lock generated
View file

@ -67,7 +67,7 @@
"source": {
"type": "git",
"url": "https://patchii.net/flash/index.git",
"reference": "e31781c69f0b13fe251771c8e7e529222630a44f"
"reference": "73051dc71ee2d0045e5dbe5d846bb665a8b1c39c"
},
"require": {
"ext-mbstring": "*",
@ -105,7 +105,7 @@
],
"description": "Composer package for the common library for my projects.",
"homepage": "https://railgun.sh/index",
"time": "2023-11-20T19:01:19+00:00"
"time": "2024-02-06T23:52:46+00:00"
},
{
"name": "flashwave/sasae",
@ -113,7 +113,7 @@
"source": {
"type": "git",
"url": "https://patchii.net/flash/sasae.git",
"reference": "b56dd222acb8f138729e6258d4a90bbb8401ff52"
"reference": "c8a9f2974e6591215b3f898dd5525de1e8367f66"
},
"require": {
"flashwave/index": "dev-master",
@ -146,7 +146,7 @@
],
"description": "A wrapper for Twig with added common functionality.",
"homepage": "https://railgun.sh/sasae",
"time": "2023-11-20T19:09:35+00:00"
"time": "2024-01-04T02:13:42+00:00"
},
{
"name": "flashwave/syokuhou",
@ -154,7 +154,7 @@
"source": {
"type": "git",
"url": "https://patchii.net/flash/syokuhou.git",
"reference": "fdf3c38cc216bf7024af331cbe1758532355c22f"
"reference": "c1fe9371ada20fcea51c225cc53b9ceae4642bc4"
},
"require": {
"flashwave/index": "dev-master",
@ -185,7 +185,7 @@
],
"description": "Configuration library for PHP.",
"homepage": "https://railgun.sh/syokuhou",
"time": "2023-11-20T19:10:04+00:00"
"time": "2024-01-04T02:12:49+00:00"
},
{
"name": "guzzlehttp/psr7",
@ -802,16 +802,16 @@
},
{
"name": "sentry/sentry",
"version": "4.1.0",
"version": "4.6.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-php.git",
"reference": "89666f297891ff937fceb2f3d1fb967a6848cf37"
"reference": "30d98a460ab10f7b7032d76c62da5b1ce6c0765d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/89666f297891ff937fceb2f3d1fb967a6848cf37",
"reference": "89666f297891ff937fceb2f3d1fb967a6848cf37",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/30d98a460ab10f7b7032d76c62da5b1ce6c0765d",
"reference": "30d98a460ab10f7b7032d76c62da5b1ce6c0765d",
"shasum": ""
},
"require": {
@ -835,7 +835,7 @@
"phpbench/phpbench": "^1.0",
"phpstan/phpstan": "^1.3",
"phpunit/phpunit": "^8.5.14|^9.4",
"symfony/phpunit-bridge": "^5.2|^6.0",
"symfony/phpunit-bridge": "^5.2|^6.0|^7.0",
"vimeo/psalm": "^4.17"
},
"suggest": {
@ -875,7 +875,7 @@
],
"support": {
"issues": "https://github.com/getsentry/sentry-php/issues",
"source": "https://github.com/getsentry/sentry-php/tree/4.1.0"
"source": "https://github.com/getsentry/sentry-php/tree/4.6.0"
},
"funding": [
{
@ -887,7 +887,7 @@
"type": "custom"
}
],
"time": "2023-12-04T12:41:21+00:00"
"time": "2024-02-13T11:32:56+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -958,16 +958,16 @@
},
{
"name": "symfony/mime",
"version": "v7.0.0",
"version": "v7.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "0a2fff95c1a10df97f571d67e76c7ae0f0d4f535"
"reference": "c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/0a2fff95c1a10df97f571d67e76c7ae0f0d4f535",
"reference": "0a2fff95c1a10df97f571d67e76c7ae0f0d4f535",
"url": "https://api.github.com/repos/symfony/mime/zipball/c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716",
"reference": "c1ffe24ba6fdc3e3f0f3fcb93519103b326a3716",
"shasum": ""
},
"require": {
@ -1021,7 +1021,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.0.0"
"source": "https://github.com/symfony/mime/tree/v7.0.3"
},
"funding": [
{
@ -1037,7 +1037,7 @@
"type": "tidelift"
}
],
"time": "2023-10-19T14:20:43+00:00"
"time": "2024-01-30T08:34:29+00:00"
},
{
"name": "symfony/options-resolver",
@ -1108,16 +1108,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"shasum": ""
},
"require": {
@ -1131,9 +1131,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1170,7 +1167,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
},
"funding": [
{
@ -1186,20 +1183,20 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "ecaafce9f77234a6a449d29e49267ba10499116d"
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d",
"reference": "ecaafce9f77234a6a449d29e49267ba10499116d",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919",
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919",
"shasum": ""
},
"require": {
@ -1212,9 +1209,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1257,7 +1251,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0"
},
"funding": [
{
@ -1273,20 +1267,20 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:30:37+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
"shasum": ""
},
"require": {
@ -1297,9 +1291,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1341,7 +1332,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
},
"funding": [
{
@ -1357,20 +1348,20 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "42292d99c55abe617799667f454222c54c60e229"
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
@ -1384,9 +1375,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1424,7 +1412,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@ -1440,20 +1428,20 @@
"type": "tidelift"
}
],
"time": "2023-07-28T09:04:16+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25",
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25",
"shasum": ""
},
"require": {
@ -1461,9 +1449,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1500,7 +1485,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0"
},
"funding": [
{
@ -1516,20 +1501,20 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.28.0",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
@ -1537,9 +1522,6 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@ -1583,7 +1565,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
},
"funding": [
{
@ -1599,7 +1581,7 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "twig/html-extra",
@ -1749,5 +1731,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View file

@ -50,10 +50,10 @@ $router->setDefaultErrorHandler(function($response, $request, $code, $text) use
]));
});
(new RpcRoutes($users, $accountLinks, $authorisations, $verifications, $cfg->getString('rpc:secret'), $cfg->getString('urls:clients')))->register($router);
(new HomeRoutes($templating, new Servers($db), $authInfo, $cfg->getString('site:login')))->register($router);
(new ClientsRoutes($templating, $accountLinks, $authorisations, $verifications, $csrfp, $authInfo))->register($router);
(new SkinsRoutes($templating, $accountLinks, new Skins($db), new Capes($db), $csrfp, $authInfo, $cfg->getString('urls:skins_base')))->register($router);
$router->register(new RpcRoutes($users, $accountLinks, $authorisations, $verifications, $cfg->getString('rpc:secret'), $cfg->getString('urls:clients')));
$router->register(new HomeRoutes($templating, new Servers($db), $authInfo, $cfg->getString('site:login')));
$router->register(new ClientsRoutes($templating, $accountLinks, $authorisations, $verifications, $csrfp, $authInfo));
$router->register(new SkinsRoutes($templating, $accountLinks, new Skins($db), new Capes($db), $csrfp, $authInfo, $cfg->getString('urls:skins_base')));
MojangInterop::registerRoutes($router);

View file

@ -3,20 +3,23 @@ namespace Mince;
use Index\DateTime;
use Index\Data\IDbResult;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Ramsey\Uuid\{Uuid,UuidInterface};
class AccountLinkInfo {
private string $userId;
private string $uuid;
private string $name;
private int $created;
public function __construct(
private string $userId,
private string $uuid,
private string $name,
private int $created
) {}
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->uuid = $result->getString(1);
$this->name = $result->getString(2);
$this->created = $result->getInteger(3);
public static function fromResult(IDbResult $result): self {
return new AccountLinkInfo(
userId: $result->getString(0),
uuid: $result->getString(1),
name: $result->getString(2),
created: $result->getInteger(3),
);
}
public function getUserId(): string {

View file

@ -3,8 +3,7 @@ namespace Mince;
use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
use Ramsey\Uuid\UuidInterface;
class AccountLinks {
@ -64,7 +63,7 @@ class AccountLinks {
if(!$result->next())
throw new RuntimeException('Link info not found.');
return new AccountLinkInfo($result);
return AccountLinkInfo::fromResult($result);
}
public function createLink(

View file

@ -4,24 +4,27 @@ namespace Mince;
use Index\DateTime;
use Index\Data\IDbResult;
use Index\Net\IPAddress;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Ramsey\Uuid\{Uuid,UuidInterface};
class AuthorisationInfo {
private string $id;
private string $uuid;
private string $addr;
private int $requested;
private ?int $granted;
private ?int $used;
public function __construct(
private string $id,
private string $uuid,
private string $addr,
private int $requested,
private ?int $granted,
private ?int $used,
) {}
public function __construct(IDbResult $result) {
$this->id = $result->getString(0);
$this->uuid = $result->getString(1);
$this->addr = $result->getString(2);
$this->requested = $result->getInteger(3);
$this->granted = $result->isNull(4) ? null : $result->getInteger(4);
$this->used = $result->isNull(5) ? null : $result->getInteger(5);
public static function fromResult(IDbResult $result): self {
return new AuthorisationInfo(
id: $result->getString(0),
uuid: $result->getString(1),
addr: $result->getString(2),
requested: $result->getInteger(3),
granted: $result->getIntegerOrNull(4),
used: $result->getIntegerOrNull(5),
);
}
public function getId(): string {

View file

@ -3,8 +3,7 @@ namespace Mince;
use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
use Index\Net\IPAddress;
use Ramsey\Uuid\UuidInterface;
@ -23,7 +22,7 @@ class Authorisations {
public function getAuthorisations(
AccountLinkInfo|UuidInterface|string $uuid
): array {
): iterable {
if($uuid instanceof AccountLinkInfo)
$uuid = $uuid->getUUIDRaw();
elseif($uuid instanceof UuidInterface)
@ -33,13 +32,7 @@ class Authorisations {
$stmt->addParameter(1, $uuid);
$stmt->execute();
$clients = [];
$result = $stmt->getResult();
while($result->next())
$clients[] = new AuthorisationInfo($result);
return $clients;
return $stmt->getResult()->getIterator(AuthorisationInfo::fromResult(...));
}
public function getAuthorisation(
@ -92,7 +85,7 @@ class Authorisations {
if(!$result->next())
throw new RuntimeException('Authorisation info not found.');
return new AuthorisationInfo($result);
return AuthorisationInfo::fromResult($result);
}
public function createAuthorisation(

View file

@ -5,14 +5,18 @@ use Index\DateTime;
use Index\Data\IDbResult;
class CapeInfo {
private string $userId;
private string $hash;
private int $updated;
public function __construct(
private string $userId,
private string $hash,
private int $updated,
) {}
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->hash = $result->getString(1);
$this->updated = $result->getInteger(2);
public static function fromResult(IDbResult $result): self {
return new CapeInfo(
userId: $result->getString(0),
hash: $result->getString(1),
updated: $result->getInteger(2),
);
}
public function getUserId(): string {

View file

@ -3,8 +3,7 @@ namespace Mince;
use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
class Capes {
private DbStatementCache $cache;
@ -35,7 +34,7 @@ class Capes {
$stmt->execute();
$result = $stmt->getResult();
return $result->next() ? new CapeInfo($result) : null;
return $result->next() ? CapeInfo::fromResult($result) : null;
}
public function updateCape(AccountLinkInfo|UserInfo|string $userInfo, string $hash): void {

View file

@ -3,12 +3,12 @@ namespace Mince;
use InvalidArgumentException;
use RuntimeException;
use Index\Routing\IRouter;
use Index\Routing\{Route,RouteHandler};
use Index\Security\CSRFP;
use Ramsey\Uuid\Uuid;
use Sasae\SasaeEnvironment;
class ClientsRoutes {
class ClientsRoutes extends RouteHandler {
public function __construct(
private SasaeEnvironment $templating,
private AccountLinks $accountLinks,
@ -18,15 +18,7 @@ class ClientsRoutes {
private object $authInfo
) {}
public function register(IRouter $router): void {
$router->use('/clients', [$this, 'verifyRequest']);
$router->get('/clients', [$this, 'getClients']);
$router->post('/clients/link', [$this, 'postLink']);
$router->post('/clients/unlink', [$this, 'postUnlink']);
$router->post('/clients/authorise', [$this, 'postAuthorise']);
$router->post('/clients/deauthorise', [$this, 'postDeauthorise']);
}
#[Route('/clients')]
public function verifyRequest($response, $request) {
if(!$this->authInfo->success)
return 403;
@ -49,6 +41,7 @@ class ClientsRoutes {
],
];
#[Route('GET', '/clients')]
public function getClients($response, $request) {
$template = $this->templating->load('clients/index');
@ -69,7 +62,7 @@ class ClientsRoutes {
try {
$linkInfo = $this->accountLinks->getLink(userInfo: $this->authInfo->user_id);
$clients = $this->authorisations->getAuthorisations($linkInfo);
$clients = iterator_to_array($this->authorisations->getAuthorisations($linkInfo));
$template->setVars([
'link' => $linkInfo,
@ -80,6 +73,7 @@ class ClientsRoutes {
return $template;
}
#[Route('POST', '/clients/link')]
public function postLink($response, $request) {
if($this->accountLinks->checkHasLink($this->authInfo->user_id)) {
$response->redirect('/clients?error=link:already');
@ -109,11 +103,13 @@ class ClientsRoutes {
$response->redirect('/clients');
}
#[Route('POST', '/clients/unlink')]
public function postUnlink($response) {
$this->accountLinks->deleteLink(userInfo: $this->authInfo->user_id);
$response->redirect('/clients');
}
#[Route('POST', '/clients/authorise')]
public function postAuthorise($response, $request) {
$body = $request->getContent();
$authId = (string)$body->getParam('auth');
@ -142,6 +138,7 @@ class ClientsRoutes {
$response->redirect('/clients');
}
#[Route('POST', '/clients/deauthorise')]
public function postDeauthorise($response, $request) {
$body = $request->getContent();
$authId = (string)$body->getParam('auth');

View file

@ -1,10 +1,10 @@
<?php
namespace Mince;
use Index\Routing\IRouter;
use Index\Routing\{Route,RouteHandler};
use Sasae\SasaeEnvironment;
class HomeRoutes {
class HomeRoutes extends RouteHandler {
public function __construct(
private SasaeEnvironment $templating,
private Servers $servers,
@ -12,27 +12,30 @@ class HomeRoutes {
private string $loginUrl
) {}
public function register(IRouter $router): void {
$router->get('/', [$this, 'getIndex']);
$router->get('/downloads', [$this, 'getDownloads']);
$router->get('/guide', [$this, 'getGuide']);
$router->get('/login', fn($response) => $response->redirect($this->userInfo->success ? '/' : $this->loginUrl));
$router->get('/index.php', function($response) {
$response->redirect('/', true);
});
}
#[Route('GET', '/')]
public function getIndex($response, $request) {
return $this->templating->render('index', [
'servers' => $this->servers->getServers(deleted: false),
'servers' => iterator_to_array($this->servers->getServers(deleted: false)),
]);
}
#[Route('GET', '/login')]
public function getLogin($response) {
$response->redirect($this->userInfo->success ? '/' : $this->loginUrl);
}
#[Route('GET', '/downloads')]
public function getDownloads() {
return $this->templating->render('downloads');
}
#[Route('GET', '/guide')]
public function getGuide() {
return $this->templating->render('guide');
}
#[Route('GET', '/index.php')]
public function getRedirect($response) {
$response->redirect('/', true);
}
}

View file

@ -2,11 +2,9 @@
namespace Mince;
use stdClass;
use Index\Http\HttpResponseBuilder;
use Index\Http\HttpRequest;
use Index\Http\{HttpResponseBuilder,HttpRequest};
use Index\Routing\IRouter;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Ramsey\Uuid\{Uuid,UuidInterface};
final class MojangInterop {
private const API_SERVER = 'https://api.mojang.com';

View file

@ -5,10 +5,10 @@ use stdClass;
use InvalidArgumentException;
use RuntimeException;
use Stringable;
use Index\Routing\IRouter;
use Index\Routing\{Route,RouteHandler};
use Ramsey\Uuid\Uuid;
class RpcRoutes {
class RpcRoutes extends RouteHandler {
public function __construct(
private Users $users,
private AccountLinks $accountLinks,
@ -18,11 +18,6 @@ class RpcRoutes {
private string $clientsUrl
) {}
public function register(IRouter $router): void {
$router->use('/rpc', [$this, 'verifyRequest']);
$router->post('/rpc/auth', [$this, 'postAuth']);
}
private static function createPayload(string $name, array $attrs = []): object {
$payload = new stdClass;
$payload->name = $name;
@ -46,6 +41,7 @@ class RpcRoutes {
return self::createPayload('error', $attrs);
}
#[Route('/rpc')]
public function verifyRequest($response, $request) {
$userTime = (int)$request->getHeaderLine('X-Mince-Time');
$userHash = base64_decode((string)$request->getHeaderLine('X-Mince-Hash'));
@ -72,6 +68,7 @@ class RpcRoutes {
return self::createErrorPayload('verification', 'Request verification failed.');
}
#[Route('POST', '/rpc/auth')]
public function postAuth($response, $request) {
$body = $request->getContent();

View file

@ -5,26 +5,30 @@ use Index\DateTime;
use Index\Data\IDbResult;
class ServerInfo {
private string $id;
private string $name;
private string $details;
private ?string $javaAddress;
private ?string $javaVersion;
private ?string $bedrockAddress;
private ?string $bedrockVersion;
private int $created;
private ?int $deleted;
public function __construct(
private string $id,
private string $name,
private string $details,
private ?string $javaAddress,
private ?string $javaVersion,
private ?string $bedrockAddress,
private ?string $bedrockVersion,
private int $created,
private ?int $deleted,
) {}
public function __construct(IDbResult $result) {
$this->id = $result->getString(0);
$this->name = $result->getString(1);
$this->details = $result->getString(2);
$this->javaAddress = $result->isNull(3) ? null : $result->getString(3);
$this->javaVersion = $result->isNull(4) ? null : $result->getString(4);
$this->bedrockAddress = $result->isNull(5) ? null : $result->getString(5);
$this->bedrockVersion = $result->isNull(6) ? null : $result->getString(6);
$this->created = $result->getInteger(7);
$this->deleted = $result->isNull(8) ? null : $result->getInteger(8);
public static function fromResult(IDbResult $result): self {
return new ServerInfo(
id: $result->getString(0),
name: $result->getString(1),
details: $result->getString(2),
javaAddress: $result->getStringOrNull(3),
javaVersion: $result->getStringOrNull(4),
bedrockAddress: $result->getStringOrNull(5),
bedrockVersion: $result->getStringOrNull(6),
created: $result->getInteger(7),
deleted: $result->getIntegerOrNull(8),
);
}
public function getId(): string {

View file

@ -1,8 +1,7 @@
<?php
namespace Mince;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
class Servers {
//private IDbConnection $dbConn;
@ -15,7 +14,7 @@ class Servers {
public function getServers(
?bool $deleted = null
): array {
): iterable {
$hasDeleted = $deleted !== null;
$query = 'SELECT server_id, server_name, server_details, server_java_address, server_java_version, server_bedrock_address, server_bedrock_version, UNIX_TIMESTAMP(server_created), UNIX_TIMESTAMP(server_deleted) FROM servers';
@ -26,12 +25,6 @@ class Servers {
$stmt = $this->cache->get($query);
$stmt->execute();
$result = $stmt->getResult();
$servers = [];
while($result->next())
$servers[] = new ServerInfo($result);
return $servers;
return $stmt->getResult()->getIterator(ServerInfo::fromResult(...));
}
}

View file

@ -5,16 +5,20 @@ use Index\DateTime;
use Index\Data\IDbResult;
class SkinInfo {
private string $userId;
private string $hash;
private string $model;
private int $updated;
public function __construct(
private string $userId,
private string $hash,
private string $model,
private int $updated,
) {}
public function __construct(IDbResult $result) {
$this->userId = $result->getString(0);
$this->hash = $result->getString(1);
$this->model = $result->getString(2);
$this->updated = $result->getInteger(3);
public static function fromResult(IDbResult $result): self {
return new SkinInfo(
userId: $result->getString(0),
hash: $result->getString(1),
model: $result->getString(2),
updated: $result->getInteger(3),
);
}
public function getUserId(): string {

View file

@ -3,8 +3,7 @@ namespace Mince;
use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
class Skins {
public const MODELS = ['classic', 'slim'];
@ -37,7 +36,7 @@ class Skins {
$stmt->execute();
$result = $stmt->getResult();
return $result->next() ? new SkinInfo($result) : null;
return $result->next() ? SkinInfo::fromResult($result) : null;
}
public function updateSkin(AccountLinkInfo|UserInfo|string $userInfo, string $hash, string $model): void {

View file

@ -7,12 +7,12 @@ use ImagickPixel;
use InvalidArgumentException;
use RuntimeException;
use Index\XString;
use Index\Routing\IRouter;
use Index\Routing\{Route,RouteHandler};
use Index\Security\CSRFP;
use Ramsey\Uuid\Uuid;
use Sasae\SasaeEnvironment;
class SkinsRoutes {
class SkinsRoutes extends RouteHandler {
private const TEXTURES_DIR = '/textures';
private const TEXTURES_PATH = MCR_DIR_PUB . self::TEXTURES_DIR;
@ -33,18 +33,6 @@ class SkinsRoutes {
throw new RuntimeException('Textures directory is not writable.');
}
public function register(IRouter $router): void {
$router->use('/skins', [$this, 'verifyRequest']);
$router->get('/skins', [$this, 'getSkins']);
$router->post('/skins/upload-skin', [$this, 'postUploadSkin']);
$router->post('/skins/delete-skin', [$this, 'postDeleteSkin']);
$router->post('/skins/upload-cape', [$this, 'postUploadCape']);
$router->post('/skins/delete-cape', [$this, 'postDeleteCape']);
$router->post('/skins/import', [$this, 'postImport']);
$router->get('/session/minecraft/profile/:id', [$this, 'getSessionMinecraftProfile']);
$router->get('/users/profiles/minecraft/:name', [$this, 'getUsersMinecraftProfile']);
}
public function checkHash(string $hash): bool {
return $this->skins->checkHash($hash)
|| $this->capes->checkHash($hash);
@ -64,6 +52,7 @@ class SkinsRoutes {
unlink($path);
}
#[Route('/skins')]
public function verifyRequest($response, $request) {
if(!$this->authInfo->success)
return 403;
@ -97,6 +86,7 @@ class SkinsRoutes {
],
];
#[Route('GET', '/skins')]
public function getSkins($response, $request) {
$skinInfo = $this->skins->getSkin($this->linkInfo);
$skinPath = $skinInfo === null ? null : $this->getRemotePath($skinInfo->getHash(), false);
@ -129,6 +119,7 @@ class SkinsRoutes {
return $template;
}
#[Route('POST', '/skins/upload-skin')]
public function postUploadSkin($response, $request) {
$body = $request->getContent();
if(!$body->hasUploadedFile('texture'))
@ -191,6 +182,7 @@ class SkinsRoutes {
$response->redirect('/skins');
}
#[Route('POST', '/skins/delete-skin')]
public function postDeleteSkin($response) {
$skinInfo = $this->skins->getSkin($this->linkInfo);
if($skinInfo !== null) {
@ -201,6 +193,7 @@ class SkinsRoutes {
$response->redirect('/skins');
}
#[Route('POST', '/skins/upload-cape')]
public function postUploadCape($response, $request) {
$body = $request->getContent();
if(!$body->hasUploadedFile('texture'))
@ -252,6 +245,7 @@ class SkinsRoutes {
$response->redirect('/skins');
}
#[Route('POST', '/skins/delete-cape')]
public function postDeleteCape($response) {
$capeInfo = $this->capes->getCape($this->linkInfo);
if($capeInfo !== null) {
@ -262,6 +256,7 @@ class SkinsRoutes {
$response->redirect('/skins');
}
#[Route('POST', '/skins/import')]
public function postImport($response, $request) {
$body = $request->getContent();
$userAgent = $request->getHeaderLine('User-Agent');
@ -331,6 +326,7 @@ class SkinsRoutes {
$response->redirect('/skins');
}
#[Route('GET', '/session/minecraft/profile/:id')]
public function getSessionMinecraftProfile($response, $request, string $id) {
try {
$uuid = Uuid::fromString($id);
@ -390,6 +386,7 @@ class SkinsRoutes {
];
}
#[Route('GET', '/users/profiles/minecraft/:name')]
public function getUsersMinecraftProfile($response, $request, string $name) {
try {
$linkInfo = $this->accountLinks->getLink(name: $name);
@ -406,4 +403,27 @@ class SkinsRoutes {
'name' => $linkInfo->getName(),
];
}
// quirky path and two of them to achieve equal string length with http://s3.amazonaws.com/MinecraftSkins/ for flashii.net and edgii.net
#[Route('GET', '/s3MinecraftSkins/:filename')]
#[Route('GET', '/s3s3MinecraftSkins/:filename')]
public function getS3MinecraftSkin($response, $request, string $name) {
$path = pathinfo($name);
if(empty($path) || empty($path['filename']) || empty($path['extension']) || $path['extension'] !== 'png')
return 404;
try {
$linkInfo = $this->accountLinks->getLink(name: $path['filename']);
} catch(RuntimeException $ex) {
return 404;
}
$skinInfo = $this->skins->getSkin($linkInfo);
if($skinInfo === null)
return 404;
$response->accelRedirect($this->getRemotePath($skinInfo->getHash(), false));
$response->setContentType('image/png');
$response->setFileName("{$path['filename']}.{$path['extension']}", false);
}
}

View file

@ -1,19 +1,22 @@
<?php
namespace Mince;
use Index\Colour\Colour;
use Index\Colour\ColourRGB;
use Index\Colour\{Colour,ColourRGB};
use Index\Data\IDbResult;
class UserInfo {
private string $id;
private string $name;
private ?int $colour;
public function __construct(
private string $id,
private string $name,
private ?int $colour,
) {}
public function __construct(IDbResult $result) {
$this->id = $result->getString(0);
$this->name = $result->getString(1);
$this->colour = $result->isNull(2) ? null : $result->getInteger(2);
public static function fromResult(IDbResult $result): self {
return new UserInfo(
id: $result->getString(0),
name: $result->getString(1),
colour: $result->getIntegerOrNull(2),
);
}
public function getId(): string {

View file

@ -2,8 +2,7 @@
namespace Mince;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
class Users {
private DbStatementCache $cache;
@ -35,6 +34,6 @@ class Users {
if(!$result->next())
throw new RuntimeException('User info not found.');
return new UserInfo($result);
return UserInfo::fromResult($result);
}
}

View file

@ -4,22 +4,25 @@ namespace Mince;
use Index\DateTime;
use Index\Data\IDbResult;
use Index\Net\IPAddress;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Ramsey\Uuid\{Uuid,UuidInterface};
class VerificationInfo {
private string $code;
private string $uuid;
private string $name;
private string $addr;
private int $created;
public function __construct(
private string $code,
private string $uuid,
private string $name,
private string $addr,
private int $created,
) {}
public function __construct(IDbResult $result) {
$this->code = $result->getString(0);
$this->uuid = $result->getString(1);
$this->name = $result->getString(2);
$this->addr = $result->getString(3);
$this->created = $result->getInteger(4);
public static function fromResult(IDbResult $result): self {
return new VerificationInfo(
code: $result->getString(0),
uuid: $result->getString(1),
name: $result->getString(2),
addr: $result->getString(3),
created: $result->getInteger(4),
);
}
public function getCode(): string {

View file

@ -3,8 +3,7 @@ namespace Mince;
use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\{DbStatementCache,IDbConnection};
use Index\Net\IPAddress;
use Index\Serialisation\Base32;
use Ramsey\Uuid\UuidInterface;
@ -74,7 +73,7 @@ class Verifications {
if(!$result->next())
throw new RuntimeException('Verification info not found.');
return new VerificationInfo($result);
return VerificationInfo::fromResult($result);
}
public function createVerification(