diff --git a/database/migrations/2025_11_18_080804_echoes_audibles_unique_keys.php b/database/migrations/2025_11_18_080804_echoes_audibles_unique_keys.php new file mode 100644 index 000000000..b6d38091a --- /dev/null +++ b/database/migrations/2025_11_18_080804_echoes_audibles_unique_keys.php @@ -0,0 +1,90 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 + */ + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; + +return new class () extends Migration { + /** + * Run the migrations. + */ + public function up(): void + { + // Remove existing echo duplicates + DB::table('user_echoes as e1') + ->join('user_echoes as e2', function ($join): void { + $join->on('e1.id', '<', 'e2.id') + ->whereColumn('e1.user_id', '=', 'e2.user_id') + ->whereColumn('e1.room_id', '=', 'e2.room_id'); + }) + ->delete(); + + DB::table('user_echoes as e1') + ->join('user_echoes as e2', function ($join): void { + $join->on('e1.id', '<', 'e2.id') + ->whereColumn('e1.user_id', '=', 'e2.user_id') + ->whereColumn('e1.target_id', '=', 'e2.target_id'); + }) + ->delete(); + + DB::table('user_echoes as e1') + ->join('user_echoes as e2', function ($join): void { + $join->on('e1.id', '<', 'e2.id') + ->whereColumn('e1.user_id', '=', 'e2.user_id') + ->whereColumn('e1.bot_id', '=', 'e2.bot_id'); + }) + ->delete(); + + Schema::table('user_echoes', function (Blueprint $table): void { + $table->unique(['user_id', 'room_id']); + $table->unique(['user_id', 'target_id']); + $table->unique(['user_id', 'bot_id']); + }); + + // Remove existing audible duplicates + DB::table('user_audibles as a1') + ->join('user_audibles as a2', function ($join): void { + $join->on('a1.id', '<', 'a2.id') + ->whereColumn('a1.user_id', '=', 'a2.user_id') + ->whereColumn('a1.room_id', '=', 'a2.room_id'); + }) + ->delete(); + + DB::table('user_audibles as a1') + ->join('user_audibles as a2', function ($join): void { + $join->on('a1.id', '<', 'a2.id') + ->whereColumn('a1.user_id', '=', 'a2.user_id') + ->whereColumn('a1.target_id', '=', 'a2.target_id'); + }) + ->delete(); + + DB::table('user_audibles as a1') + ->join('user_audibles as a2', function ($join): void { + $join->on('a1.id', '<', 'a2.id') + ->whereColumn('a1.user_id', '=', 'a2.user_id') + ->whereColumn('a1.bot_id', '=', 'a2.bot_id'); + }) + ->delete(); + + Schema::table('user_audibles', function (Blueprint $table): void { + $table->unique(['user_id', 'room_id']); + $table->unique(['user_id', 'target_id']); + $table->unique(['user_id', 'bot_id']); + }); + } +}; diff --git a/database/schema/mysql-schema.sql b/database/schema/mysql-schema.sql index 33749102d..4652be008 100644 --- a/database/schema/mysql-schema.sql +++ b/database/schema/mysql-schema.sql @@ -2259,10 +2259,12 @@ CREATE TABLE `user_audibles` ( `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), + UNIQUE KEY `user_audibles_user_id_room_id_unique` (`user_id`,`room_id`), + UNIQUE KEY `user_audibles_user_id_target_id_unique` (`user_id`,`target_id`), + UNIQUE KEY `user_audibles_user_id_bot_id_unique` (`user_id`,`bot_id`), KEY `user_audibles_room_id_index` (`room_id`), KEY `user_audibles_bot_id_index` (`bot_id`), KEY `user_audibles_status_index` (`status`), - KEY `user_audibles_user_id_foreign` (`user_id`), KEY `user_audibles_target_id_foreign` (`target_id`), CONSTRAINT `user_audibles_target_id_foreign` FOREIGN KEY (`target_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE, CONSTRAINT `user_audibles_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE @@ -2280,9 +2282,11 @@ CREATE TABLE `user_echoes` ( `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), + UNIQUE KEY `user_echoes_user_id_room_id_unique` (`user_id`,`room_id`), + UNIQUE KEY `user_echoes_user_id_target_id_unique` (`user_id`,`target_id`), + UNIQUE KEY `user_echoes_user_id_bot_id_unique` (`user_id`,`bot_id`), KEY `user_echoes_room_id_index` (`room_id`), KEY `user_echoes_bot_id_index` (`bot_id`), - KEY `user_echoes_user_id_foreign` (`user_id`), KEY `user_echoes_target_id_foreign` (`target_id`), CONSTRAINT `user_echoes_target_id_foreign` FOREIGN KEY (`target_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE, CONSTRAINT `user_echoes_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE @@ -3042,3 +3046,4 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (363,'2025_09_07_23 INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (364,'2025_09_08_000029_make_audits_morphable',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (365,'2025_09_25_110038_alter_reports_create_assignee',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (366,'2025_11_08_094209_rename_warnings_torrent_to_torrent_id',1); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (367,'2025_11_18_080804_echoes_audibles_unique_keys',1);