diff --git a/public-legacy/profile.php b/public-legacy/profile.php index cd1776c..010bad0 100644 --- a/public-legacy/profile.php +++ b/public-legacy/profile.php @@ -5,7 +5,6 @@ use InvalidArgumentException; use RuntimeException; use Index\ByteFormat; use Misuzu\Parsers\Parser; -use Misuzu\Profile\ProfileFields; use Misuzu\Users\User; use Misuzu\Users\Assets\UserBackgroundAsset; @@ -41,7 +40,7 @@ $notices = []; $activeBanInfo = $msz->tryGetActiveBan($profileUser); $isBanned = $activeBanInfo !== null; -$profileFields = new ProfileFields($db); +$profileFields = $msz->getProfileFields(); $viewingOwnProfile = $currentUserId === $profileUser->getId(); $userPerms = perms_get_user($currentUserId)[MSZ_PERMS_USER]; $canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS); diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php index 5c40772..77ae569 100644 --- a/src/MisuzuContext.php +++ b/src/MisuzuContext.php @@ -13,6 +13,8 @@ use Misuzu\Config\IConfig; use Misuzu\Counters\Counters; use Misuzu\Emoticons\Emotes; use Misuzu\News\News; +use Misuzu\Profile\ProfileFields; +use Misuzu\Satori\SatoriRoutes; use Misuzu\SharpChat\SharpChatRoutes; use Misuzu\Users\Bans; use Misuzu\Users\BanInfo; @@ -54,6 +56,7 @@ class MisuzuContext { private Users $users; private Sessions $sessions; private Counters $counters; + private ProfileFields $profileFields; public function __construct(IDbConnection $dbConn, IConfig $config) { $this->dbConn = $dbConn; @@ -73,6 +76,7 @@ class MisuzuContext { $this->users = new Users($this->dbConn); $this->sessions = new Sessions($this->dbConn); $this->counters = new Counters($this->dbConn); + $this->profileFields = new ProfileFields($this->dbConn); } public function getDbConn(): IDbConnection { @@ -160,6 +164,10 @@ class MisuzuContext { return $this->counters; } + public function getProfileFields(): ProfileFields { + return $this->profileFields; + } + private array $activeBansCache = []; public function tryGetActiveBan(User|string|null $userInfo = null): ?BanInfo { @@ -256,6 +264,7 @@ class MisuzuContext { $this->router->post('/forum/mark-as-read', $mszCompatHandler('Forum', 'markAsReadPOST')); new SharpChatRoutes($this->router, $this->config->scopeTo('sockChat'), $this->bans, $this->emotes, $this->sessions); + new SatoriRoutes($this->dbConn, $this->config->scopeTo('satori'), $this->router, $this->profileFields); } private function registerLegacyRedirects(): void { diff --git a/src/Satori/SatoriRoutes.php b/src/Satori/SatoriRoutes.php new file mode 100644 index 0000000..aa81a8e --- /dev/null +++ b/src/Satori/SatoriRoutes.php @@ -0,0 +1,162 @@ +dbConn = $dbConn; + $this->config = $config; + $this->profileFields = $profileFields; + + // Simplify default error pages + if($router instanceof HttpFx) + $router->use('/_satori', function() use($router) { + $router->addErrorHandler(400, function($response) { + $response->setContent('HTTP 400'); + }); + $router->addErrorHandler(403, function($response) { + $response->setContent('HTTP 403'); + }); + $router->addErrorHandler(404, function($response) { + $response->setContent('HTTP 404'); + }); + $router->addErrorHandler(500, function($response) { + $response->setContent('HTTP 500'); + }); + $router->addErrorHandler(503, function($response) { + $response->setContent('HTTP 503'); + }); + }); + + $router->use('/_satori', [$this, 'verifyRequest']); + $router->get('/_satori/get-profile-field', [$this, 'getProfileField']); + $router->get('/_satori/get-recent-forum-posts', [$this, 'getRecentForumPosts']); + $router->get('/_satori/get-recent-registrations', [$this, 'getRecentRegistrations']); + } + + public function verifyRequest($response, $request) { + $secretKey = $this->config->getString('secret'); + + if(!empty($secretKey)) { + $userTime = (int)$request->getHeaderLine('X-Satori-Time'); + $userHash = base64_decode((string)$request->getHeaderLine('X-Satori-Hash')); + + $currentTime = time(); + if(empty($userHash) || $userTime < $currentTime - 60 || $userTime > $currentTime + 60) + return 403; + + $verifyText = (string)$userTime . '#' . $request->getPath() . '?' . $request->getParamString(); + $verifyHash = hash_hmac('sha256', $verifyText, $secretKey, true); + + if(!hash_equals($verifyHash, $userHash)) + return 403; + } + } + + public function getProfileField($response, $request): array { + $userId = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT); + $fieldId = (string)$request->getParam('field', FILTER_SANITIZE_NUMBER_INT); + + try { + $fieldValue = $this->profileFields->getFieldValue($fieldId, $userId); + } catch(RuntimeException $ex) { + return ['error' => 105]; + } + + return [ + 'field_value' => $fieldValue->getValue(), + ]; + } + + public function getRecentForumPosts($response, $request): array { + $categoryIds = $this->config->getArray('forum.categories'); + if(empty($categoryIds)) + return []; + + $batchSize = $this->config->getInteger('forum.batch', 6); + $backlogDays = $this->config->getInteger('forum.backlog', 7); + $startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT); + + $args = 0; + $stmt = $this->dbConn->prepare(sprintf( + 'SELECT fp.post_id, ft.topic_id, ft.topic_title, fc.forum_id, fc.forum_name, u.user_id, u.username,' + . ' COALESCE(u.user_colour, r.role_colour), (SELECT MIN(post_id) = fp.post_id FROM msz_forum_posts WHERE topic_id = fp.topic_id)' + . ' FROM msz_forum_posts AS fp' + . ' LEFT JOIN msz_users AS u ON u.user_id = fp.user_id' + . ' LEFT JOIN msz_roles AS r ON r.role_id = u.display_role' + . ' LEFT JOIN msz_forum_topics AS ft ON ft.topic_id = fp.topic_id' + . ' LEFT JOIN msz_forum_categories AS fc ON fc.forum_id = fp.forum_id' + . ' WHERE post_id > ? AND post_deleted IS NULL AND post_created >= NOW() - INTERVAL ? DAY' + . ' AND fp.forum_id IN (%s)' + . ' ORDER BY post_id LIMIT ?', + DbTools::prepareListString($categoryIds) + )); + $stmt->addParameter(++$args, $startId); + $stmt->addParameter(++$args, $backlogDays); + foreach($categoryIds as $categoryId) + $stmt->addParameter(++$args, $categoryId); + $stmt->addParameter(++$args, $batchSize); + $stmt->execute(); + + $posts = []; + $result = $stmt->getResult(); + + while($result->next()) + $posts[] = [ + 'post_id' => $result->getInteger(0), + 'topic_id' => $result->getInteger(1), + 'topic_title' => $result->getString(2), + 'forum_id' => $result->getInteger(3), + 'forum_name' => $result->getString(4), + 'user_id' => $result->getInteger(5), + 'username' => $result->getString(6), + 'user_colour' => $result->getInteger(7), + 'is_opening_post' => $result->getInteger(8), + ]; + + return $posts; + } + + public function getRecentRegistrations($response, $request) { + $batchSize = $this->config->getInteger('users.batch', 10); + $backlogDays = $this->config->getInteger('users.backlog', 7); + $startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT); + + $stmt = $this->dbConn->prepare( + 'SELECT user_id, username FROM msz_users' + . ' WHERE user_id > ? AND user_created >= NOW() - INTERVAL ? DAY' + . ' ORDER BY user_id LIMIT ?' + ); + $stmt->addParameter(1, $startId); + $stmt->addParameter(2, $backlogDays); + $stmt->addParameter(3, $batchSize); + $stmt->execute(); + + $users = []; + $result = $stmt->getResult(); + + while($result->next()) + $users[] = [ + 'user_id' => $result->getInteger(0), + 'username' => $result->getString(1), + ]; + + return $users; + } +}