فهرست منبع

Merge branch 'staging' into transalte-profile

Felipe Mateus 3 ماه پیش
والد
کامیت
dc20b5fbe7
100فایلهای تغییر یافته به همراه928 افزوده شده و 916 حذف شده
  1. 10 0
      CHANGELOG.md
  2. 6 0
      README.md
  3. 46 22
      app/Console/Commands/AccountPostCountStatUpdate.php
  4. 3 3
      app/Console/Commands/TransformImports.php
  5. 3 1
      app/Http/Controllers/Admin/AdminSettingsController.php
  6. 82 19
      app/Http/Controllers/Api/ApiV1Controller.php
  7. 1 0
      app/Http/Controllers/ComposeController.php
  8. 3 1
      app/Http/Controllers/DirectMessageController.php
  9. 2 0
      app/Http/Controllers/ImportPostController.php
  10. 57 56
      app/Jobs/ImageOptimizePipeline/ImageUpdate.php
  11. 10 1
      app/Services/Account/AccountStatService.php
  12. 3 3
      app/Services/LikeService.php
  13. 44 39
      app/Services/SnowflakeService.php
  14. 2 1
      app/Util/Media/Image.php
  15. 100 100
      composer.json
  16. 261 437
      composer.lock
  17. 7 7
      config/backup.php
  18. 1 1
      config/filesystems.php
  19. 3 0
      config/import.php
  20. 2 0
      config/instance.php
  21. 2 2
      config/snowflake.php
  22. 279 223
      package-lock.json
  23. 1 0
      package.json
  24. BIN
      public/_lang/bn.json
  25. BIN
      public/_lang/de.json
  26. BIN
      public/_lang/en.json
  27. BIN
      public/_lang/es.json
  28. BIN
      public/_lang/fi.json
  29. BIN
      public/_lang/fr.json
  30. BIN
      public/_lang/hi.json
  31. BIN
      public/_lang/hu.json
  32. BIN
      public/_lang/id.json
  33. BIN
      public/_lang/it.json
  34. BIN
      public/_lang/pt.json
  35. BIN
      public/_lang/ru.json
  36. BIN
      public/_lang/sk.json
  37. BIN
      public/js/account-import.js
  38. BIN
      public/js/activity.js
  39. BIN
      public/js/admin.js
  40. BIN
      public/js/admin_invite.js
  41. BIN
      public/js/app.js
  42. BIN
      public/js/changelog.bundle.d40f01eba00c9885.js
  43. BIN
      public/js/changelog.bundle.efd3d17aee17020e.js
  44. BIN
      public/js/collectioncompose.js
  45. BIN
      public/js/collections.js
  46. BIN
      public/js/compose-classic.js
  47. BIN
      public/js/compose.chunk.80e32f21442c8a91.js
  48. 0 0
      public/js/compose.chunk.80e32f21442c8a91.js.LICENSE.txt
  49. BIN
      public/js/compose.chunk.b06beb250e24db17.js
  50. BIN
      public/js/compose.js
  51. BIN
      public/js/daci.chunk.61b540b1630f8445.js
  52. BIN
      public/js/daci.chunk.8cf1cb07ac8a9100.js
  53. BIN
      public/js/developers.js
  54. BIN
      public/js/direct.js
  55. BIN
      public/js/discover.chunk.00d9b5656d32080e.js
  56. BIN
      public/js/discover.chunk.0ca404627af971f2.js
  57. BIN
      public/js/discover.js
  58. BIN
      public/js/discover~findfriends.chunk.6d494abb9e464081.js
  59. BIN
      public/js/discover~findfriends.chunk.bf787612b58e5473.js
  60. BIN
      public/js/discover~hashtag.bundle.93ce902dca5b65e3.js
  61. BIN
      public/js/discover~hashtag.bundle.c8eb86fb63ede45e.js
  62. BIN
      public/js/discover~memories.chunk.9541b66de9d5d907.js
  63. BIN
      public/js/discover~memories.chunk.9621c5ecf4482f0a.js
  64. BIN
      public/js/discover~myhashtags.chunk.e2ca0db60346d0c2.js
  65. BIN
      public/js/discover~myhashtags.chunk.f4257bc65189fde3.js
  66. BIN
      public/js/discover~serverfeed.chunk.138d9d53d1debac1.js
  67. BIN
      public/js/discover~serverfeed.chunk.4e135dd1c07c17dd.js
  68. BIN
      public/js/discover~settings.chunk.295935b63f9c0971.js
  69. BIN
      public/js/discover~settings.chunk.b1b5642ccef06123.js
  70. BIN
      public/js/dms.chunk.13449036a5b769e6.js
  71. BIN
      public/js/dms.chunk.1a2a644df5c78346.js
  72. BIN
      public/js/dms~message.chunk.4e68bb824f396d86.js
  73. BIN
      public/js/dms~message.chunk.f0d6ccb6f2f1cbf7.js
  74. BIN
      public/js/error404.bundle.e2f43f5006962e80.js
  75. BIN
      public/js/error404.bundle.f5958c1713b4ab7c.js
  76. BIN
      public/js/group-status.js
  77. BIN
      public/js/group-topic-feed.js
  78. BIN
      public/js/group.create.38102523ebf4cde9.js
  79. BIN
      public/js/group.create.72c3a1e5c1dc00dc.js
  80. BIN
      public/js/groups-page-about.16d96a32748daa93.js
  81. BIN
      public/js/groups-page-about.76a616aa7e1a367b.js
  82. BIN
      public/js/groups-page-media.056a7bbc46b79034.js
  83. BIN
      public/js/groups-page-media.526b66b27a0bd091.js
  84. BIN
      public/js/groups-page-members.a8ea4f209fcbe238.js
  85. BIN
      public/js/groups-page-members.c59de89c3b8e3a02.js
  86. BIN
      public/js/groups-page-topics.d279a2438ee20311.js
  87. BIN
      public/js/groups-page-topics.f69667c933f7d122.js
  88. BIN
      public/js/groups-page.4a77f2a4e0024224.js
  89. BIN
      public/js/groups-page.d484dab549a033ca.js
  90. BIN
      public/js/groups-post.e160e406bdb4a1b0.js
  91. BIN
      public/js/groups-profile.1bb8be935d1f108a.js
  92. BIN
      public/js/groups-profile.58b5bf1af4d0722e.js
  93. BIN
      public/js/groups.js
  94. BIN
      public/js/hashtag.js
  95. BIN
      public/js/home.chunk.3d9801a7722f4dfb.js
  96. 0 0
      public/js/home.chunk.3d9801a7722f4dfb.js.LICENSE.txt
  97. BIN
      public/js/home.chunk.acf96f52790bffa6.js
  98. BIN
      public/js/i18n.bundle.85976a3b9d6b922a.js
  99. BIN
      public/js/i18n.bundle.951c52d1740442f6.js
  100. BIN
      public/js/landing.js

