Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions app/Federation/Handlers/QuoteRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Federation\Handlers;

use App\Federation\ActivityBuilders\QuoteRequestActivityBuilder;
use App\Jobs\Federation\DiscoverInstance;
use App\Models\Comment;
use App\Models\CommentReply;
use App\Models\Profile;
Expand All @@ -25,6 +26,11 @@ class QuoteRequestHandler
*/
public function handle(array $activity, Profile $actor, ?Profile $target = null): void
{
// Ensure we discover the instance if this is a new remote actor
if (! $actor->local && $actor->uri) {
DiscoverInstance::dispatch($actor->uri)->onQueue('activitypub-in');
}

$quotedObjectUrl = $activity['object'];
$quotePostUrl = is_array($activity['instrument'])
? ($activity['instrument']['id'] ?? null)
Expand Down Expand Up @@ -160,6 +166,31 @@ protected function approveQuoteRequest(
string $quotePostUrl,
Profile $quoterProfile
): void {
// Ensure the quoter profile has an inbox URL for delivery
if (! $quoterProfile->inbox_url) {
// Try to re-fetch the actor data to get the inbox URL (bypass cache)
$actorData = app(\App\Services\ActivityPubService::class)->get($quoterProfile->uri, [], true, true, true, true);

if ($actorData && isset($actorData['inbox'])) {
$quoterProfile->inbox_url = $actorData['inbox'];
$quoterProfile->shared_inbox_url = data_get($actorData, 'endpoints.sharedInbox');
$quoterProfile->save();

// Discover the instance since we're seeing it for the first time
DiscoverInstance::dispatch($quoterProfile->uri)->onQueue('activitypub-in');

} else {
if (config('logging.dev_log')) {
Log::error('QuoteRequest handler: Failed to fetch inbox_url for quoter', [
'quoter_id' => $quoterProfile->id,
'quoter_uri' => $quoterProfile->uri,
]);
}

return;
}
}

$existing = QuoteAuthorization::where('quote_post_url', $quotePostUrl)
->where('quotable_type', get_class($quotable))
->where('quotable_id', $quotable->id)
Expand Down Expand Up @@ -212,6 +243,30 @@ protected function approveQuoteRequest(
*/
protected function rejectQuoteRequest(array $activity, $quotable, Profile $quoterProfile): void
{
// Ensure the quoter profile has an inbox URL for delivery
if (! $quoterProfile->inbox_url) {
// Try to re-fetch the actor data to get the inbox URL (bypass cache)
$actorData = app(\App\Services\ActivityPubService::class)->get($quoterProfile->uri, [], true, true, true, true);
if ($actorData && isset($actorData['inbox'])) {
$quoterProfile->inbox_url = $actorData['inbox'];
$quoterProfile->shared_inbox_url = data_get($actorData, 'endpoints.sharedInbox');
$quoterProfile->save();

// Discover the instance since we're seeing it for the first time
DiscoverInstance::dispatch($quoterProfile->uri)->onQueue('activitypub-in');

} else {
if (config('logging.dev_log')) {
Log::error('QuoteRequest handler: Failed to fetch inbox_url for quoter (reject)', [
'quoter_id' => $quoterProfile->id,
'quoter_uri' => $quoterProfile->uri,
]);
}

return;
}
}

$reject = QuoteRequestActivityBuilder::buildReject(
$activity,
$quotable->profile
Expand Down
24 changes: 14 additions & 10 deletions app/Services/ActivityPubService.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class ActivityPubService
{
public function get($url, $params = [], $signed = true, $validateUrl = true, $validateContentType = true)
public function get($url, $params = [], $signed = true, $validateUrl = true, $validateContentType = true, $bypassCache = false)
{
if ($validateUrl) {
$valid = app(SanitizeService::class)->url($url, true);
Expand All @@ -21,27 +21,31 @@ public function get($url, $params = [], $signed = true, $validateUrl = true, $va

$cacheKey = 'activitypub-fetch:'.sha1($url.json_encode($params));

$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
if (! $bypassCache) {
$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
}
}

$lock = Cache::lock('activitypub-fetch_lock:'.sha1($url.json_encode($params)), 10);

try {
$lock->block(10);

$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
if (! $bypassCache) {
$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
}
}

$result = $this->fetchActivityPub($url, $params, $signed, $validateContentType);

if ($result !== false) {
Cache::put($cacheKey, $result, 14400);
if ($result !== false && $result !== null) {
Cache::put($cacheKey, $result, 14400); // Cache successful results for 4 hours
} else {
Cache::put($cacheKey, null, 14400);
Cache::put($cacheKey, null, 300); // Cache failures for only 5 minutes
}

return $result;
Expand Down