MoveSendFollowPipeline.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <?php
  2. namespace App\Jobs\MovePipeline;
  3. use App\Util\ActivityPub\HttpSignature;
  4. use GuzzleHttp\Client;
  5. use GuzzleHttp\Exception\ClientException;
  6. use Illuminate\Contracts\Queue\ShouldQueue;
  7. use Illuminate\Foundation\Queue\Queueable;
  8. use Illuminate\Queue\Middleware\ThrottlesExceptions;
  9. use Illuminate\Queue\Middleware\WithoutOverlapping;
  10. class MoveSendFollowPipeline implements ShouldQueue
  11. {
  12. use Queueable;
  13. public $follower;
  14. public $targetInbox;
  15. public $targetPid;
  16. public $target;
  17. /**
  18. * The number of times the job may be attempted.
  19. *
  20. * @var int
  21. */
  22. public $tries = 5;
  23. /**
  24. * The maximum number of unhandled exceptions to allow before failing.
  25. *
  26. * @var int
  27. */
  28. public $maxExceptions = 3;
  29. /**
  30. * Get the middleware the job should pass through.
  31. *
  32. * @return array<int, object>
  33. */
  34. public function middleware(): array
  35. {
  36. return [
  37. new WithoutOverlapping('move-send-follow:'.$this->follower->id.':target:'.$this->target),
  38. (new ThrottlesExceptions(2, 5 * 60))->backoff(5),
  39. ];
  40. }
  41. /**
  42. * Create a new job instance.
  43. */
  44. public function __construct($follower, $targetInbox, $targetPid, $target)
  45. {
  46. $this->follower = $follower;
  47. $this->targetInbox = $targetInbox;
  48. $this->targetPid = $targetPid;
  49. $this->target = $target;
  50. }
  51. /**
  52. * Execute the job.
  53. */
  54. public function handle(): void
  55. {
  56. $follower = $this->follower;
  57. $targetPid = $this->targetPid;
  58. $targetInbox = $this->targetInbox;
  59. $target = $this->target;
  60. if (! $follower->username || ! $follower->private_key) {
  61. return;
  62. }
  63. $permalink = 'https://'.config('pixelfed.domain.app').'/users/'.$follower->username;
  64. $version = config('pixelfed.version');
  65. $appUrl = config('app.url');
  66. $userAgent = "(Pixelfed/{$version}; +{$appUrl})";
  67. $addlHeaders = [
  68. 'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
  69. 'User-Agent' => $userAgent,
  70. ];
  71. $activity = [
  72. '@context' => 'https://www.w3.org/ns/activitystreams',
  73. 'type' => 'Follow',
  74. 'actor' => $permalink,
  75. 'object' => $target,
  76. ];
  77. $keyId = $permalink.'#main-key';
  78. $payload = json_encode($activity);
  79. $headers = HttpSignature::signRaw($follower->private_key, $keyId, $targetInbox, $activity, $addlHeaders);
  80. $client = new Client([
  81. 'timeout' => config('federation.activitypub.delivery.timeout'),
  82. ]);
  83. try {
  84. $client->post($targetInbox, [
  85. 'curl' => [
  86. CURLOPT_HTTPHEADER => $headers,
  87. CURLOPT_POSTFIELDS => $payload,
  88. CURLOPT_HEADER => true,
  89. ],
  90. ]);
  91. } catch (ClientException $e) {
  92. }
  93. }
  94. }