+ 10 - 0
CHANGELOG.md

@@ -4,6 +4,7 @@
 
 ### Added
 - Add app register email verify resends ([dbd1e17](https://github.com/pixelfed/pixelfed/commit/dbd1e17))
+- Add AVIF support ([7ddbe0c47](https://github.com/pixelfed/pixelfed/commit/7ddbe0c47))
 
 ### Features
 - WebGL photo filters ([#5374](https://github.com/pixelfed/pixelfed/pull/5374))
@@ -47,6 +48,15 @@
 - Update CuratedOnboarding, add new app:curated-onboarding command, extend email verification window to 7 days and fix resend verification mails ([49604210](https://github.com/pixelfed/pixelfed/commit/49604210))
 - Update DirectMessageController, fix performance issue ([4ec9f99](https://github.com/pixelfed/pixelfed/commit/4ec9f99))
 - Update App Register to expire codes after 4 hours instead of 60 minutes ([0844094b](https://github.com/pixelfed/pixelfed/commit/0844094b))
+- Update ApiV1Controller, fix max_id pagination on home and public timeline feeds ([38e17a06e](https://github.com/pixelfed/pixelfed/commit/38e17a06e))
+- Update Post component, rewrite local post urls ([d2f2a1b1c](https://github.com/pixelfed/pixelfed/commit/d2f2a1b1c))
+- Update Profile component, rewrite local profile urls ([dfbccaa19](https://github.com/pixelfed/pixelfed/commit/dfbccaa19))
+- Update AccountPostCountStatUpdate, fix memory leak ([134eb6324](https://github.com/pixelfed/pixelfed/commit/134eb6324))
+- Update snowflake config, allow custom datacenter/worker ids ([806e210f1](https://github.com/pixelfed/pixelfed/commit/806e210f1))
+- Update ApiV1Controller, return empty statuses feed for private accounts instead of 403 response ([cce657d9c](https://github.com/pixelfed/pixelfed/commit/cce657d9c))
+- Update DM config, allow new users to send DMs by default, with a new env variable to enforce a 72h limit ([717f17cde](https://github.com/pixelfed/pixelfed/commit/717f17cde))
+- Update ApiV1Controller, add pagination to conversations endpoint with min/max/since id pagination and link header support ([244e86bad](https://github.com/pixelfed/pixelfed/commit/244e86bad))
+- Update Direct message component, fix pagination ([e6ef64857](https://github.com/pixelfed/pixelfed/commit/e6ef64857))
 -  ([](https://github.com/pixelfed/pixelfed/commit/))
 
 ## [v0.12.4 (2024-11-08)](https://github.com/pixelfed/pixelfed/compare/v0.12.4...dev)

+ 6 - 0
README.md

@@ -51,6 +51,12 @@ Discovery](https://nlnet.nl/discovery/), part of the [Next Generation
 Internet](https://ngi.eu) initiative.
 
 <p>This project is supported by:</p>
+<p>
+  <a href="https://www.fastly.com/fast-forward">
+    <img src="https://github.com/user-attachments/assets/f1499b1f-c05f-480a-a5d5-dbebcb0e20fd">
+  </a>
+</p>
+
 <p>
   <a href="https://www.digitalocean.com/?utm_medium=opensource&utm_source=pixelfed">
     <img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">

+ 46 - 22
app/Console/Commands/AccountPostCountStatUpdate.php

@@ -2,11 +2,11 @@
 
 namespace App\Console\Commands;
 
-use Illuminate\Console\Command;
-use App\Services\AccountService;
+use App\Profile;
 use App\Services\Account\AccountStatService;
+use App\Services\AccountService;
 use App\Status;
-use App\Profile;
+use Illuminate\Console\Command;
 
 class AccountPostCountStatUpdate extends Command
 {
@@ -29,29 +29,53 @@ class AccountPostCountStatUpdate extends Command
      */
     public function handle()
     {
-        $ids = AccountStatService::getAllPostCountIncr();
-        if(!$ids || !count($ids)) {
+        $chunkSize = 100;
+        $lastId = 0;
+
+        while (true) {
+            $ids = AccountStatService::getPostCountChunk($lastId, $chunkSize);
+
+            if (empty($ids)) {
+                break;
+            }
+
+            foreach ($ids as $id) {
+                $this->processAccount($id);
+                $lastId = $id;
+            }
+
+            if (function_exists('gc_collect_cycles')) {
+                gc_collect_cycles();
+            }
+        }
+
+        return 0;
+    }
+
+    private function processAccount($id)
+    {
+        $acct = AccountService::get($id, true);
+        if (! $acct) {
+            AccountStatService::removeFromPostCount($id);
+
             return;
         }
-        foreach($ids as $id) {
-            $acct = AccountService::get($id, true);
-            if(!$acct) {
+
+        $statusCount = Status::whereProfileId($id)->count();
+        if ($statusCount != $acct['statuses_count']) {
+            $profile = Profile::find($id);
+            if (! $profile) {
                 AccountStatService::removeFromPostCount($id);
-                continue;
-            }
-            $statusCount = Status::whereProfileId($id)->count();
-            if($statusCount != $acct['statuses_count']) {
-                $profile = Profile::find($id);
-                if(!$profile) {
-                    AccountStatService::removeFromPostCount($id);
-                    continue;
-                }
-                $profile->status_count = $statusCount;
-                $profile->save();
-                AccountService::del($id);
+
+                return;
             }
-            AccountStatService::removeFromPostCount($id);
+
+            $profile->status_count = $statusCount;
+            $profile->save();
+
+            AccountService::del($id);
         }
-        return;
+
+        AccountStatService::removeFromPostCount($id);
     }
 }

+ 3 - 3
app/Console/Commands/TransformImports.php

@@ -109,11 +109,11 @@ class TransformImports extends Command
             $status->caption = $caption;
             $status->type = $ip->post_type;
 
-            $status->scope = 'unlisted';
-            $status->visibility = 'unlisted';
+            $status->scope = 'public';
+            $status->visibility = 'public';
             $status->id = $idk['id'];
             $status->created_at = now()->parse($ip->creation_date);
-            $status->save();
+            $status->saveQuietly();
 
             foreach ($ip->media as $ipm) {
                 $fileName = last(explode('/', $ipm['uri']));

+ 3 - 1
app/Http/Controllers/Admin/AdminSettingsController.php

@@ -70,6 +70,7 @@ trait AdminSettingsController
             'type_gif' => 'nullable',
             'type_mp4' => 'nullable',
             'type_webp' => 'nullable',
+            'type_avif' => 'nullable',
             'admin_account_id' => 'nullable',
             'regs' => 'required|in:open,filtered,closed',
             'account_migration' => 'nullable',
@@ -128,6 +129,7 @@ trait AdminSettingsController
             'type_gif' => 'image/gif',
             'type_mp4' => 'video/mp4',
             'type_webp' => 'image/webp',
+            'type_avif' => 'image/avif',
         ];
 
         foreach ($mimes as $key => $value) {
@@ -609,7 +611,7 @@ trait AdminSettingsController
         $mediaTypes = $request->input('media_types');
         $mediaArray = explode(',', $mediaTypes);
         foreach ($mediaArray as $mediaType) {
-            if (! in_array($mediaType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4'])) {
+            if (! in_array($mediaType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4', 'image/avif'])) {
                 return redirect()->back()->withErrors(['media_types' => 'Invalid media type']);
             }
         }

+ 82 - 19
app/Http/Controllers/Api/ApiV1Controller.php

@@ -747,7 +747,7 @@ class ApiV1Controller extends Controller
         } elseif ($profile['locked']) {
             $following = FollowerService::follows($pid, $profile['id']);
             if (! $following) {
-                return response('', 403);
+                return response()->json([]);
             }
             $visibility = ['public', 'unlisted', 'private'];
         } else {
@@ -1897,6 +1897,8 @@ class ApiV1Controller extends Controller
         switch ($media->mime) {
             case 'image/jpeg':
             case 'image/png':
+            case 'image/webp':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 
@@ -2125,6 +2127,8 @@ class ApiV1Controller extends Controller
         switch ($media->mime) {
             case 'image/jpeg':
             case 'image/png':
+            case 'image/webp':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 
@@ -2550,7 +2554,7 @@ class ApiV1Controller extends Controller
                 $minId = null;
             }
 
-            if ($maxId) {
+            if ($maxId && $res->count() >= $limit) {
                 $link = '<'.$baseUrl.'max_id='.$minId.'>; rel="next"';
             }
 
@@ -2973,7 +2977,7 @@ class ApiV1Controller extends Controller
             $minId = null;
         }
 
-        if ($maxId) {
+        if ($maxId && $res->count() >= $limit) {
             $link = '<'.$baseUrl.'max_id='.$minId.'>; rel="next"';
         }
 
@@ -3003,13 +3007,22 @@ class ApiV1Controller extends Controller
         abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
-            'limit' => 'min:1|max:40',
+            'limit' => 'sometimes|integer|min:1|max:40',
             'scope' => 'nullable|in:inbox,sent,requests',
+            'min_id' => 'nullable|integer',
+            'max_id' => 'nullable|integer',
+            'since_id' => 'nullable|integer',
         ]);
 
         $limit = $request->input('limit', 20);
+        if ($limit > 20) {
+            $limit = 20;
+        }
         $scope = $request->input('scope', 'inbox');
         $user = $request->user();
+        $min_id = $request->input('min_id');
+        $max_id = $request->input('max_id');
+        $since_id = $request->input('since_id');
 
         if ($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id)) {
             return [];
@@ -3017,7 +3030,9 @@ class ApiV1Controller extends Controller
 
         $pid = $user->profile_id;
 
-        if (config('database.default') == 'pgsql') {
+        $isPgsql = config('database.default') == 'pgsql';
+
+        if ($isPgsql) {
             $dms = DirectMessage::when($scope === 'inbox', function ($q) use ($pid) {
                 return $q->whereIsHidden(false)
                     ->where(function ($query) use ($pid) {
@@ -3053,20 +3068,38 @@ class ApiV1Controller extends Controller
                 });
         }
 
-        $dms = $dms->orderByDesc('status_id')
-            ->simplePaginate($limit)
-            ->map(function ($dm) use ($pid) {
-                $from = $pid == $dm->to_id ? $dm->from_id : $dm->to_id;
+        if ($min_id) {
+            $dms = $dms->where('id', '>', $min_id);
+        }
+        if ($max_id) {
+            $dms = $dms->where('id', '<', $max_id);
+        }
+        if ($since_id) {
+            $dms = $dms->where('id', '>', $since_id);
+        }
 
-                return [
-                    'id' => $dm->id,
-                    'unread' => false,
-                    'accounts' => [
-                        AccountService::getMastodon($from, true),
-                    ],
-                    'last_status' => StatusService::getDirectMessage($dm->status_id),
-                ];
-            })
+        $dms = $dms->orderByDesc('status_id')->orderBy('id');
+
+        $dmResults = $dms->limit($limit + 1)->get();
+
+        $hasNextPage = $dmResults->count() > $limit;
+
+        if ($hasNextPage) {
+            $dmResults = $dmResults->take($limit);
+        }
+
+        $transformedDms = $dmResults->map(function ($dm) use ($pid) {
+            $from = $pid == $dm->to_id ? $dm->from_id : $dm->to_id;
+
+            return [
+                'id' => $dm->id,
+                'unread' => false,
+                'accounts' => [
+                    AccountService::getMastodon($from, true),
+                ],
+                'last_status' => StatusService::getDirectMessage($dm->status_id),
+            ];
+        })
             ->filter(function ($dm) {
                 return $dm
                     && ! empty($dm['last_status'])
@@ -3080,7 +3113,37 @@ class ApiV1Controller extends Controller
             })
             ->values();
 
-        return $this->json($dms);
+        $links = [];
+
+        if (! $transformedDms->isEmpty()) {
+            $baseUrl = url()->current().'?'.http_build_query(array_merge(
+                $request->except(['min_id', 'max_id', 'since_id']),
+                ['limit' => $limit]
+            ));
+
+            $firstId = $transformedDms->first()['id'];
+            $lastId = $transformedDms->last()['id'];
+
+            $firstLink = $baseUrl;
+            $links[] = '<'.$firstLink.'>; rel="first"';
+
+            if ($hasNextPage) {
+                $nextLink = $baseUrl.'&max_id='.$lastId;
+                $links[] = '<'.$nextLink.'>; rel="next"';
+            }
+
+            if ($max_id || $since_id) {
+                $prevLink = $baseUrl.'&min_id='.$firstId;
+                $links[] = '<'.$prevLink.'>; rel="prev"';
+            }
+        }
+
+        if (! empty($links)) {
+            return response()->json($transformedDms->toArray())
+                ->header('Link', implode(', ', $links));
+        }
+
+        return $this->json($transformedDms);
     }
 
     /**

+ 1 - 0
app/Http/Controllers/ComposeController.php

@@ -133,6 +133,7 @@ class ComposeController extends Controller
             case 'image/jpeg':
             case 'image/png':
             case 'image/webp':
+            case 'image/avif':
                 ImageOptimize::dispatch($media)->onQueue('mmo');
                 break;
 

+ 3 - 1
app/Http/Controllers/DirectMessageController.php

@@ -144,7 +144,9 @@ class DirectMessageController extends Controller
         $user = $request->user();
         abort_if($user->has_roles && ! UserRoleService::can('can-direct-message', $user->id), 403, 'Invalid permissions for this action');
         if (! $user->is_admin) {
-            abort_if($user->created_at->gt(now()->subHours(72)), 400, 'You need to wait a bit before you can DM another account');
+            if ((bool) ! config_cache('instance.allow_new_account_dms')) {
+                abort_if($user->created_at->gt(now()->subHours(72)), 400, 'You need to wait a bit before you can DM another account');
+            }
         }
         $profile = $user->profile;
         $recipient = Profile::where('id', '!=', $profile->id)->findOrFail($request->input('to_id'));

+ 2 - 0
app/Http/Controllers/ImportPostController.php

@@ -29,6 +29,8 @@ class ImportPostController extends Controller
 
             'allow_video_posts' => config('import.instagram.allow_video_posts'),
 
+            'allow_image_webp' => config('import.instagram.allow_image_webp') && str_contains(config_cache('pixelfed.media_types'), 'image/webp'),
+
             'permissions' => [
                 'admins_only' => config('import.instagram.permissions.admins_only'),
                 'admin_follows_only' => config('import.instagram.permissions.admin_follows_only'),

+ 57 - 56
app/Jobs/ImageOptimizePipeline/ImageUpdate.php

@@ -16,70 +16,71 @@ use App\Jobs\MediaPipeline\MediaStoragePipeline;
 
 class ImageUpdate implements ShouldQueue
 {
-	use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
-	protected $media;
+    protected $media;
 
-	protected $protectedMimes = [
-		'image/jpeg',
-		'image/png',
-		'image/webp'
-	];
+    protected $protectedMimes = [
+        'image/jpeg',
+        'image/png',
+        'image/webp',
+        'image/avif'
+    ];
 
-	/**
-	 * Delete the job if its models no longer exist.
-	 *
-	 * @var bool
-	 */
-	public $deleteWhenMissingModels = true;
+    /**
+     * Delete the job if its models no longer exist.
+     *
+     * @var bool
+     */
+    public $deleteWhenMissingModels = true;
 
-	/**
-	 * Create a new job instance.
-	 *
-	 * @return void
-	 */
-	public function __construct(Media $media)
-	{
-		$this->media = $media;
-	}
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct(Media $media)
+    {
+        $this->media = $media;
+    }
 
-	/**
-	 * Execute the job.
-	 *
-	 * @return void
-	 */
-	public function handle()
-	{
-		$media = $this->media;
-		if(!$media) {
-			return;
-		}
-		$path = storage_path('app/'.$media->media_path);
-		$thumb = storage_path('app/'.$media->thumbnail_path);
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $media = $this->media;
+        if(!$media) {
+            return;
+        }
+        $path = storage_path('app/'.$media->media_path);
+        $thumb = storage_path('app/'.$media->thumbnail_path);
 
-		if (!is_file($path)) {
-			return;
-		}
+        if (!is_file($path)) {
+            return;
+        }
 
-		if((bool) config_cache('pixelfed.optimize_image')) {
-			if (in_array($media->mime, $this->protectedMimes) == true) {
-				ImageOptimizer::optimize($thumb);
-				if(!$media->skip_optimize) {
-					ImageOptimizer::optimize($path);
-				}
-			}
-		}
+        if((bool) config_cache('pixelfed.optimize_image')) {
+            if (in_array($media->mime, $this->protectedMimes) == true) {
+                ImageOptimizer::optimize($thumb);
+                if(!$media->skip_optimize) {
+                    ImageOptimizer::optimize($path);
+                }
+            }
+        }
 
-		if (!is_file($path) || !is_file($thumb)) {
-			return;
-		}
+        if (!is_file($path) || !is_file($thumb)) {
+            return;
+        }
 
-		$photo_size = filesize($path);
-		$thumb_size = filesize($thumb);
-		$total = ($photo_size + $thumb_size);
-		$media->size = $total;
-		$media->save();
+        $photo_size = filesize($path);
+        $thumb_size = filesize($thumb);
+        $total = ($photo_size + $thumb_size);
+        $media->size = $total;
+        $media->save();
 
-		MediaStoragePipeline::dispatch($media);
-	}
+        MediaStoragePipeline::dispatch($media);
+    }
 }

+ 10 - 1
app/Services/Account/AccountStatService.php

@@ -2,7 +2,6 @@
 
 namespace App\Services\Account;
 
-use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Redis;
 
 class AccountStatService
@@ -28,4 +27,14 @@ class AccountStatService
     {
         return Redis::zrange(self::REFRESH_CACHE_KEY, 0, $limit);
     }
+
+    public static function getPostCountChunk($lastId, $count)
+    {
+        return Redis::zrangebyscore(
+            self::REFRESH_CACHE_KEY,
+            '('.$lastId,
+            '+inf',
+            ['limit' => [0, $count]]
+        );
+    }
 }

+ 3 - 3
app/Services/LikeService.php

@@ -79,13 +79,13 @@ class LikeService {
 
 		$res = Cache::remember('pf:services:likes:liked_by:' . $status->id, 86400, function() use($status, $empty) {
 			$like = Like::whereStatusId($status->id)->first();
-			if(!$like) {
+			if(!$like || !$like->profile_id) {
 				return $empty;
 			}
 			$id = $like->profile_id;
-			$profile = ProfileService::get($id, true);
+			$profile = AccountService::get($id, true);
 			if(!$profile) {
-				return [];
+				return $empty;
 			}
 			$profileUrl = "/i/web/profile/{$profile['id']}";
 			$res = [

+ 44 - 39
app/Services/SnowflakeService.php

@@ -2,45 +2,50 @@
 
 namespace App\Services;
 
-use Illuminate\Support\Carbon;
 use Cache;
+use Illuminate\Support\Carbon;
 
-class SnowflakeService {
-
-	public static function byDate(Carbon $ts = null)
-	{
-		if($ts instanceOf Carbon) {
-			$ts = now()->parse($ts)->timestamp;
-		} else {
-			return self::next();
-		}
-
-		return ((round($ts * 1000) - 1549756800000) << 22)
-		| (random_int(1,31) << 17)
-		| (random_int(1,31) << 12)
-		| 0;
-	}
-
-	public static function next()
-	{
-		$seq = Cache::get('snowflake:seq');
-
-		if(!$seq) {
-			Cache::put('snowflake:seq', 1);
-			$seq = 1;
-		} else {
-			Cache::increment('snowflake:seq');
-		}
-
-		if($seq >= 4095) {
-			Cache::put('snowflake:seq', 0);
-			$seq = 0;
-		}
-
-		return ((round(microtime(true) * 1000) - 1549756800000) << 22)
-		| (random_int(1,31) << 17)
-		| (random_int(1,31) << 12)
-		| $seq;
-	}
-
+class SnowflakeService
+{
+    public static function byDate(?Carbon $ts = null)
+    {
+        if ($ts instanceof Carbon) {
+            $ts = now()->parse($ts)->timestamp;
+        } else {
+            return self::next();
+        }
+
+        $datacenterId = config('snowflake.datacenter_id') ?? random_int(1, 31);
+        $workerId = config('snowflake.worker_id') ?? random_int(1, 31);
+
+        return ((round($ts * 1000) - 1549756800000) << 22)
+        | ($datacenterId << 17)
+        | ($workerId << 12)
+        | 0;
+    }
+
+    public static function next()
+    {
+        $seq = Cache::get('snowflake:seq');
+
+        if (! $seq) {
+            Cache::put('snowflake:seq', 1);
+            $seq = 1;
+        } else {
+            Cache::increment('snowflake:seq');
+        }
+
+        if ($seq >= 4095) {
+            Cache::put('snowflake:seq', 0);
+            $seq = 0;
+        }
+
+        $datacenterId = config('snowflake.datacenter_id') ?? random_int(1, 31);
+        $workerId = config('snowflake.worker_id') ?? random_int(1, 31);
+
+        return ((round(microtime(true) * 1000) - 1549756800000) << 22)
+        | ($datacenterId << 17)
+        | ($workerId << 12)
+        | $seq;
+    }
 }

+ 2 - 1
app/Util/Media/Image.php

@@ -16,7 +16,8 @@ class Image
 	public $acceptedMimes = [
 		'image/png',
 		'image/jpeg',
-		'image/webp'
+		'image/webp',
+		'image/avif',
 	];
 
 	public function __construct()

+ 100 - 100
composer.json

@@ -1,105 +1,105 @@
 {
-	"name": "pixelfed/pixelfed",
-	"description": "Open and ethical photo sharing platform, powered by ActivityPub federation.",
-	"keywords": ["framework", "laravel", "pixelfed", "activitypub", "social", "network", "federation"],
-	"license": "AGPL-3.0-only",
-	"type": "project",
-	"require": {
-		"php": "^8.2|^8.3",
-		"ext-bcmath": "*",
-		"ext-ctype": "*",
-		"ext-curl": "*",
-		"ext-intl": "*",
-		"ext-json": "*",
-		"ext-mbstring": "*",
-		"ext-openssl": "*",
-		"bacon/bacon-qr-code": "^3.0",
-		"brick/math": "^0.9.3",
-		"buzz/laravel-h-captcha": "^1.0.4",
-		"doctrine/dbal": "^3.0",
-		"endroid/qr-code": "^6.0",
-		"intervention/image": "^2.4",
-		"jenssegers/agent": "^2.6",
-		"laravel-notification-channels/expo": "~1.3.0|~2.0.0",
-		"laravel-notification-channels/webpush": "^8.0",
-		"laravel/framework": "^11.0",
-		"laravel/helpers": "^1.1",
-		"laravel/horizon": "^5.0",
-		"laravel/passport": "^12.0",
-		"laravel/pulse": "^1.3",
-		"laravel/tinker": "^2.9",
-		"laravel/ui": "^4.2",
-		"league/flysystem-aws-s3-v3": "^3.0",
-		"league/iso3166": "^2.1|^4.0",
-		"league/uri": "^7.4",
-		"pbmedia/laravel-ffmpeg": "^8.0",
-		"phpseclib/phpseclib": "~2.0",
-		"pixelfed/fractal": "^0.18.0",
-		"pixelfed/laravel-snowflake": "^2.0",
-		"pragmarx/google2fa": "^8.0",
-		"predis/predis": "^2.0",
-		"pusher/pusher-php-server": "^7.2",
-		"resend/resend-php": "^0.13.0",
-		"spatie/laravel-backup": "^8.0.0",
-		"spatie/laravel-image-optimizer": "^1.8.0",
-		"stevebauman/purify": "^6.2.0",
-		"symfony/http-client": "^6.1",
-		"symfony/mailgun-mailer": "^6.1"
-	},
-	"require-dev": {
-		"fakerphp/faker": "^1.23",
-		"laravel/pint": "^1.13",
-		"laravel/telescope": "^5.0",
-		"mockery/mockery": "^1.6",
-		"nunomaduro/collision": "^8.1",
-		"phpunit/phpunit": "^11.0.1"
-	},
-	"autoload": {
-		"classmap": [
-			"database/seeds",
-			"database/factories"
-		],
-		"psr-4": {
-			"App\\": "app/"
-		},
-		"files": [
-			"app/helpers.php"
-		]
-	},
-	"autoload-dev": {
-		"psr-4": {
-			"Tests\\": "tests/"
-		},
-		"files": [
-			"app/helpers.php"
-		]
-	},
-	"extra": {
-		"laravel": {
-			"dont-discover": [
-				"laravel/passport"
-			]
-		}
-	},
-	"scripts": {
-		"post-root-package-install": [
-			"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
-		],
-		"post-create-project-cmd": [
-			"@php artisan key:generate --ansi"
-		],
+    "name": "pixelfed/pixelfed",
+    "description": "Open and ethical photo sharing platform, powered by ActivityPub federation.",
+    "keywords": ["framework", "laravel", "pixelfed", "activitypub", "social", "network", "federation"],
+    "license": "AGPL-3.0-only",
+    "type": "project",
+    "require": {
+        "php": "^8.2|^8.3|^8.4",
+        "ext-bcmath": "*",
+        "ext-ctype": "*",
+        "ext-curl": "*",
+        "ext-intl": "*",
+        "ext-json": "*",
+        "ext-mbstring": "*",
+        "ext-openssl": "*",
+        "bacon/bacon-qr-code": "^3.0",
+        "brick/math": "^0.11",
+        "buzz/laravel-h-captcha": "^1.0.4",
+        "doctrine/dbal": "^3.0",
+        "endroid/qr-code": "^6.0",
+        "intervention/image": "^2.4",
+        "jenssegers/agent": "^2.6",
+        "laravel-notification-channels/expo": "^2.0.0",
+        "laravel-notification-channels/webpush": "^10.2",
+        "laravel/framework": "^12.0",
+        "laravel/helpers": "^1.1",
+        "laravel/horizon": "^5.0",
+        "laravel/passport": "^12.0",
+        "laravel/pulse": "^1.3",
+        "laravel/tinker": "^2.9",
+        "laravel/ui": "^4.2",
+        "league/flysystem-aws-s3-v3": "^3.0",
+        "league/iso3166": "^2.1|^4.0",
+        "league/uri": "^7.4",
+        "pbmedia/laravel-ffmpeg": "^8.0",
+        "phpseclib/phpseclib": "~2.0",
+        "pixelfed/fractal": "^0.18.0",
+        "pixelfed/laravel-snowflake": "^2.0",
+        "pragmarx/google2fa": "^8.0",
+        "predis/predis": "^2.0",
+        "pusher/pusher-php-server": "^7.2",
+        "resend/resend-php": "^0.13.0",
+        "spatie/laravel-backup": "^9.2.9",
+        "spatie/laravel-image-optimizer": "^1.8.2",
+        "stevebauman/purify": "^6.2.0",
+        "symfony/http-client": "^6.1",
+        "symfony/mailgun-mailer": "^6.1"
+    },
+    "require-dev": {
+        "fakerphp/faker": "^1.23",
+        "laravel/pint": "^1.13",
+        "laravel/telescope": "^5.5",
+        "mockery/mockery": "^1.6",
+        "nunomaduro/collision": "^8.1",
+        "phpunit/phpunit": "^11.0.1"
+    },
+    "autoload": {
+        "classmap": [
+            "database/seeds",
+            "database/factories"
+        ],
+        "psr-4": {
+            "App\\": "app/"
+        },
+        "files": [
+            "app/helpers.php"
+        ]
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\": "tests/"
+        },
+        "files": [
+            "app/helpers.php"
+        ]
+    },
+    "extra": {
+        "laravel": {
+            "dont-discover": [
+                "laravel/passport"
+            ]
+        }
+    },
+    "scripts": {
+        "post-root-package-install": [
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+        ],
+        "post-create-project-cmd": [
+            "@php artisan key:generate --ansi"
+        ],
         "post-update-cmd": [
             "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
         ],
-		"post-autoload-dump": [
-			"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
-			"@php artisan package:discover --ansi"
-		]
-	},
-	"config": {
-		"preferred-install": "dist",
-		"sort-packages": true,
-		"optimize-autoloader": true
-	},
-	"prefer-stable": true
+        "post-autoload-dump": [
+            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
+            "@php artisan package:discover --ansi"
+        ]
+    },
+    "config": {
+        "preferred-install": "dist",
+        "sort-packages": true,
+        "optimize-autoloader": true
+    },
+    "prefer-stable": true
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 261 - 437
composer.lock


+ 7 - 7
config/backup.php

@@ -137,12 +137,12 @@ return [
     'notifications' => [
 
         'notifications' => [
-            \Spatie\Backup\Notifications\Notifications\BackupHasFailedNotification::class => ['mail'],
-            \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFoundNotification::class => ['mail'],
-            \Spatie\Backup\Notifications\Notifications\CleanupHasFailedNotification::class => ['mail'],
-            \Spatie\Backup\Notifications\Notifications\BackupWasSuccessfulNotification::class => ['mail'],
-            \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFoundNotification::class => ['mail'],
-            \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessfulNotification::class => ['mail'],
+            \Spatie\Backup\Notifications\Notifications\BackupHasFailedNotification::class => [],
+            \Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFoundNotification::class => [],
+            \Spatie\Backup\Notifications\Notifications\CleanupHasFailedNotification::class => [],
+            \Spatie\Backup\Notifications\Notifications\BackupWasSuccessfulNotification::class => [],
+            \Spatie\Backup\Notifications\Notifications\HealthyBackupWasFoundNotification::class => [],
+            \Spatie\Backup\Notifications\Notifications\CleanupWasSuccessfulNotification::class => [],
         ],
 
         /*
@@ -152,7 +152,7 @@ return [
         'notifiable' => \Spatie\Backup\Notifications\Notifiable::class,
 
         'mail' => [
-            'to' => env('BACKUP_EMAIL_ADDRESS', ''),
+            'to' => env('BACKUP_EMAIL_ADDRESS', 'noreply@example.com'),
 
             'from' => [
                 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),

+ 1 - 1
config/filesystems.php

@@ -53,7 +53,7 @@ return [
                 ],
                 'dir' => [
                     'public' => 0755,
-                    'private' => 0700,
+                    'private' => 0711,
                 ],
             ],
         ],

+ 3 - 0
config/import.php

@@ -24,6 +24,9 @@ return [
         // Allow video posts to be imported
         'allow_video_posts' => env('PF_IMPORT_IG_ALLOW_VIDEO_POSTS', true),
 
+        // Allow webp posts to be imported
+        'allow_image_webp' => env('PF_IMPORT_IG_ALLOW_IMAGE_WEBP', true),
+
         'permissions' => [
             // Limit to admin accounts only
             'admins_only' => env('PF_IMPORT_IG_PERM_ADMIN_ONLY', false),

+ 2 - 0
config/instance.php

@@ -186,4 +186,6 @@ return [
     ],
 
     'show_peers' => env('INSTANCE_SHOW_PEERS', false),
+
+    'allow_new_account_dms' => env('INSTANCE_ALLOW_NEW_DMS', true),
 ];

+ 2 - 2
config/snowflake.php

@@ -1,6 +1,6 @@
 <?php
 return [
     'epoch' => 1549756800000,
-    'worker_id' => 1,
-    'datacenter_id' => 1,
+    'worker_id' => env('SNOWFLAKE_WORKER_ID', null),
+    'datacenter_id' => env('SNOWFLAKE_DATACENTER_ID', null),
 ];

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 279 - 223
package-lock.json


+ 1 - 0
package.json

@@ -13,6 +13,7 @@
 		"axios": ">=1.6.0",
 		"bootstrap": "^4.5.2",
 		"cross-env": "^5.2.1",
+		"esbuild": "^0.25.1",
 		"jquery": "^3.6.0",
 		"laravel-echo": "^1.12.0",
 		"laravel-mix-make-file-hash": "^2.2.0",

BIN
public/_lang/bn.json


BIN
public/_lang/de.json


BIN
public/_lang/en.json


BIN
public/_lang/es.json


BIN
public/_lang/fi.json


BIN
public/_lang/fr.json


BIN
public/_lang/hi.json


BIN
public/_lang/hu.json


BIN
public/_lang/id.json


BIN
public/_lang/it.json


BIN
public/_lang/pt.json


BIN
public/_lang/ru.json


BIN
public/_lang/sk.json


BIN
public/js/account-import.js


BIN
public/js/activity.js


BIN
public/js/admin.js


BIN
public/js/admin_invite.js


BIN
public/js/app.js


BIN
public/js/changelog.bundle.d40f01eba00c9885.js


BIN
public/js/changelog.bundle.efd3d17aee17020e.js


BIN
public/js/collectioncompose.js


BIN
public/js/collections.js


BIN
public/js/compose-classic.js


BIN
public/js/compose.chunk.80e32f21442c8a91.js


+ 0 - 0
public/js/compose.chunk.b06beb250e24db17.js.LICENSE.txt → public/js/compose.chunk.80e32f21442c8a91.js.LICENSE.txt


BIN
public/js/compose.chunk.b06beb250e24db17.js


BIN
public/js/compose.js


BIN
public/js/daci.chunk.61b540b1630f8445.js


BIN
public/js/daci.chunk.8cf1cb07ac8a9100.js


BIN
public/js/developers.js


BIN
public/js/direct.js


BIN
public/js/discover.chunk.00d9b5656d32080e.js


BIN
public/js/discover.chunk.0ca404627af971f2.js


BIN
public/js/discover.js


BIN
public/js/discover~findfriends.chunk.6d494abb9e464081.js


BIN
public/js/discover~findfriends.chunk.bf787612b58e5473.js


BIN
public/js/discover~hashtag.bundle.93ce902dca5b65e3.js


BIN
public/js/discover~hashtag.bundle.c8eb86fb63ede45e.js


BIN
public/js/discover~memories.chunk.9541b66de9d5d907.js


BIN
public/js/discover~memories.chunk.9621c5ecf4482f0a.js


BIN
public/js/discover~myhashtags.chunk.e2ca0db60346d0c2.js


BIN
public/js/discover~myhashtags.chunk.f4257bc65189fde3.js


BIN
public/js/discover~serverfeed.chunk.138d9d53d1debac1.js


BIN
public/js/discover~serverfeed.chunk.4e135dd1c07c17dd.js


BIN
public/js/discover~settings.chunk.295935b63f9c0971.js


BIN
public/js/discover~settings.chunk.b1b5642ccef06123.js


BIN
public/js/dms.chunk.13449036a5b769e6.js


BIN
public/js/dms.chunk.1a2a644df5c78346.js


BIN
public/js/dms~message.chunk.4e68bb824f396d86.js


BIN
public/js/dms~message.chunk.f0d6ccb6f2f1cbf7.js


BIN
public/js/error404.bundle.e2f43f5006962e80.js


BIN
public/js/error404.bundle.f5958c1713b4ab7c.js


BIN
public/js/group-status.js


BIN
public/js/group-topic-feed.js


BIN
public/js/group.create.38102523ebf4cde9.js


BIN
public/js/group.create.72c3a1e5c1dc00dc.js


BIN
public/js/groups-page-about.16d96a32748daa93.js


BIN
public/js/groups-page-about.76a616aa7e1a367b.js


BIN
public/js/groups-page-media.056a7bbc46b79034.js


BIN
public/js/groups-page-media.526b66b27a0bd091.js


BIN
public/js/groups-page-members.a8ea4f209fcbe238.js


BIN
public/js/groups-page-members.c59de89c3b8e3a02.js


BIN
public/js/groups-page-topics.d279a2438ee20311.js


BIN
public/js/groups-page-topics.f69667c933f7d122.js


BIN
public/js/groups-page.4a77f2a4e0024224.js


BIN
public/js/groups-page.d484dab549a033ca.js


BIN
public/js/groups-post.4c3d4860b029bbaf.js → public/js/groups-post.e160e406bdb4a1b0.js


BIN
public/js/groups-profile.1bb8be935d1f108a.js


BIN
public/js/groups-profile.58b5bf1af4d0722e.js


BIN
public/js/groups.js


BIN
public/js/hashtag.js


BIN
public/js/home.chunk.3d9801a7722f4dfb.js


+ 0 - 0
public/js/home.chunk.acf96f52790bffa6.js.LICENSE.txt → public/js/home.chunk.3d9801a7722f4dfb.js.LICENSE.txt


BIN
public/js/home.chunk.acf96f52790bffa6.js


BIN
public/js/i18n.bundle.85976a3b9d6b922a.js


BIN
public/js/i18n.bundle.951c52d1740442f6.js


BIN
public/js/landing.js


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است