StatusDelete.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php
  2. namespace App\Jobs\StatusPipeline;
  3. use DB;
  4. use App\{
  5. AccountInterstitial,
  6. MediaTag,
  7. Notification,
  8. Report,
  9. Status,
  10. StatusHashtag,
  11. };
  12. use Illuminate\Bus\Queueable;
  13. use Illuminate\Contracts\Queue\ShouldQueue;
  14. use Illuminate\Foundation\Bus\Dispatchable;
  15. use Illuminate\Queue\InteractsWithQueue;
  16. use Illuminate\Queue\SerializesModels;
  17. use League\Fractal;
  18. use League\Fractal\Serializer\ArraySerializer;
  19. use App\Transformer\ActivityPub\Verb\DeleteNote;
  20. use App\Util\ActivityPub\Helpers;
  21. use GuzzleHttp\Pool;
  22. use GuzzleHttp\Client;
  23. use GuzzleHttp\Promise;
  24. use App\Util\ActivityPub\HttpSignature;
  25. class StatusDelete implements ShouldQueue
  26. {
  27. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  28. protected $status;
  29. /**
  30. * Delete the job if its models no longer exist.
  31. *
  32. * @var bool
  33. */
  34. public $deleteWhenMissingModels = true;
  35. /**
  36. * Create a new job instance.
  37. *
  38. * @return void
  39. */
  40. public function __construct(Status $status)
  41. {
  42. $this->status = $status;
  43. }
  44. /**
  45. * Execute the job.
  46. *
  47. * @return void
  48. */
  49. public function handle()
  50. {
  51. $status = $this->status;
  52. if(config('federation.activitypub.enabled') == true) {
  53. $this->fanoutDelete($status);
  54. } else {
  55. $this->unlinkRemoveMedia($status);
  56. }
  57. }
  58. public function unlinkRemoveMedia($status)
  59. {
  60. foreach ($status->media as $media) {
  61. $thumbnail = storage_path("app/{$media->thumbnail_path}");
  62. $photo = storage_path("app/{$media->media_path}");
  63. try {
  64. if (is_file($thumbnail)) {
  65. unlink($thumbnail);
  66. }
  67. if (is_file($photo)) {
  68. unlink($photo);
  69. }
  70. $media->delete();
  71. } catch (Exception $e) {
  72. }
  73. }
  74. if($status->in_reply_to_id) {
  75. DB::transaction(function() use($status) {
  76. $parent = Status::findOrFail($status->in_reply_to_id);
  77. --$parent->reply_count;
  78. $parent->save();
  79. });
  80. }
  81. DB::transaction(function() use($status) {
  82. $comments = Status::where('in_reply_to_id', $status->id)->get();
  83. foreach ($comments as $comment) {
  84. $comment->in_reply_to_id = null;
  85. $comment->save();
  86. Notification::whereItemType('App\Status')
  87. ->whereItemId($comment->id)
  88. ->delete();
  89. }
  90. $status->likes()->delete();
  91. Notification::whereItemType('App\Status')
  92. ->whereItemId($status->id)
  93. ->delete();
  94. StatusHashtag::whereStatusId($status->id)->delete();
  95. Report::whereObjectType('App\Status')
  96. ->whereObjectId($status->id)
  97. ->delete();
  98. MediaTag::where('status_id', $status->id)
  99. ->cursor()
  100. ->each(function($tag) {
  101. Notification::where('item_type', 'App\MediaTag')
  102. ->where('item_id', $tag->id)
  103. ->forceDelete();
  104. $tag->delete();
  105. });
  106. AccountInterstitial::where('item_type', 'App\Status')
  107. ->where('item_id', $status->id)
  108. ->delete();
  109. $status->forceDelete();
  110. });
  111. return true;
  112. }
  113. protected function fanoutDelete($status)
  114. {
  115. $audience = $status->profile->getAudienceInbox();
  116. $profile = $status->profile;
  117. $fractal = new Fractal\Manager();
  118. $fractal->setSerializer(new ArraySerializer());
  119. $resource = new Fractal\Resource\Item($status, new DeleteNote());
  120. $activity = $fractal->createData($resource)->toArray();
  121. $this->unlinkRemoveMedia($status);
  122. $payload = json_encode($activity);
  123. $client = new Client([
  124. 'timeout' => config('federation.activitypub.delivery.timeout')
  125. ]);
  126. $requests = function($audience) use ($client, $activity, $profile, $payload) {
  127. foreach($audience as $url) {
  128. $headers = HttpSignature::sign($profile, $url, $activity);
  129. yield function() use ($client, $url, $headers, $payload) {
  130. return $client->postAsync($url, [
  131. 'curl' => [
  132. CURLOPT_HTTPHEADER => $headers,
  133. CURLOPT_POSTFIELDS => $payload,
  134. CURLOPT_HEADER => true
  135. ]
  136. ]);
  137. };
  138. }
  139. };
  140. $pool = new Pool($client, $requests($audience), [
  141. 'concurrency' => config('federation.activitypub.delivery.concurrency'),
  142. 'fulfilled' => function ($response, $index) {
  143. },
  144. 'rejected' => function ($reason, $index) {
  145. }
  146. ]);
  147. $promise = $pool->promise();
  148. $promise->wait();
  149. }
  150. }