Compare commits

...

8 commits

6 changed files with 136 additions and 62 deletions

View file

@ -35,4 +35,4 @@ if($config->hasValues('sentry:dsn'))
$db = DbTools::create($config->getString('database:dsn', 'null:'));
$db->execute('SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';');
$awk = new AwakiContext($db, $config->scopeTo('urls'));
$awk = new AwakiContext($db, $config);

58
composer.lock generated
View file

@ -12,7 +12,7 @@
"source": {
"type": "git",
"url": "https://patchii.net/flash/index.git",
"reference": "9d5b050b8928435416a7efbebe2a19ae8e626224"
"reference": "e4c8ed711e045cffe840ba10a239ede14b0b171f"
},
"require": {
"ext-mbstring": "*",
@ -50,7 +50,7 @@
],
"description": "Composer package for the common library for my projects.",
"homepage": "https://railgun.sh/index",
"time": "2024-03-28T23:27:04+00:00"
"time": "2024-04-10T23:40:14+00:00"
},
{
"name": "flashwave/syokuhou",
@ -268,20 +268,20 @@
},
{
"name": "psr/http-factory",
"version": "1.0.2",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "e616d01114759c4c489f93b099585439f795fe35"
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
"reference": "e616d01114759c4c489f93b099585439f795fe35",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
@ -305,7 +305,7 @@
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
@ -317,9 +317,9 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory/tree/1.0.2"
"source": "https://github.com/php-fig/http-factory"
},
"time": "2023-04-10T20:10:41+00:00"
"time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
@ -525,16 +525,16 @@
},
{
"name": "sentry/sentry",
"version": "4.6.1",
"version": "4.7.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-php.git",
"reference": "5a94184175e5830b589bf923da8c9c3af2c0f409"
"reference": "d6769b2a5e6bf19ed3bbfbf52328ceaf8e6fcb1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/5a94184175e5830b589bf923da8c9c3af2c0f409",
"reference": "5a94184175e5830b589bf923da8c9c3af2c0f409",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/d6769b2a5e6bf19ed3bbfbf52328ceaf8e6fcb1f",
"reference": "d6769b2a5e6bf19ed3bbfbf52328ceaf8e6fcb1f",
"shasum": ""
},
"require": {
@ -598,7 +598,7 @@
],
"support": {
"issues": "https://github.com/getsentry/sentry-php/issues",
"source": "https://github.com/getsentry/sentry-php/tree/4.6.1"
"source": "https://github.com/getsentry/sentry-php/tree/4.7.0"
},
"funding": [
{
@ -610,20 +610,20 @@
"type": "custom"
}
],
"time": "2024-03-08T08:18:09+00:00"
"time": "2024-04-10T13:22:13+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.4.0",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
@ -632,7 +632,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.4-dev"
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@ -661,7 +661,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
@ -677,20 +677,20 @@
"type": "tidelift"
}
],
"time": "2023-05-23T14:45:45+00:00"
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v7.0.0",
"version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "700ff4096e346f54cb628ea650767c8130f1001f"
"reference": "23cc173858776ad451e31f053b1c9f47840b2cfa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f",
"reference": "700ff4096e346f54cb628ea650767c8130f1001f",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa",
"reference": "23cc173858776ad451e31f053b1c9f47840b2cfa",
"shasum": ""
},
"require": {
@ -728,7 +728,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.0.0"
"source": "https://github.com/symfony/options-resolver/tree/v7.0.7"
},
"funding": [
{
@ -744,7 +744,7 @@
"type": "tidelift"
}
],
"time": "2023-08-08T10:20:21+00:00"
"time": "2024-04-18T09:29:19+00:00"
}
],
"packages-dev": [],

View file

