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
This commit is contained in:
Yannis Vogel 2025-05-18 08:51:18 +02:00
parent 6cf9a030a4
commit 767f51cc5b
No known key found for this signature in database
8 changed files with 95 additions and 20 deletions

View file

@ -47,7 +47,7 @@ class Api extends Main
public function __construct()
{
$this->contentType = "application/json";
Main::__construct();
parent::__construct();
}
/**

View file

@ -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;
}
}

View file

@ -47,7 +47,7 @@ class Activity extends APObject
return $this;
}
public function getAActor() : string
public function getAActor(): string
{
return $this->aactor;
}

View file

@ -2,7 +2,7 @@
namespace Federator\Jobs;
class InboxJob
class InboxJob extends \Federator\Api
{
/** @var array<string, mixed> $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();
}
/**

View file

@ -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) {

View file

@ -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']);

View file

@ -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':

View file

@ -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<string, mixed> $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);
}
/**