From 767f51cc5b2384a905b7dc91cbb04ce3b2d0033e Mon Sep 17 00:00:00 2001 From: Yannis Vogel Date: Sun, 18 May 2025 08:51:18 +0200 Subject: [PATCH] queue-redis connection + fqdn->CN follow-support - the resque queue integration is now connected to our redis instance. - IMPORTANT NOTICE: In order for this to work, we needed to change the plugins file under vendor/resque/php-resque/lib/Resque/Redis.php #137 ($this->driver->auth($password, $user); (this change is neccessary for our implementation, as they normally don't support the $user attribute as it would be upstream breaking change). I might create a fork for this, to which we bind with composer - The inboxJob now inherits from api instead of creating its own api instance - inbox now actually works with follows and Undo-follows from mastodon->CN (adding the follower to our followers-db and removing it on undo). - fixed bug where paths for templates was incorrectly adjusted - better/proper support for comment-activity in getExternalPosts --- php/federator/api.php | 2 +- php/federator/api/fedusers/inbox.php | 44 ++++++++++++++++++- .../data/activitypub/common/activity.php | 2 +- php/federator/jobs/inboxJob.php | 20 +++++---- php/federator/main.php | 5 ++- php/federator/workers/worker_inbox.php | 12 +++++ plugins/federator/contentnation.php | 13 +++++- plugins/federator/rediscache.php | 17 +++++-- 8 files changed, 95 insertions(+), 20 deletions(-) diff --git a/php/federator/api.php b/php/federator/api.php index 62f7c4e..ec8e9fa 100644 --- a/php/federator/api.php +++ b/php/federator/api.php @@ -47,7 +47,7 @@ class Api extends Main public function __construct() { $this->contentType = "application/json"; - Main::__construct(); + parent::__construct(); } /** diff --git a/php/federator/api/fedusers/inbox.php b/php/federator/api/fedusers/inbox.php index c16f4e7..3c1ff50 100644 --- a/php/federator/api/fedusers/inbox.php +++ b/php/federator/api/fedusers/inbox.php @@ -107,7 +107,7 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface } // Extract username from the actor URL - $username = basename((string)(parse_url($actor, PHP_URL_PATH) ?? '')); + $username = basename((string) (parse_url($actor, PHP_URL_PATH) ?? '')); $domain = parse_url($actor, PHP_URL_HOST); if ($username === null || $domain === null) { error_log("Inbox::post no username or domain found"); @@ -127,7 +127,6 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface if (!isset($user)) { continue; } - $token = \Resque::enqueue('inbox', 'Federator\\Jobs\\InboxJob', [ 'user' => $user, 'activity' => $inboxActivity->toObject(), @@ -160,6 +159,9 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface if ($user === null || $user->id === null) { throw new \Federator\Exceptions\ServerError("Inbox::postForUser couldn't find user: $_user"); } + } else { + // Not a local user, nothing to do + return false; } $rootDir = PROJECT_ROOT . '/'; @@ -170,6 +172,44 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface FILE_APPEND ); + $type = $inboxActivity->getType(); + + if ($type === 'Follow') { + // Someone wants to follow our user + $actor = $inboxActivity->getAActor(); // The follower's actor URI + if ($actor !== '') { + // Extract follower username (you may need to adjust this logic) + $followerUsername = basename((string) (parse_url($actor, PHP_URL_PATH) ?? '')); + $followerDomain = parse_url($actor, PHP_URL_HOST); + if (is_string($followerDomain)) { + $followerId = "{$followerUsername}@{$followerDomain}"; + + // Save the follow relationship + \Federator\DIO\Followers::addFollow($dbh, $followerId, $user->id, $followerDomain); + error_log("Inbox::postForUser: Added follower $followerId for user $user->id"); + } + } + } elseif ($type === 'Undo') { + // Check if this is an Undo of a Follow (i.e., Unfollow) + $object = $inboxActivity->getObject(); + if (is_object($object) && method_exists($object, 'getType') && $object->getType() === 'Follow') { + if ($object instanceof \Federator\Data\ActivityPub\Common\Activity) { + $actor = $object->getAActor(); + if ($actor !== '') { + $followerUsername = basename((string) (parse_url($actor, PHP_URL_PATH) ?? '')); + $followerDomain = parse_url($actor, PHP_URL_HOST); + if (is_string($followerDomain)) { + $followerId = "{$followerUsername}@{$followerDomain}"; + + // Remove the follow relationship + \Federator\DIO\Followers::removeFollow($dbh, $followerId, $user->id); + error_log("Inbox::postForUser: Removed follower $followerId for user $user->id"); + } + } + } + } + } + return true; } } diff --git a/php/federator/data/activitypub/common/activity.php b/php/federator/data/activitypub/common/activity.php index 6e8289c..7cccc92 100644 --- a/php/federator/data/activitypub/common/activity.php +++ b/php/federator/data/activitypub/common/activity.php @@ -47,7 +47,7 @@ class Activity extends APObject return $this; } - public function getAActor() : string + public function getAActor(): string { return $this->aactor; } diff --git a/php/federator/jobs/inboxJob.php b/php/federator/jobs/inboxJob.php index b02c313..6452d3b 100644 --- a/php/federator/jobs/inboxJob.php +++ b/php/federator/jobs/inboxJob.php @@ -2,7 +2,7 @@ namespace Federator\Jobs; -class InboxJob +class InboxJob extends \Federator\Api { /** @var array $args Arguments for the job */ public $args = []; @@ -28,19 +28,21 @@ class InboxJob */ protected $dbh; + /** + * constructor + */ + public function __construct() + { + parent::__construct(); + } + /** * Set up environment for this job */ public function setUp(): void { - $contentnation = new \Federator\Api(); - $contentnation->openDatabase(); - $contentnation->loadPlugins(); - - // Recreate dependencies as needed - $this->dbh = $contentnation->getDatabase(); // get DB connection - $this->connector = $contentnation->getConnector(); // get connector - $this->cache = $contentnation->getCache(); // get cache + $this->openDatabase(); + $this->loadPlugins(); } /** diff --git a/php/federator/main.php b/php/federator/main.php index fb77f3f..f9b966c 100644 --- a/php/federator/main.php +++ b/php/federator/main.php @@ -232,9 +232,10 @@ class Main */ public function renderTemplate($template, $data) { + $rootDir = PROJECT_ROOT . '/'; $smarty = new \Smarty\Smarty(); - $smarty->setCompileDir(PROJECT_ROOT . $this->config['templates']['compiledir']); - $smarty->setTemplateDir((string) realpath(PROJECT_ROOT . $this->config['templates']['path'])); + $smarty->setCompileDir($rootDir . $this->config['templates']['compiledir']); + $smarty->setTemplateDir((string) realpath($rootDir . $this->config['templates']['path'])); $smarty->assign('database', $this->dbh); $smarty->assign('maininstance', $this); foreach ($data as $key => $value) { diff --git a/php/federator/workers/worker_inbox.php b/php/federator/workers/worker_inbox.php index 4eb3646..a264ec2 100644 --- a/php/federator/workers/worker_inbox.php +++ b/php/federator/workers/worker_inbox.php @@ -4,6 +4,18 @@ define('PROJECT_ROOT', dirname(__DIR__, 3)); require_once PROJECT_ROOT . '/vendor/autoload.php'; +$config = parse_ini_file(PROJECT_ROOT . '/rediscache.ini'); + +// Set the Redis backend for Resque +$redisUrl = sprintf( + 'redis://%s:%s@%s:%d', + urlencode($config['username']), + urlencode($config['password']), + $config['host'], + intval($config['port'], 10) +); +\Resque::setBackend($redisUrl); + // Start the worker $worker = new \Resque_Worker(['inbox']); diff --git a/plugins/federator/contentnation.php b/plugins/federator/contentnation.php index 9e8cda8..11a9a92 100644 --- a/plugins/federator/contentnation.php +++ b/plugins/federator/contentnation.php @@ -170,8 +170,17 @@ class ContentNation implements Connector $posts[] = $create; break; // Article case 'Comment': - $comment = new \Federator\Data\ActivityPub\Common\Activity('Comment'); - $create->setObject($comment); + $commentJson = $activity; + $commentJson['type'] = 'Note'; + $commentJson['summary'] = $activity['subject']; + $note = \Federator\Data\ActivityPub\Factory::newFromJson($commentJson, ""); + if ($note === null) { + error_log("ContentNation::getRemotePostsByUser couldn't create comment"); + $comment = new \Federator\Data\ActivityPub\Common\Activity('Comment'); + $create->setObject($comment); + break; + } + $create->setObject($note); $posts[] = $create; break; // Comment case 'Vote': diff --git a/plugins/federator/rediscache.php b/plugins/federator/rediscache.php index 17b8deb..0c74936 100644 --- a/plugins/federator/rediscache.php +++ b/plugins/federator/rediscache.php @@ -71,6 +71,16 @@ class RedisCache implements Cache $this->redis->pconnect($this->config['host'], intval($this->config['port'], 10)); // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal $this->redis->auth([$this->config['username'], $this->config['password']]); + + // Set the Redis backend for Resque + $redisUrl = sprintf( + 'redis://%s:%s@%s:%d', + urlencode($this->config['username']), + urlencode($this->config['password']), + $this->config['host'], + intval($this->config['port'], 10) + ); + \Resque::setBackend($redisUrl); } /** @@ -104,7 +114,8 @@ class RedisCache implements Cache * @param array $jsonData the json data from our platfrom @unused-param * @return \Federator\Data\ActivityPub\Common\Activity|false */ - public function jsonToActivity(array $jsonData) { + public function jsonToActivity(array $jsonData) + { error_log("rediscache::jsonToActivity not implemented"); return false; } @@ -298,7 +309,7 @@ class RedisCache implements Cache { $key = self::createKey('s', $_session . $_user); $serialized = $user->toJson(); - $this->redis->setEx($key, $this->userTTL, $serialized,); + $this->redis->setEx($key, $this->userTTL, $serialized); } /** @@ -314,7 +325,7 @@ class RedisCache implements Cache $this->connect(); } $key = self::createKey('publickey', $keyId); - $this->redis->setEx($key, $this->publicKeyPemTTL, $publicKeyPem); // TTL = 1 hour + $this->redis->setEx($key, $this->publicKeyPemTTL, $publicKeyPem); } /**