@ -0,0 +1,16 @@
<?php
use Index\Data\IDbConnection;
use Index\Data\Migration\IDbMigration;
final class AddSatoriRedirsTable_20240522_204323 implements IDbMigration {
public function migrate(IDbConnection $conn): void {
$conn->execute('
CREATE TABLE awk_satori_redirects (
redir_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
redir_url VARCHAR(255) NOT NULL,
redir_created TIMESTAMP NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (redir_id)
) ENGINE=InnoDB COLLATE=utf8mb4_bin;
');
}
}

View file

@ -12,13 +12,12 @@ use Syokuhou\IConfig;
class AwakiContext {
private const DB_INIT = 'SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';';
private IDbConnection $dbConn;
private HttpRouter $router;
private IConfig $urls;
public function __construct(IDbConnection $dbConn, IConfig $urls) {
$this->dbConn = $dbConn;
$this->urls = $urls;
public function __construct(
private IDbConnection $dbConn,
private IConfig $config
) {
$dbConn->execute(self::DB_INIT);
}
@ -55,6 +54,7 @@ class AwakiContext {
}
private function registerHttpRoutes(): void {
new RedirectorRoutes($this->router, $this->dbConn, $this->urls);
$this->router->register(new SatoriRoutes($this->dbConn, $this->config->scopeTo('satori')));
$this->router->register(new RedirectorRoutes($this->dbConn, $this->config->scopeTo('urls')));
}
}

View file

@ -2,39 +2,24 @@
namespace Awaki;
use Index\Data\IDbConnection;
use Index\Http\Routing\IRouter;
use Index\Http\Routing\{HandlerAttribute,HttpGet,IRouter,IRouteHandler};
use Index\Serialisation\Base62;
use Syokuhou\IConfig;
final class RedirectorRoutes {
private IDbConnection $dbConn;
private IConfig $urls;
final class RedirectorRoutes implements IRouteHandler {
public function __construct(
private IDbConnection $dbConn,
private IConfig $urls
) {}
public function __construct(IRouter $router, IDbConnection $dbConn, IConfig $urls) {
$this->dbConn = $dbConn;
$this->urls = $urls;
$router->get('/', [$this, 'index']);
// profile
$router->get('/u([0-9]+)', [$this, 'redirectProfile']);
$router->get('/p([0-9]+)', [$this, 'redirectProfile']);
$router->get('/u/([A-Za-z0-9\-_]+)', [$this, 'redirectProfile']);
$router->get('/p/([A-Za-z0-9\-_]+)', [$this, 'redirectProfile']);
// forum categories
$router->get('/f/?([0-9]+)', [$this, 'redirectForumCategory']);
$router->get('/fc/?([0-9]+)', [$this, 'redirectForumCategory']);
// forum topic
$router->get('/ft/?([0-9]+)', [$this, 'redirectForumTopic']);
// forum post
$router->get('/fp/?([0-9]+)', [$this, 'redirectForumPost']);
public function registerRoutes(IRouter $router): void {
HandlerAttribute::register($router, $this);
// databased, registered last cuz it matches everything otherwise!
$router->get('/([A-Za-z0-9\-_]+)', [$this, 'redirectDatabase']);
$router->get('/([A-Za-z0-9\-_]+)', $this->redirectDatabase(...));
}
#[HttpGet('/')]
public function index($response): void {
$response->accelRedirect('/index.html');
$response->setTypeHTML();
@ -73,18 +58,23 @@ final class RedirectorRoutes {
$this->redirect($response, $request, $url);
}
#[HttpGet('/[up]([0-9]+)')]
#[HttpGet('/[up]/([A-Za-z0-9\-_]+)')]
public function redirectProfile($response, $request, string $userId) {
$this->redirectSimple($response, $request, $this->urls->getString('user_profile'), $userId);
}
#[HttpGet('/fc?/?([0-9]+)')]
public function redirectForumCategory($response, $request, string $categoryId) {
$this->redirectSimple($response, $request, $this->urls->getString('forum_category'), $categoryId);
}
#[HttpGet('/ft/?([0-9]+)')]
public function redirectForumTopic($response, $request, string $topicId) {
$this->redirectSimple($response, $request, $this->urls->getString('forum_topic'), $topicId);
}
#[HttpGet('/fp/?([0-9]+)')]
public function redirectForumPost($response, $request, string $postId) {
$this->redirectSimple($response, $request, $this->urls->getString('forum_post'), $postId);
}

68
src/SatoriRoutes.php Normal file
View file

@ -0,0 +1,68 @@
<?php
namespace Awaki;
use Index\Data\IDbConnection;
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler};
use Index\Serialisation\Base62;
use Syokuhou\IConfig;
final class SatoriRoutes extends RouteHandler {
public function __construct(
private IDbConnection $dbConn,
private IConfig $config
) {}
#[HttpGet('/[bg]/([A-Za-z0-9]+)')]
public function redirectSatoriShort($response, $request, string $linkId) {
$linkId = Base62::decode($linkId);
$getInfo = $this->dbConn->prepare('SELECT redir_url FROM awk_satori_redirects WHERE redir_id = ?');
$getInfo->addParameter(1, $linkId);
$getInfo->execute();
$info = $getInfo->getResult();
if(!$info->next())
return 404;
$response->redirect($info->getString(0), true);
}
#[HttpPost('/satori/create')]
public function createRedirect($response, $request) {
if(!$request->isFormContent())
return 400;
$content = $request->getContent();
$url = (string)$content->getParam('u');
$time = (int)$content->getParam('t', FILTER_SANITIZE_NUMBER_INT);
$sign = base64_decode((string)$content->getParam('s'));
$hash = hash_hmac('sha256', "satori#create#{$time}#{$url}", $this->config->getString('secret'), true);
if(!hash_equals($hash, $sign))
return 403;
$currentTime = time();
if($time < $currentTime - 30 || $time > $currentTime + 30)
return 403;
$stmt = $this->dbConn->prepare('SELECT redir_id FROM awk_satori_redirects WHERE redir_url = ?');
$stmt->addParameter(1, $url);
$stmt->execute();
$result = $stmt->getResult();
if($result->next()) {
$linkId = $stmt->getInteger(0);
} else {
$stmt = $this->dbConn->prepare('INSERT INTO awk_satori_redirects (redir_url) VALUES (?)');
$stmt->addParameter(1, $url);
$stmt->execute();
$linkId = (int)$stmt->getLastInsertId();
}
return [
'url' => sprintf($this->config->getString('format'), Base62::encode($linkId)),
];
}
}