fix: add foreign keys everywhere

This commit is contained in:
Roardom
2026-01-07 08:45:02 +00:00
parent 995bd8c0ce
commit 1335fe5ca8
10 changed files with 450 additions and 72 deletions
+18 -8
View File
@@ -16,6 +16,8 @@ declare(strict_types=1);
namespace App\Actions\Fortify;
use App\Models\Chatroom;
use App\Models\ChatStatus;
use App\Models\Group;
use App\Models\Invite;
use App\Models\User;
@@ -69,14 +71,22 @@ class CreateNewUser implements CreatesNewUsers
])->validate();
$user = User::query()->create([
'username' => $input['username'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
'passkey' => md5(random_bytes(60)),
'rsskey' => md5(random_bytes(60)),
'uploaded' => config('other.default_upload'),
'downloaded' => config('other.default_download'),
'group_id' => Group::query()->where('slug', '=', 'validating')->soleValue('id'),
'username' => $input['username'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
'passkey' => md5(random_bytes(60)),
'rsskey' => md5(random_bytes(60)),
'uploaded' => config('other.default_upload'),
'downloaded' => config('other.default_download'),
'group_id' => Group::query()->where('slug', '=', 'validating')->soleValue('id'),
'chatroom_id' => Chatroom::query()
->when(
\is_int(config('chat.system_chatroom')),
fn ($query) => $query->where('id', '=', config('chat.system_chatroom')),
fn ($query) => $query->where('name', '=', config('chat.system_chatroom')),
)
->soleValue('id'),
'chat_status_id' => ChatStatus::query()->value('id'),
]);
$user->passkeys()->create(['content' => $user->passkey]);
+1 -1
View File
@@ -129,7 +129,7 @@ final class Group extends Model
*
* @var string[]
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $guarded = [];
/**
* Indicates if the model should be timestamped.
+1 -1
View File
@@ -67,7 +67,7 @@ class TorrentFactory extends Factory
'highspeed' => $this->faker->boolean(),
'status' => ModerationStatus::APPROVED,
'moderated_at' => now(),
'moderated_by' => 1,
'moderated_by' => User::factory(),
'anon' => $this->faker->boolean(),
'sticky' => $this->faker->boolean(),
'internal' => $this->faker->boolean(),
@@ -0,0 +1,291 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @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
{
// Missing still:
//
// For performance:
//
// - announces.{user_id,torrent_id}
//
// Foreign records can be deleted but we want to retain these records with the deleted id for transaction history:
//
// - bon_transactions.bon_exchange_id
// - donations.package_id
//
// The tmdb ids are stored before the movie/tv records are fetched via http:
//
// - requests.{tmdb_movie_id,tmdb_tv_id}
// - torrents.{tmdb_movie_id,tmdb_tv_id}
//
// Chatbox needs a separate refactor:
//
// - messages.{bot_id,chatroom_id}
// - user_audibles.{room_id,bot_id}
// - user_echoes.{room_id,bot_id}
DB::table('application_image_proofs')
->whereNotIn('application_id', DB::table('applications')->select('id'))
->delete();
Schema::table('application_image_proofs', function (Blueprint $table): void {
$table->dropIndex(['application_id']);
$table->foreign('application_id')->references('id')->on('applications')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('application_url_proofs')
->whereNotIn('application_id', DB::table('applications')->select('id'))
->delete();
Schema::table('application_url_proofs', function (Blueprint $table): void {
$table->dropIndex(['application_id']);
$table->foreign('application_id')->references('id')->on('applications')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('automatic_torrent_freeleeches')
->whereNotIn('category_id', DB::table('categories')->select('id'))
->update([
'category_id' => null,
]);
DB::table('automatic_torrent_freeleeches')
->whereNotIn('resolution_id', DB::table('resolutions')->select('id'))
->update([
'resolution_id' => null,
]);
DB::table('automatic_torrent_freeleeches')
->whereNotIn('type_id', DB::table('types')->select('id'))
->update([
'type_id' => null,
]);
Schema::table('automatic_torrent_freeleeches', function (Blueprint $table): void {
$table->unsignedSmallInteger('category_id')->nullable()->change();
$table->unsignedSmallInteger('resolution_id')->nullable()->change();
$table->unsignedSmallInteger('type_id')->nullable()->change();
$table->foreign('category_id')->references('id')->on('categories')->cascadeOnUpdate()->nullOnDelete();
$table->foreign('type_id')->references('id')->on('types')->cascadeOnUpdate()->nullOnDelete();
$table->foreign('resolution_id')->references('id')->on('resolutions')->cascadeOnUpdate()->nullOnDelete();
});
Schema::table('donations', function (Blueprint $table): void {
$table->dropIndex(['user_id']);
$table->foreign('user_id')->references('id')->on('users')->cascadeOnUpdate();
$table->dropIndex(['gifted_user_id']);
$table->foreign('gifted_user_id')->references('id')->on('users')->cascadeOnUpdate()->nullOnDelete();
});
DB::table('forum_permissions')
->whereNotIn('group_id', DB::table('groups')->select('id'))
->delete();
Schema::table('forum_permissions', function (Blueprint $table): void {
$table->dropIndex('fk_permissions_groups1_idx');
$table->foreign('group_id')->references('id')->on('groups')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('likes')
->whereNotIn('post_id', DB::table('posts')->select('id'))
->delete();
Schema::table('likes', function (Blueprint $table): void {
$table->foreign('post_id')->references('id')->on('posts')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('playlist_torrents')
->whereNotIn('playlist_id', DB::table('playlists')->select('id'))
->delete();
Schema::table('playlist_torrents', function (Blueprint $table): void {
$table->dropIndex(['playlist_id']);
$table->foreign('playlist_id')->references('id')->on('playlists')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('reports')
->whereNotIn('reported_request_id', DB::table('requests')->select('id'))
->delete();
DB::table('reports')
->whereNotIn('assigned_to', DB::table('users')->select('id'))
->update([
'assigned_to' => 1,
]);
Schema::table('reports', function (Blueprint $table): void {
$table->dropIndex(['reported_request_id']);
$table->foreign('reported_request_id')->references('id')->on('requests')->cascadeOnUpdate()->nullOnDelete();
$table->foreign('assigned_to')->references('id')->on('users')->cascadeOnUpdate();
});
DB::table('request_bounty')
->whereNotIn('requests_id', DB::table('requests')->select('id'))
->delete();
Schema::table('request_bounty', function (Blueprint $table): void {
$table->dropIndex('request_id');
$table->foreign('requests_id')->references('id')->on('requests')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('request_claims')
->whereNotIn('request_id', DB::table('requests')->select('id'))
->delete();
Schema::table('request_claims', function (Blueprint $table): void {
$table->dropIndex('request_id');
$table->foreign('request_id')->references('id')->on('requests')->cascadeOnUpdate()->cascadeOnDelete();
});
Schema::table('subtitles', function (Blueprint $table): void {
$table->dropIndex(['language_id']);
$table->foreign('language_id')->references('id')->on('media_languages')->cascadeOnUpdate();
});
foreach (DB::table('ticket_attachments')->whereNotIn('ticket_id', DB::table('tickets')->select('id'))->get() as $attachment) {
$path = storage_path('app/files/attachments/files/'.$attachment->file_name);
if (is_file($path)) {
@unlink($path);
}
DB::table('ticket_attachments')->where('id', '=', $attachment->id)->delete();
}
Schema::table('ticket_attachments', function (Blueprint $table): void {
$table->dropIndex(['ticket_id']);
$table->foreign('ticket_id')->references('id')->on('tickets')->cascadeOnUpdate();
});
if (DB::table('tickets')->whereNotIn('category_id', DB::table('ticket_categories')->select('id'))->exists()) {
$newTicketCategoryId = DB::table('ticket_categories')->insertGetId([
'name' => 'Other',
'position' => 999,
'created_at' => now(),
'updated_at' => now(),
]);
DB::table('tickets')
->whereNotIn('category_id', DB::table('ticket_categories')->select('id'))
->update([
'category_id' => $newTicketCategoryId,
]);
}
if (DB::table('tickets')->whereNotIn('priority_id', DB::table('ticket_priorities')->select('id'))->exists()) {
$newTicketPriorityId = DB::table('ticket_priorities')->insertGetId([
'name' => 'Other',
'position' => 999,
'color' => '#000',
'icon' => 'fas fa-circle',
'created_at' => now(),
'updated_at' => now(),
]);
DB::table('tickets')
->whereNotIn('priority_id', DB::table('ticket_priorities')->select('id'))
->update([
'priority_id' => $newTicketPriorityId,
]);
}
Schema::table('tickets', function (Blueprint $table): void {
$table->dropIndex(['category_id']);
$table->foreign('category_id')->references('id')->on('ticket_categories')->cascadeOnUpdate();
$table->dropIndex(['priority_id']);
$table->foreign('priority_id')->references('id')->on('ticket_priorities')->cascadeOnUpdate();
});
DB::table('ticket_notes')
->whereNotIn('user_id', DB::table('users')->select('id'))
->update([
'user_id' => 1,
]);
DB::table('ticket_notes')
->whereNotIn('ticket_id', DB::table('tickets')->select('id'))
->delete();
Schema::table('ticket_notes', function (Blueprint $table): void {
$table->dropIndex(['user_id']);
$table->foreign('user_id')->references('id')->on('users')->cascadeOnUpdate();
$table->dropIndex(['ticket_id']);
$table->foreign('ticket_id')->references('id')->on('tickets')->cascadeOnUpdate()->cascadeOnDelete();
});
DB::table('torrents')
->whereNotIn('distributor_id', DB::table('distributors')->select('id'))
->update([
'distributor_id' => null,
]);
DB::table('torrents')
->whereNotIn('region_id', DB::table('regions')->select('id'))
->update([
'region_id' => null,
]);
DB::table('torrents')
->whereNotIn('moderated_by', DB::table('users')->select('id'))
->update([
'moderated_by' => 1,
]);
Schema::table('torrents', function (Blueprint $table): void {
$table->dropIndex(['distributor_id']);
$table->foreign('distributor_id')->references('id')->on('distributors')->cascadeOnUpdate();
$table->dropIndex(['region_id']);
$table->foreign('region_id')->references('id')->on('regions')->cascadeOnUpdate();
$table->dropIndex('moderated_by');
$table->foreign('moderated_by')->references('id')->on('users');
});
DB::table('users')
->whereNotIn('group_id', DB::table('groups')->select('id'))
->update([
'group_id' => DB::table('groups')->where('slug', '=', 'banned')->value('id'),
]);
DB::table('users')
->whereNotIn('chatroom_id', DB::table('chatrooms')->select('id'))
->update([
'chatroom_id' => DB::table('chatrooms')->value('id'),
]);
DB::table('users')
->whereNotIn('chat_status_id', DB::table('chat_statuses')->select('id'))
->update([
'chat_status_id' => DB::table('chat_statuses')->value('id'),
]);
Schema::table('users', function (Blueprint $table): void {
$table->dropIndex('fk_users_groups_idx');
$table->foreign('group_id')->references('id')->on('groups')->cascadeOnUpdate();
$table->foreign('chatroom_id')->references('id')->on('chatrooms')->cascadeOnUpdate();
$table->foreign('chat_status_id')->references('id')->on('chat_statuses')->cascadeOnUpdate();
});
}
};
+56 -24
View File
@@ -85,7 +85,8 @@ CREATE TABLE `application_image_proofs` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `application_image_proofs_application_id_index` (`application_id`)
KEY `application_image_proofs_application_id_foreign` (`application_id`),
CONSTRAINT `application_image_proofs_application_id_foreign` FOREIGN KEY (`application_id`) REFERENCES `applications` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `application_url_proofs`;
@@ -98,7 +99,8 @@ CREATE TABLE `application_url_proofs` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `application_url_proofs_application_id_index` (`application_id`)
KEY `application_url_proofs_application_id_foreign` (`application_id`),
CONSTRAINT `application_url_proofs_application_id_foreign` FOREIGN KEY (`application_id`) REFERENCES `applications` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `applications`;
@@ -165,13 +167,19 @@ CREATE TABLE `automatic_torrent_freeleeches` (
`position` int unsigned NOT NULL,
`name_regex` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`size` bigint unsigned DEFAULT NULL,
`category_id` int unsigned DEFAULT NULL,
`type_id` int unsigned DEFAULT NULL,
`resolution_id` int unsigned DEFAULT NULL,
`category_id` smallint unsigned DEFAULT NULL,
`type_id` smallint unsigned DEFAULT NULL,
`resolution_id` smallint unsigned DEFAULT NULL,
`freeleech_percentage` int unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `automatic_torrent_freeleeches_category_id_foreign` (`category_id`),
KEY `automatic_torrent_freeleeches_type_id_foreign` (`type_id`),
KEY `automatic_torrent_freeleeches_resolution_id_foreign` (`resolution_id`),
CONSTRAINT `automatic_torrent_freeleeches_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `automatic_torrent_freeleeches_resolution_id_foreign` FOREIGN KEY (`resolution_id`) REFERENCES `resolutions` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `automatic_torrent_freeleeches_type_id_foreign` FOREIGN KEY (`type_id`) REFERENCES `types` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `bans`;
@@ -469,9 +477,11 @@ CREATE TABLE `donations` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `donations_user_id_index` (`user_id`),
KEY `donations_gifted_user_id_index` (`gifted_user_id`),
KEY `donations_package_id_index` (`package_id`)
KEY `donations_package_id_index` (`package_id`),
KEY `donations_user_id_foreign` (`user_id`),
KEY `donations_gifted_user_id_foreign` (`gifted_user_id`),
CONSTRAINT `donations_gifted_user_id_foreign` FOREIGN KEY (`gifted_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `donations_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `email_updates`;
@@ -587,8 +597,8 @@ CREATE TABLE `forum_permissions` (
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_group_id_forum_id_unique` (`group_id`,`forum_id`),
KEY `fk_permissions_forums1_idx` (`forum_id`),
KEY `fk_permissions_groups1_idx` (`group_id`),
KEY `permissions_group_id_forum_id_read_topic_index` (`group_id`,`forum_id`,`read_topic`),
CONSTRAINT `forum_permissions_group_id_foreign` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `permissions_forum_id_foreign` FOREIGN KEY (`forum_id`) REFERENCES `forums` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1000,6 +1010,8 @@ CREATE TABLE `likes` (
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `likes_user_id_foreign` (`user_id`),
KEY `likes_post_id_foreign` (`post_id`),
CONSTRAINT `likes_post_id_foreign` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `likes_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1240,9 +1252,9 @@ CREATE TABLE `playlist_torrents` (
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `playlist_torrents_playlist_id_torrent_id_tmdb_id_unique` (`playlist_id`,`torrent_id`,`tmdb_id`),
KEY `playlist_torrents_playlist_id_index` (`playlist_id`),
KEY `playlist_torrents_tmdb_id_index` (`tmdb_id`),
KEY `playlist_torrents_torrent_id_foreign` (`torrent_id`),
CONSTRAINT `playlist_torrents_playlist_id_foreign` FOREIGN KEY (`playlist_id`) REFERENCES `playlists` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `playlist_torrents_torrent_id_foreign` FOREIGN KEY (`torrent_id`) REFERENCES `torrents` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1376,8 +1388,11 @@ CREATE TABLE `reports` (
KEY `reports_reporter_id_foreign` (`reporter_id`),
KEY `reports_reported_user_id_index` (`reported_user_id`),
KEY `reports_reported_torrent_id_index` (`reported_torrent_id`),
KEY `reports_reported_request_id_index` (`reported_request_id`),
KEY `reports_solved_by_assigned_to_snoozed_until_index` (`solved_by`,`assigned_to`,`snoozed_until`),
KEY `reports_reported_request_id_foreign` (`reported_request_id`),
KEY `reports_assigned_to_foreign` (`assigned_to`),
CONSTRAINT `reports_assigned_to_foreign` FOREIGN KEY (`assigned_to`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `reports_reported_request_id_foreign` FOREIGN KEY (`reported_request_id`) REFERENCES `requests` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `reports_reported_user_id_foreign` FOREIGN KEY (`reported_user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `reports_reporter_id_foreign` FOREIGN KEY (`reporter_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `reports_solved_by_foreign` FOREIGN KEY (`solved_by`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
@@ -1396,8 +1411,9 @@ CREATE TABLE `request_bounty` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `request_id` (`requests_id`),
KEY `request_bounty_user_id_foreign` (`user_id`),
KEY `request_bounty_requests_id_foreign` (`requests_id`),
CONSTRAINT `request_bounty_requests_id_foreign` FOREIGN KEY (`requests_id`) REFERENCES `requests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `request_bounty_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `check_request_bounty_seedbonus` CHECK ((`seedbonus` >= 0))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -1413,8 +1429,9 @@ CREATE TABLE `request_claims` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `request_id` (`request_id`),
KEY `request_claims_user_id_foreign` (`user_id`),
KEY `request_claims_request_id_foreign` (`request_id`),
CONSTRAINT `request_claims_request_id_foreign` FOREIGN KEY (`request_id`) REFERENCES `requests` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `request_claims_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1610,11 +1627,12 @@ CREATE TABLE `subtitles` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `subtitles_language_id_index` (`language_id`),
KEY `subtitles_verified_index` (`verified`),
KEY `subtitles_user_id_foreign` (`user_id`),
KEY `subtitles_moderated_by_foreign` (`moderated_by`),
KEY `subtitles_torrent_id_foreign` (`torrent_id`),
KEY `subtitles_language_id_foreign` (`language_id`),
CONSTRAINT `subtitles_language_id_foreign` FOREIGN KEY (`language_id`) REFERENCES `media_languages` (`id`) ON UPDATE CASCADE,
CONSTRAINT `subtitles_moderated_by_foreign` FOREIGN KEY (`moderated_by`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `subtitles_torrent_id_foreign` FOREIGN KEY (`torrent_id`) REFERENCES `torrents` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `subtitles_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
@@ -1650,8 +1668,9 @@ CREATE TABLE `ticket_attachments` (
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ticket_attachments_ticket_id_index` (`ticket_id`),
KEY `ticket_attachments_user_id_foreign` (`user_id`),
KEY `ticket_attachments_ticket_id_foreign` (`ticket_id`),
CONSTRAINT `ticket_attachments_ticket_id_foreign` FOREIGN KEY (`ticket_id`) REFERENCES `tickets` (`id`) ON UPDATE CASCADE,
CONSTRAINT `ticket_attachments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -1678,8 +1697,10 @@ CREATE TABLE `ticket_notes` (
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ticket_notes_user_id_index` (`user_id`),
KEY `ticket_notes_ticket_id_index` (`ticket_id`)
KEY `ticket_notes_user_id_foreign` (`user_id`),
KEY `ticket_notes_ticket_id_foreign` (`ticket_id`),
CONSTRAINT `ticket_notes_ticket_id_foreign` FOREIGN KEY (`ticket_id`) REFERENCES `tickets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `ticket_notes_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `ticket_priorities`;
@@ -1715,10 +1736,12 @@ CREATE TABLE `tickets` (
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `tickets_category_id_index` (`category_id`),
KEY `tickets_priority_id_index` (`priority_id`),
KEY `tickets_user_id_foreign` (`user_id`),
KEY `tickets_staff_id_foreign` (`staff_id`),
KEY `tickets_category_id_foreign` (`category_id`),
KEY `tickets_priority_id_foreign` (`priority_id`),
CONSTRAINT `tickets_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `ticket_categories` (`id`) ON UPDATE CASCADE,
CONSTRAINT `tickets_priority_id_foreign` FOREIGN KEY (`priority_id`) REFERENCES `ticket_priorities` (`id`) ON UPDATE CASCADE,
CONSTRAINT `tickets_staff_id_foreign` FOREIGN KEY (`staff_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `tickets_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -2174,13 +2197,10 @@ CREATE TABLE `torrents` (
KEY `imdb` (`imdb`),
KEY `tvdb` (`tvdb`),
KEY `mal` (`mal`),
KEY `moderated_by` (`moderated_by`),
KEY `torrents_igdb_index` (`igdb`),
KEY `torrents_type_id_index` (`type_id`),
KEY `torrents_resolution_id_index` (`resolution_id`),
KEY `torrents_personal_release_index` (`personal_release`),
KEY `torrents_distributor_id_index` (`distributor_id`),
KEY `torrents_region_id_index` (`region_id`),
KEY `torrents_status_index` (`status`),
KEY `torrents_seeders_index` (`seeders`),
KEY `torrents_leechers_index` (`leechers`),
@@ -2203,7 +2223,13 @@ CREATE TABLE `torrents` (
KEY `torrents_category_id_status_deleted_at_tv_id_size_index` (`category_id`,`status`,`deleted_at`,`tmdb_tv_id`,`size`),
KEY `torrents_movie_id_index` (`tmdb_movie_id`),
KEY `torrents_tv_id_index` (`tmdb_tv_id`),
KEY `torrents_distributor_id_foreign` (`distributor_id`),
KEY `torrents_region_id_foreign` (`region_id`),
KEY `torrents_moderated_by_foreign` (`moderated_by`),
CONSTRAINT `torrents_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE RESTRICT,
CONSTRAINT `torrents_distributor_id_foreign` FOREIGN KEY (`distributor_id`) REFERENCES `distributors` (`id`) ON UPDATE CASCADE,
CONSTRAINT `torrents_moderated_by_foreign` FOREIGN KEY (`moderated_by`) REFERENCES `users` (`id`),
CONSTRAINT `torrents_region_id_foreign` FOREIGN KEY (`region_id`) REFERENCES `regions` (`id`) ON UPDATE CASCADE,
CONSTRAINT `torrents_resolution_id_foreign` FOREIGN KEY (`resolution_id`) REFERENCES `resolutions` (`id`) ON DELETE RESTRICT,
CONSTRAINT `torrents_type_id_foreign` FOREIGN KEY (`type_id`) REFERENCES `types` (`id`) ON DELETE RESTRICT,
CONSTRAINT `torrents_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
@@ -2603,11 +2629,16 @@ CREATE TABLE `users` (
UNIQUE KEY `users_passkey_unique` (`passkey`),
UNIQUE KEY `users_rsskey_unique` (`rsskey`),
UNIQUE KEY `users_api_token_unique` (`api_token`),
KEY `fk_users_groups_idx` (`group_id`),
KEY `users_read_rules_index` (`read_rules`),
KEY `users_deleted_at_index` (`deleted_at`),
KEY `users_deleted_by_foreign` (`deleted_by`),
KEY `users_group_id_foreign` (`group_id`),
KEY `users_chatroom_id_foreign` (`chatroom_id`),
KEY `users_chat_status_id_foreign` (`chat_status_id`),
CONSTRAINT `users_chat_status_id_foreign` FOREIGN KEY (`chat_status_id`) REFERENCES `chat_statuses` (`id`) ON UPDATE CASCADE,
CONSTRAINT `users_chatroom_id_foreign` FOREIGN KEY (`chatroom_id`) REFERENCES `chatrooms` (`id`) ON UPDATE CASCADE,
CONSTRAINT `users_deleted_by_foreign` FOREIGN KEY (`deleted_by`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
CONSTRAINT `users_group_id_foreign` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON UPDATE CASCADE,
CONSTRAINT `check_users_seedbonus` CHECK ((`seedbonus` >= 0))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -3106,3 +3137,4 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (370,'2026_01_06_23
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (371,'2026_01_07_040502_mark_columns_as_unsigned',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (372,'2026_01_09_015532_alter_table_reports_make_verdict_nullable',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (373,'2026_01_14_120000_add_auto_freeleech_to_user_settings',1);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (374,'2026_01_27_073136_add_foreign_keys_everywhere',1);
+25 -30
View File
@@ -18,6 +18,7 @@ namespace Database\Seeders;
use App\Models\Group;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class GroupSeeder extends Seeder
{
@@ -25,6 +26,7 @@ class GroupSeeder extends Seeder
{
Group::query()->upsert([
[
'id' => 1,
'name' => 'Validating',
'slug' => 'validating',
'position' => 4,
@@ -56,6 +58,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 2,
'name' => 'Guest',
'slug' => 'guest',
'position' => 3,
@@ -87,6 +90,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 3,
'name' => 'User',
'slug' => 'user',
'position' => 6,
@@ -118,6 +122,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 4,
'name' => 'Administrator',
'slug' => 'administrator',
'position' => 20,
@@ -149,6 +154,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 5,
'name' => 'Banned',
'slug' => 'banned',
'position' => 1,
@@ -180,6 +186,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 6,
'name' => 'Moderator',
'slug' => 'moderator',
'position' => 19,
@@ -211,6 +218,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 7,
'name' => 'Uploader',
'slug' => 'uploader',
'position' => 15,
@@ -242,6 +250,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 8,
'name' => 'Trustee',
'slug' => 'trustee',
'position' => 16,
@@ -273,6 +282,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 9,
'name' => 'Bot',
'slug' => 'bot',
'position' => 22,
@@ -304,6 +314,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 10,
'name' => 'Owner',
'slug' => 'owner',
'position' => 21,
@@ -335,6 +346,7 @@ class GroupSeeder extends Seeder
'min_age' => 0,
],
[
'id' => 11,
'name' => 'PowerUser',
'slug' => 'poweruser',
'position' => 7,
@@ -366,6 +378,7 @@ class GroupSeeder extends Seeder
'min_age' => 30 * 24 * 3600,
],
[
'id' => 12,
'name' => 'SuperUser',
'slug' => 'superuser',
'position' => 8,
@@ -397,6 +410,7 @@ class GroupSeeder extends Seeder
'min_age' => 60 * 24 * 3600,
],
[
'id' => 13,
'name' => 'ExtremeUser',
'slug' => 'extremeuser',
'position' => 9,
@@ -428,6 +442,7 @@ class GroupSeeder extends Seeder
'min_age' => 90 * 24 * 3600,
],
[
'id' => 14,
'name' => 'InsaneUser',
'slug' => 'insaneuser',
'position' => 10,
@@ -459,6 +474,7 @@ class GroupSeeder extends Seeder
'min_age' => 180 * 24 * 3600,
],
[
'id' => 15,
'name' => 'Leech',
'slug' => 'leech',
'position' => 5,
@@ -490,6 +506,7 @@ class GroupSeeder extends Seeder
'min_age' => 0,
],
[
'id' => 16,
'name' => 'Veteran',
'slug' => 'veteran',
'position' => 11,
@@ -521,6 +538,7 @@ class GroupSeeder extends Seeder
'min_age' => 365 * 24 * 3600,
],
[
'id' => 17,
'name' => 'Seeder',
'slug' => 'seeder',
'position' => 12,
@@ -552,6 +570,7 @@ class GroupSeeder extends Seeder
'min_age' => 30 * 24 * 3600,
],
[
'id' => 18,
'name' => 'Archivist',
'slug' => 'archivist',
'position' => 13,
@@ -583,6 +602,7 @@ class GroupSeeder extends Seeder
'min_age' => 90 * 24 * 3600,
],
[
'id' => 19,
'name' => 'Internal',
'slug' => 'internal',
'position' => 14,
@@ -614,6 +634,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 20,
'name' => 'Disabled',
'slug' => 'disabled',
'position' => 2,
@@ -645,6 +666,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 21,
'name' => 'Pruned',
'slug' => 'pruned',
'position' => 0,
@@ -676,6 +698,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 22,
'name' => 'Editor',
'slug' => 'editor',
'position' => 17,
@@ -707,6 +730,7 @@ class GroupSeeder extends Seeder
'min_age' => null,
],
[
'id' => 23,
'name' => 'Torrent Moderator',
'slug' => 'torrent-moderator',
'position' => 18,
@@ -737,35 +761,6 @@ class GroupSeeder extends Seeder
'min_ratio' => null,
'min_age' => null,
]
], ['slug'], [
'name',
'position',
'color',
'icon',
'effect',
'autogroup',
'system_required',
'is_owner',
'is_admin',
'is_modo',
'is_torrent_modo',
'is_editor',
'is_internal',
'is_uploader',
'is_trusted',
'is_freeleech',
'is_immune',
'can_chat',
'can_comment',
'can_invite',
'can_request',
'can_upload',
'level',
'min_uploaded',
'min_seedsize',
'min_avg_seedtime',
'min_ratio',
'min_age'
]);
], ['id'], ['id' => DB::raw('id')]);
}
}
+32
View File
@@ -16,6 +16,8 @@ declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Chatroom;
use App\Models\ChatStatus;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
@@ -26,6 +28,12 @@ class UserSeeder extends Seeder
{
public function run(): void
{
$this->call([
ChatStatusSeeder::class,
ChatroomSeeder::class,
GroupSeeder::class,
]);
User::query()->upsert([
[
'id' => User::SYSTEM_USER_ID,
@@ -37,6 +45,14 @@ class UserSeeder extends Seeder
'passkey' => md5(random_bytes(60)),
'rsskey' => md5(random_bytes(60)),
'api_token' => Str::random(100),
'chatroom_id' => Chatroom::query()
->when(
\is_int(config('chat.system_chatroom')),
fn ($query) => $query->where('id', '=', config('chat.system_chatroom')),
fn ($query) => $query->where('name', '=', config('chat.system_chatroom')),
)
->soleValue('id'),
'chat_status_id' => ChatStatus::query()->value('id'),
],
[
'id' => 2,
@@ -48,6 +64,14 @@ class UserSeeder extends Seeder
'passkey' => md5(random_bytes(60)),
'rsskey' => md5(random_bytes(60)),
'api_token' => Str::random(100),
'chatroom_id' => Chatroom::query()
->when(
\is_int(config('chat.system_chatroom')),
fn ($query) => $query->where('id', '=', config('chat.system_chatroom')),
fn ($query) => $query->where('name', '=', config('chat.system_chatroom')),
)
->soleValue('id'),
'chat_status_id' => ChatStatus::query()->value('id'),
],
[
'id' => 3,
@@ -59,6 +83,14 @@ class UserSeeder extends Seeder
'passkey' => md5(random_bytes(60)),
'rsskey' => md5(random_bytes(60)),
'api_token' => Str::random(100),
'chatroom_id' => Chatroom::query()
->when(
\is_int(config('chat.system_chatroom')),
fn ($query) => $query->where('id', '=', config('chat.system_chatroom')),
fn ($query) => $query->where('name', '=', config('chat.system_chatroom')),
)
->soleValue('id'),
'chat_status_id' => ChatStatus::query()->value('id'),
],
], ['username'], ['updated_at' => DB::raw('updated_at')]);
}
+12
View File
@@ -1,5 +1,11 @@
parameters:
ignoreErrors:
-
message: '#^Call to function is_int\(\) with string\|null will always evaluate to false\.$#'
identifier: function.impossibleType
count: 1
path: app/Actions/Fortify/CreateNewUser.php
-
message: '#^Instanceof between App\\Models\\User and Illuminate\\Contracts\\Auth\\MustVerifyEmail will always evaluate to true\.$#'
identifier: instanceof.alwaysTrue
@@ -624,6 +630,12 @@ parameters:
count: 1
path: database/migrations/2022_12_30_090351_update_user_privacy_table.php
-
message: '#^Call to function is_int\(\) with string\|null will always evaluate to false\.$#'
identifier: function.impossibleType
count: 3
path: database/seeders/UserSeeder.php
-
message: '#^Variable \$level might not be defined\.$#'
identifier: variable.undefined
@@ -16,7 +16,8 @@ declare(strict_types=1);
use App\Models\Invite;
use App\Models\User;
use Database\Seeders\GroupSeeder;
use Database\Seeders\ChatroomSeeder;
use Database\Seeders\ChatStatusSeeder;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\URL;
@@ -26,7 +27,8 @@ use function Pest\Laravel\assertDatabaseMissing;
use function Pest\Laravel\seed;
beforeEach(function (): void {
seed(GroupSeeder::class);
seed(ChatroomSeeder::class);
seed(ChatStatusSeeder::class);
Event::fake(Registered::class);
});
@@ -17,21 +17,25 @@ declare(strict_types=1);
use App\Models\Bot;
use App\Models\Chatroom;
use App\Models\User;
use Database\Seeders\ChatroomSeeder;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Event;
use function Pest\Laravel\assertDatabaseHas;
test('newly registered user is greeted in chat room', function (): void {
User::factory()->system()->create();
$this->seed(ChatroomSeeder::class);
$user = User::factory()->create();
$bot = Bot::factory()->create([
'command' => 'Systembot',
]);
$chatroom = Chatroom::factory()->create([
'name' => config('chat.system_chatroom'),
]);
$user = User::factory()->create();
$chatroom = Chatroom::query()
->when(
\is_int(config('chat.system_chatroom')),
fn ($query) => $query->where('id', '=', config('chat.system_chatroom')),
fn ($query) => $query->where('name', '=', config('chat.system_chatroom')),
)
->sole();
Event::dispatch(new Registered($user));