From e5ef197ed6388a12b0b2a79bc80314dbdd9a3380 Mon Sep 17 00:00:00 2001 From: w33b Date: Fri, 16 Jan 2026 23:14:47 +0100 Subject: [PATCH] Add user roles system --- app/Enums/UserRole.php | 11 ++++ app/Http/Controllers/Admin/UserController.php | 5 +- .../Auth/DiscordAuthController.php | 28 ++++------- app/Http/Kernel.php | 1 + app/Http/Middleware/IsAdmin.php | 35 ++++--------- app/Http/Middleware/IsBanned.php | 9 ++-- app/Http/Middleware/IsModerator.php | 29 +++++++++++ app/Livewire/AdminUserSearch.php | 9 ++-- app/Livewire/DownloadsSearch.php | 6 +-- app/Models/User.php | 45 ++++++++++++++++- config/discord.php | 14 +++--- ...026_01_11_184725_migrate_to_user_roles.php | 50 +++++++++++++++++++ resources/views/admin/home/alert.blade.php | 4 +- resources/views/admin/stream.blade.php | 2 +- resources/views/layouts/navigation.blade.php | 2 +- .../livewire/admin-user-search.blade.php | 25 ++++++---- resources/views/livewire/comment.blade.php | 4 +- .../partials/download-authorized.blade.php | 4 +- resources/views/partials/comment.blade.php | 4 +- resources/views/stream/index.blade.php | 2 +- .../views/user/partials/profile.blade.php | 2 +- 21 files changed, 206 insertions(+), 85 deletions(-) create mode 100644 app/Enums/UserRole.php create mode 100644 app/Http/Middleware/IsModerator.php create mode 100644 database/migrations/2026_01_11_184725_migrate_to_user_roles.php diff --git a/app/Enums/UserRole.php b/app/Enums/UserRole.php new file mode 100644 index 0000000..67da8d7 --- /dev/null +++ b/app/Enums/UserRole.php @@ -0,0 +1,11 @@ +update(['is_banned' => 1]); + $user->addRole(UserRole::BANNED); alert()->success('Banned', 'User has been banned.'); break; case 'unban': - $user->update(['is_banned' => 0]); + $user->removeRole(UserRole::BANNED); alert()->success('Unbanned', 'User has been unbanned.'); break; default: diff --git a/app/Http/Controllers/Auth/DiscordAuthController.php b/app/Http/Controllers/Auth/DiscordAuthController.php index 482f429..634ecce 100644 --- a/app/Http/Controllers/Auth/DiscordAuthController.php +++ b/app/Http/Controllers/Auth/DiscordAuthController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Auth; +use App\Enums\UserRole; use App\Models\User; use App\Http\Controllers\Controller; @@ -88,10 +89,7 @@ class DiscordAuthController extends Controller // User is not in the guild if ($response->status() === 404) { - $user->update([ - 'is_patreon' => false, - ]); - + $user->removeRole(UserRole::SUPPORTER); return; } @@ -110,21 +108,15 @@ class DiscordAuthController extends Controller $discordRoles = $response->json('roles', []); $patreonRoles = config('discord.patreon_roles', []); - $isPatreon = false; - foreach($patreonRoles as $patreonRole) - { - if (in_array($patreonRole, $discordRoles, true)) { - $isPatreon = true; - break; - } + // If intersect of array is empty, then the user doesn't have the role + $hasSupporterRole = !empty(array_intersect($discordRoles, $patreonRoles)); + + if (!$hasSupporterRole) { + // Remove role if not found + $user->removeRole(UserRole::SUPPORTER); + return; } - // Only update if something actually changed - if ($user->is_patreon !== $isPatreon) { - $user->update([ - 'is_patreon' => $isPatreon, - ]); - } + $user->addRole(UserRole::SUPPORTER); } - } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 360c802..8ac7594 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -59,6 +59,7 @@ class Kernel extends HttpKernel 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, 'auth.admin' => \App\Http\Middleware\IsAdmin::class, + 'auth.moderator' => \App\Http\Middleware\IsModerator::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, diff --git a/app/Http/Middleware/IsAdmin.php b/app/Http/Middleware/IsAdmin.php index c398ff0..690688d 100644 --- a/app/Http/Middleware/IsAdmin.php +++ b/app/Http/Middleware/IsAdmin.php @@ -1,28 +1,14 @@ auth = $auth; - } - /** * Handle an incoming request. * @@ -30,15 +16,14 @@ class IsAdmin { * @param \Closure $next * @return mixed */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): Response { - if( ! $this->auth->user()->is_admin) + if(Auth::check() && Auth::user()->hasRole(UserRole::ADMINISTRATOR)) { - session()->flash('error_msg','This resource is restricted to Administrators!'); - return redirect()->route('home.index'); + return $next($request); } - return $next($request); + session()->flash('error_msg','This resource is restricted to Administrators!'); + return redirect()->route('home.index'); } - } diff --git a/app/Http/Middleware/IsBanned.php b/app/Http/Middleware/IsBanned.php index 54e7fdf..d0ec686 100644 --- a/app/Http/Middleware/IsBanned.php +++ b/app/Http/Middleware/IsBanned.php @@ -1,8 +1,11 @@ check() && auth()->user()->is_banned == 1) + if(Auth::check() && Auth::user()->hasRole(UserRole::BANNED)) { Auth::logout(); $request->session()->invalidate(); diff --git a/app/Http/Middleware/IsModerator.php b/app/Http/Middleware/IsModerator.php new file mode 100644 index 0000000..e168141 --- /dev/null +++ b/app/Http/Middleware/IsModerator.php @@ -0,0 +1,29 @@ +hasRole(UserRole::MODERATOR)) + { + return $next($request); + } + + session()->flash('error_msg','This resource is restricted to Administrators!'); + return redirect()->route('home.index'); + } +} diff --git a/app/Livewire/AdminUserSearch.php b/app/Livewire/AdminUserSearch.php index 79e8663..16cfccd 100644 --- a/app/Livewire/AdminUserSearch.php +++ b/app/Livewire/AdminUserSearch.php @@ -2,6 +2,7 @@ namespace App\Livewire; +use App\Enums\UserRole; use App\Models\Comment; use App\Models\User; @@ -17,7 +18,7 @@ class AdminUserSearch extends Component public $search = ''; #[Url(history: true)] - public $filtered = ['true']; + public $discordId = ''; #[Url(history: true)] public $patreon = []; @@ -38,10 +39,10 @@ class AdminUserSearch extends Component public function render() { - $users = User::when($this->filtered !== [], fn ($query) => $query->where('id', '>=', 10000)) - ->when($this->patreon !== [], fn ($query) => $query->where('is_patreon', 1)) - ->when($this->banned !== [], fn ($query) => $query->where('is_banned', 1)) + $users = User::when($this->patreon !== [], fn ($query) => $query->whereJsonContains('roles', UserRole::SUPPORTER->value)) + ->when($this->banned !== [], fn ($query) => $query->whereJsonContains('roles', UserRole::BANNED->value)) ->when($this->search !== '', fn ($query) => $query->where('name', 'like', '%'.$this->search.'%')) + ->when($this->discordId !== '', fn ($query) => $query->where('discord_id', '=', $this->discordId)) ->paginate(20); return view('livewire.admin-user-search', [ diff --git a/app/Livewire/DownloadsSearch.php b/app/Livewire/DownloadsSearch.php index dbb9619..6de27e8 100644 --- a/app/Livewire/DownloadsSearch.php +++ b/app/Livewire/DownloadsSearch.php @@ -73,9 +73,9 @@ class DownloadsSearch extends Component $types[] = 'FHD'; } elseif ($label === 'FHD 48fps') { $types[] = 'FHDi'; - } elseif ($label === 'UHD' && auth()->user()->is_patreon) { + } elseif ($label === 'UHD' && auth()->user()->hasRole(\App\Enums\UserRole::SUPPORTER)) { $types[] = 'UHD'; - } elseif ($label === 'UHD 48fps' && auth()->user()->is_patreon) { + } elseif ($label === 'UHD 48fps' && auth()->user()->hasRole(\App\Enums\UserRole::SUPPORTER)) { $types[] = 'UHDi'; } } @@ -98,7 +98,7 @@ class DownloadsSearch extends Component public function mount() { - if (!auth()->user()->is_patreon) { + if (!auth()->user()->hasRole(\App\Enums\UserRole::SUPPORTER)) { return; } diff --git a/app/Models/User.php b/app/Models/User.php index d9de512..a78ddf1 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -3,6 +3,8 @@ namespace App\Models; //use Illuminate\Contracts\Auth\MustVerifyEmail; + +use App\Enums\UserRole; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; @@ -25,7 +27,6 @@ class User extends Authenticatable 'email', 'password', 'locale', - 'is_banned', // Discord 'discord_id', 'discord_avatar', @@ -54,7 +55,7 @@ class User extends Authenticatable 'name' => 'string', 'email' => 'string', 'locale' => 'string', - 'roles' => 'json', + 'roles' => 'array', 'tag_blacklist' => 'array', // Discord 'discord_id' => 'integer', @@ -119,4 +120,44 @@ class User extends Authenticatable return asset('images/default-avatar.webp'); } + + /** + * Check if user has a specific role + */ + public function hasRole(UserRole $role): bool + { + return in_array($role->value, $this->roles ?? [], true); + } + + /** + * Add Role to User + */ + public function addRole(UserRole $role): void + { + if ($this->hasRole($role)) { + return; + } + + // Get all current roles + $roles = $this->roles ?? []; + + // Add new role + $roles[] = $role->value; + + $this->roles = $roles; + $this->save(); + } + + /** + * Remove Role from User + */ + public function removeRole(UserRole $role): void + { + if (!$this->hasRole($role)) { + return; + } + + $this->roles = array_diff($this->roles, [$role->value]); + $this->save(); + } } diff --git a/config/discord.php b/config/discord.php index bac3bad..091023a 100644 --- a/config/discord.php +++ b/config/discord.php @@ -6,13 +6,13 @@ return [ 'guild_id' => 802233383710228550, 'patreon_roles' => [ - 841798154999169054, // ???? - 803329707650187364, // Tier-5 - 803327903659196416, // ???? - 803325441942356059, // Tier-3 - 803322725576736858, // Tier-2 - 802270568912519198, // Tier-1 - 802234830384267315 // admin + '841798154999169054', // ???? + '803329707650187364', // Tier-5 + '803327903659196416', // ???? + '803325441942356059', // Tier-3 + '803322725576736858', // Tier-2 + '802270568912519198', // Tier-1 + '802234830384267315' // admin ], 'discord_bot_token' => env('DISCORD_BOT_TOKEN'), diff --git a/database/migrations/2026_01_11_184725_migrate_to_user_roles.php b/database/migrations/2026_01_11_184725_migrate_to_user_roles.php new file mode 100644 index 0000000..b9721dc --- /dev/null +++ b/database/migrations/2026_01_11_184725_migrate_to_user_roles.php @@ -0,0 +1,50 @@ +where('is_patreon', 1)->update([ + 'roles' => DB::raw("JSON_ARRAY('supporter')") + ]); + + // Migrate banned + DB::table('users')->where('is_banned', 1)->update([ + 'roles' => DB::raw("JSON_ARRAY('banned')") + ]); + + // Migrate admins + DB::table('users')->where('is_admin', 1)->update([ + 'roles' => DB::raw("JSON_ARRAY('admin')") + ]); + + // Drop columns + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('is_admin'); + $table->dropColumn('is_patreon'); + $table->dropColumn('is_banned'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::table('users')->update(['roles' => null]); + Schema::table('users', function (Blueprint $table) { + $table->boolean('is_admin')->default(0); + $table->boolean('is_patreon')->default(0); + $table->boolean('is_banned')->default(0); + }); + } +}; diff --git a/resources/views/admin/home/alert.blade.php b/resources/views/admin/home/alert.blade.php index b7c8285..a7af263 100644 --- a/resources/views/admin/home/alert.blade.php +++ b/resources/views/admin/home/alert.blade.php @@ -6,7 +6,7 @@