From 51c67bb797a4d03b640cfede7725ac16cc3e0c73 Mon Sep 17 00:00:00 2001 From: w33b Date: Fri, 9 Jan 2026 10:45:41 +0100 Subject: [PATCH] Improve Migrations & Fix Discord Avatars --- app/Models/User.php | 2 +- .../2025_12_03_193018_fix_user_ids.php | 154 ------------- ..._01_06_161620_fix_discord_oauth_system.php | 17 ++ ...26_01_08_213625_fix_database_structure.php | 207 ++++++++++++++++++ 4 files changed, 225 insertions(+), 155 deletions(-) delete mode 100644 database/migrations/2025_12_03_193018_fix_user_ids.php create mode 100644 database/migrations/2026_01_08_213625_fix_database_structure.php diff --git a/app/Models/User.php b/app/Models/User.php index fb8b0bb..69c60ea 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -103,7 +103,7 @@ class User extends Authenticatable { if ($this->discord_id && $this->discord_avatar && !$this->avatar) { - return "https://external-content.duckduckgo.com/iu/?u=https://cdn.discordapp.com/avatars/{$this->discord_id}/{$this->discord_avatar}.webp"; + return $this->discord_avatar; } if ($this->avatar) diff --git a/database/migrations/2025_12_03_193018_fix_user_ids.php b/database/migrations/2025_12_03_193018_fix_user_ids.php deleted file mode 100644 index fb5a340..0000000 --- a/database/migrations/2025_12_03_193018_fix_user_ids.php +++ /dev/null @@ -1,154 +0,0 @@ -unsignedBigInteger('discord_id')->nullable()->after('id'); - }); - - // 2. Migrate Discord Users IDs - DB::table('users') - ->where('id', '>', 10000) - ->update(['discord_id' => DB::raw('id')]); - - // 3. Temporary new auto increment column - Schema::table('users', function (Blueprint $table) { - $table->unsignedBigInteger('new_id')->first(); - }); - - // 3.5 Manually count (cursed) - $counter = 1; - foreach(User::orderBy('id')->get() as $user) { - $user->new_id = $counter; - $user->save(); - $counter++; - } - - // 4. Drop foreign keys - $this->dropForeignKeys(); - - // 5. Fix ID's in other tables - $this->updateUserIDsInOtherTables(); - - // 6. Remove old ID - Schema::table('users', function (Blueprint $table) { - $table->bigInteger('id')->unsigned()->change(); - $table->dropPrimary('id'); - $table->dropColumn('id'); - }); - - // 7. Rename new_id to id - Schema::table('users', function (Blueprint $table) { - $table->renameColumn('new_id', 'id'); - $table->unsignedBigInteger('id')->autoIncrement()->primary()->change(); - }); - - // 8. Recreate foreign key constraints - $this->addForeignKeys(); - } - - /** - * Drop Foreign Keys referencing the user id - */ - private function dropForeignKeys(): void - { - Schema::table('markable_likes', function (Blueprint $table) { - $table->dropForeign(['user_id']); - }); - - Schema::table('watched', function (Blueprint $table) { - $table->dropForeign(['user_id']); - }); - - Schema::table('discord_access_tokens', function (Blueprint $table) { - $table->dropForeign(['user_id']); - }); - - // Our Schema does include a foreign key, for whatever reason it doesn't exist in the first palce - // Schema::table('user_downloads', function (Blueprint $table) { - // $table->dropForeign(['user_id']); - // }); - } - - /** - * Tables to fix the IDs: - * - comments ['commenter_id'] - * - discord_access_tokens ['user_id'] - * - markable_likes ['user_id'] - * - notifications ['notifiable_id'] - * - playlists ['user_id'] - * - user_downloads ['user_id'] - * - watched ['user_id'] - */ - private function updateUserIDsInOtherTables(): void - { - - DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { - foreach ($users as $user) { - DB::table('comments') - ->where('commenter_id', $user->id) - ->update(['commenter_id' => $user->new_id]); - - DB::table('discord_access_tokens') - ->where('user_id', $user->id) - ->update(['user_id' => $user->new_id]); - - DB::table('markable_likes') - ->where('user_id', $user->id) - ->update(['user_id' => $user->new_id]); - - DB::table('notifications') - ->where('notifiable_id', $user->id) - ->update(['notifiable_id' => $user->new_id]); - - DB::table('playlists') - ->where('user_id', $user->id) - ->update(['user_id' => $user->new_id]); - - DB::table('user_downloads') - ->where('user_id', $user->id) - ->update(['user_id' => $user->new_id]); - - DB::table('watched') - ->where('user_id', $user->id) - ->update(['user_id' => $user->new_id]); - } - }); - } - - /** - * Re-Add Foreign Keys to tables which we dropped previously - */ - private function addForeignKeys(): void - { - Schema::table('markable_likes', function (Blueprint $table) { - $table->unsignedBigInteger('user_id')->references('id')->on('users')->onDelete('cascade')->change(); - }); - - Schema::table('watched', function (Blueprint $table) { - $table->unsignedBigInteger('user_id')->references('id')->on('users')->onDelete('cascade')->change(); - }); - - Schema::table('discord_access_tokens', function (Blueprint $table) { - $table->unsignedBigInteger('user_id')->references('id')->on('users')->onDelete('cascade')->change(); - }); - - Schema::table('user_downloads', function (Blueprint $table) { - $table->unsignedBigInteger('user_id')->references('id')->on('users')->onDelete('cascade')->change(); - }); - } -}; diff --git a/database/migrations/2026_01_06_161620_fix_discord_oauth_system.php b/database/migrations/2026_01_06_161620_fix_discord_oauth_system.php index d436f11..8fd33ab 100644 --- a/database/migrations/2026_01_06_161620_fix_discord_oauth_system.php +++ b/database/migrations/2026_01_06_161620_fix_discord_oauth_system.php @@ -1,5 +1,6 @@ string('password')->nullable()->after('email_verified_at'); $table->rememberToken()->after('password'); }); + + + /** + * -------------------------------------------------------------------- + * Fix Discord Profile Pictures + * -------------------------------------------------------------------- + * The oauth package by socialite now returns a full url of the avatar. + * Meaning all the old entries have to be fixed. + */ + foreach (User::whereNotNull('discord_avatar')->get() as $user) + { + $isGif = preg_match('/a_.+/m', $user->discord_avatar) === 1; + $extension = $isGif ? 'gif' : 'webp'; + $user->discord_avatar = sprintf('https://cdn.discordapp.com/avatars/%s/%s.%s', $user->id, $user->discord_avatar, $extension); + $user->save(); + } } }; diff --git a/database/migrations/2026_01_08_213625_fix_database_structure.php b/database/migrations/2026_01_08_213625_fix_database_structure.php new file mode 100644 index 0000000..b512360 --- /dev/null +++ b/database/migrations/2026_01_08_213625_fix_database_structure.php @@ -0,0 +1,207 @@ +unsignedBigInteger('discord_id')->nullable()->after('id'); + }); + + // 2. Migrate Discord Users IDs + DB::table('users') + ->where('id', '>', 10000) + ->update(['discord_id' => DB::raw('id')]); + + // 3. Temporary new auto increment column + Schema::table('users', function (Blueprint $table) { + $table->unsignedBigInteger('new_id')->first(); + }); + + // 3.5 Count + DB::statement(' + UPDATE users u + JOIN ( + SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS rn + FROM users + ) t ON u.id = t.id + SET u.new_id = t.rn + '); + + // 4. Drop foreign keys + $this->dropForeignKeys(); + + // 5. Fix ID's in other tables + $this->updateUserIDsInOtherTables(); + + // 6. Remove old ID + Schema::table('users', function (Blueprint $table) { + $table->bigInteger('id')->unsigned()->change(); + $table->dropPrimary('id'); + $table->dropColumn('id'); + }); + + // 7. Rename new_id to id + Schema::table('users', function (Blueprint $table) { + $table->renameColumn('new_id', 'id'); + }); + + // 8. Change new ID to auto increment and set as primary key + Schema::table('users', function (Blueprint $table) { + $table->unsignedBigInteger('id')->autoIncrement()->primary()->change(); + }); + + // 9. Remove data that would conflict with constraints + $this->deleteUnreferencedData(); + + // 9. Recreate foreign key constraints + $this->addForeignKeys(); + } + + /** + * Drop Foreign Keys referencing the user id + */ + private function dropForeignKeys(): void + { + Schema::table('markable_likes', function (Blueprint $table) { + $table->dropForeign(['user_id']); + }); + + Schema::table('watched', function (Blueprint $table) { + $table->dropForeign(['user_id']); + }); + + // Our Schema does include a foreign key, for whatever reason it doesn't exist in the first palce + // Schema::table('user_downloads', function (Blueprint $table) { + // $table->dropForeign(['user_id']); + // }); + } + + /** + * Tables to fix the IDs: + * - comments ['commenter_id'] + * - markable_likes ['user_id'] + * - notifications ['notifiable_id'] + * - playlists ['user_id'] + * - user_downloads ['user_id'] + * - watched ['user_id'] + */ + private function updateUserIDsInOtherTables(): void + { + DB::statement(' + UPDATE comments c + JOIN users u ON c.commenter_id = u.id + SET c.commenter_id = u.new_id + '); + + DB::statement(' + UPDATE watched w + JOIN users u ON w.user_id = u.id + SET w.user_id = u.new_id + '); + + DB::statement(' + UPDATE markable_likes ml + JOIN users u ON ml.user_id = u.id + SET ml.user_id = u.new_id + '); + + DB::statement(' + UPDATE notifications n + JOIN users u ON n.notifiable_id = u.id + SET n.notifiable_id = u.new_id + '); + + DB::statement(' + UPDATE playlists p + JOIN users u ON p.user_id = u.id + SET p.user_id = u.new_id + '); + + DB::statement(' + UPDATE user_downloads ud + JOIN users u ON ud.user_id = u.id + SET ud.user_id = u.new_id + '); + } + + /** + * Due to incorrect handling of user deletes, + * we have unreferenced data + */ + private function deleteUnreferencedData(): void + { + // User Downloads Table + DB::table('user_downloads') + ->where('user_id', '>', 1_000_000) + ->delete(); + + // User Playlists Table + $playlists = Playlist::where('user_id', '>', 1_000_000) + ->get(); + + foreach($playlists as $playlist) { + DB::table('playlist_episodes') + ->where('playlist_id', '=', $playlist->id) + ->delete(); + + $playlist->forceDelete(); + } + } + + /** + * Re-Add Foreign Keys to tables which we dropped previously + */ + private function addForeignKeys(): void + { + Schema::table('markable_likes', function (Blueprint $table) { + // Ensure the column is unsigned + $table->bigInteger('user_id')->unsigned()->change(); + + // Add the foreign key constraint + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + + Schema::table('watched', function (Blueprint $table) { + // Ensure the column is unsigned + $table->bigInteger('user_id')->unsigned()->change(); + + // Add the foreign key constraint + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + + Schema::table('user_downloads', function (Blueprint $table) { + // Ensure the column is unsigned + $table->bigInteger('user_id')->unsigned()->change(); + + // Add the foreign key constraint + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + + Schema::table('playlist_episodes', function (Blueprint $table) { + // Ensure the column is unsigned + $table->bigInteger('playlist_id')->unsigned()->change(); + + // Add the foreign key constraint + $table->foreign('playlist_id')->references('id')->on('playlists')->onDelete('cascade'); + }); + + Schema::table('playlists', function (Blueprint $table) { + // Ensure the column is unsigned + $table->bigInteger('user_id')->unsigned()->change(); + + // Add the foreign key constraint + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } +};