mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 12:10:41 -05:00
Sync Admin Roles
Roles (or "ranks" as they're called on the hub) are now being pulled down into the local database.
This commit is contained in:
parent
47a7121707
commit
5e1c05f49d
@ -7,6 +7,7 @@ use App\Models\Mod;
|
||||
use App\Models\ModVersion;
|
||||
use App\Models\SptVersion;
|
||||
use App\Models\User;
|
||||
use App\Models\UserRole;
|
||||
use Carbon\Carbon;
|
||||
use CurlHandle;
|
||||
use Exception;
|
||||
@ -164,62 +165,50 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
protected function importUsers(): void
|
||||
{
|
||||
DB::connection('mysql_hub')
|
||||
->table('wcf1_user')
|
||||
->select('userID', 'username', 'email', 'password', 'registrationDate', 'banned', 'banReason', 'banExpires')
|
||||
->table('wcf1_user as u')
|
||||
->select('u.userID', 'u.username', 'u.email', 'u.password', 'u.registrationDate', 'u.banned', 'u.banReason', 'u.banExpires', 'u.rankID', 'r.rankTitle')
|
||||
->leftJoin('wcf1_user_rank as r', 'u.rankID', '=', 'r.rankID')
|
||||
->chunkById(250, function (Collection $users) {
|
||||
|
||||
$insertData = [];
|
||||
$bannedUsers = [];
|
||||
$userData = $bannedUsers = $userRanks = [];
|
||||
|
||||
foreach ($users as $user) {
|
||||
$insertData[] = [
|
||||
'hub_id' => (int) $user->userID,
|
||||
'name' => $user->username,
|
||||
'email' => mb_convert_case($user->email, MB_CASE_LOWER, 'UTF-8'),
|
||||
'password' => $this->cleanPasswordHash($user->password),
|
||||
'created_at' => $this->cleanRegistrationDate($user->registrationDate),
|
||||
'updated_at' => now('UTC')->toDateTimeString(),
|
||||
];
|
||||
$userData[] = $this->collectUserData($user);
|
||||
|
||||
// If the user is banned, add them to an array of banned users.
|
||||
if ($user->banned) {
|
||||
$bannedUsers[] = [
|
||||
'hub_id' => (int) $user->userID,
|
||||
'comment' => $user->banReason ?? '',
|
||||
'expired_at' => $this->cleanUnbannedAtDate($user->banExpires),
|
||||
];
|
||||
$bannedUserData = $this->collectBannedUserData($user);
|
||||
if ($bannedUserData) {
|
||||
$bannedUsers[] = $bannedUserData;
|
||||
}
|
||||
|
||||
$userRankData = $this->collectUserRankData($user);
|
||||
if ($userRankData) {
|
||||
$userRanks[] = $userRankData;
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($insertData)) {
|
||||
DB::table('users')->upsert($insertData, ['hub_id'], [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]);
|
||||
}
|
||||
|
||||
// Ban the users in the new local database.
|
||||
if (! empty($bannedUsers)) {
|
||||
foreach ($bannedUsers as $bannedUser) {
|
||||
$user = User::whereHubId($bannedUser['hub_id'])->first();
|
||||
$user->ban([
|
||||
'comment' => $bannedUser['comment'],
|
||||
'expired_at' => $bannedUser['expired_at'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->upsertUsers($userData);
|
||||
$this->handleBannedUsers($bannedUsers);
|
||||
$this->handleUserRoles($userRanks);
|
||||
}, 'userID');
|
||||
}
|
||||
|
||||
protected function collectUserData($user): array
|
||||
{
|
||||
return [
|
||||
'hub_id' => (int) $user->userID,
|
||||
'name' => $user->username,
|
||||
'email' => mb_convert_case($user->email, MB_CASE_LOWER, 'UTF-8'),
|
||||
'password' => $this->cleanPasswordHash($user->password),
|
||||
'created_at' => $this->cleanRegistrationDate($user->registrationDate),
|
||||
'updated_at' => now('UTC')->toDateTimeString(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the password hash from the Hub database.
|
||||
*/
|
||||
protected function cleanPasswordHash(string $password): string
|
||||
{
|
||||
// The hub passwords are hashed sometimes with a prefix of the hash type. We only want the hash.
|
||||
// The hub passwords sometimes hashed with a prefix of the hash type. We only want the hash.
|
||||
// If it's not Bcrypt, they'll have to reset their password. Tough luck.
|
||||
$clean = str_ireplace(['invalid:', 'bcrypt:', 'bcrypt::', 'cryptmd5:', 'cryptmd5::'], '', $password);
|
||||
|
||||
@ -242,6 +231,22 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
return $date->toDateTimeString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an array of banned user data ready to be inserted into the local database.
|
||||
*/
|
||||
protected function collectBannedUserData($user): ?array
|
||||
{
|
||||
if ($user->banned) {
|
||||
return [
|
||||
'hub_id' => (int) $user->userID,
|
||||
'comment' => $user->banReason ?? '',
|
||||
'expired_at' => $this->cleanUnbannedAtDate($user->banExpires),
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the banned_at date from the Hub database.
|
||||
*/
|
||||
@ -282,6 +287,98 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an array of user rank data ready to be inserted into the local database.
|
||||
*/
|
||||
protected function collectUserRankData($user): ?array
|
||||
{
|
||||
if ($user->rankID && $user->rankTitle) {
|
||||
return [
|
||||
'hub_id' => (int) $user->userID,
|
||||
'title' => $user->rankTitle,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update the users in the local database.
|
||||
*/
|
||||
protected function upsertUsers($usersData): void
|
||||
{
|
||||
if (! empty($usersData)) {
|
||||
DB::table('users')->upsert($usersData, ['hub_id'], [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the hub-banned users from the local database and ban them locally.
|
||||
*/
|
||||
protected function handleBannedUsers($bannedUsers): void
|
||||
{
|
||||
foreach ($bannedUsers as $bannedUser) {
|
||||
$user = User::whereHubId($bannedUser['hub_id'])->first();
|
||||
$user->ban([
|
||||
'comment' => $bannedUser['comment'],
|
||||
'expired_at' => $bannedUser['expired_at'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch or create the user ranks in the local database and assign them to the users.
|
||||
*/
|
||||
protected function handleUserRoles($userRanks): void
|
||||
{
|
||||
foreach ($userRanks as $userRank) {
|
||||
$roleName = Str::ucfirst(Str::afterLast($userRank['title'], '.'));
|
||||
$roleData = $this->buildUserRoleData($roleName);
|
||||
UserRole::upsert($roleData, ['name'], ['name', 'short_name', 'description', 'color_class']);
|
||||
|
||||
$userRole = UserRole::whereName($roleData['name'])->first();
|
||||
$user = User::whereHubId($userRank['hub_id'])->first();
|
||||
$user->assignRole($userRole);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the user role data based on the role name.
|
||||
*/
|
||||
protected function buildUserRoleData(string $name): array
|
||||
{
|
||||
if ($name === 'Administrator') {
|
||||
return [
|
||||
'name' => 'Administrator',
|
||||
'short_name' => 'Admin',
|
||||
'description' => 'An administrator has full access to the site.',
|
||||
'color_class' => 'sky',
|
||||
];
|
||||
}
|
||||
|
||||
if ($name === 'Moderator') {
|
||||
return [
|
||||
'name' => 'Moderator',
|
||||
'short_name' => 'Mod',
|
||||
'description' => 'A moderator has the ability to moderate user content.',
|
||||
'color_class' => 'emerald',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'short_name' => '',
|
||||
'description' => '',
|
||||
'color_class' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the licenses from the Hub database to the local database.
|
||||
*/
|
||||
@ -568,13 +665,13 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
*/
|
||||
public function failed(Exception $exception): void
|
||||
{
|
||||
// Drop the temporary tables.
|
||||
// Explicitly drop the temporary tables.
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values');
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_content');
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_version_labels');
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_version_content');
|
||||
|
||||
// This *should* drop the temporary tables as well, but I like to be sure.
|
||||
// Close the connections. This should drop the temporary tables as well, but I like to be explicit.
|
||||
DB::connection('mysql_hub')->disconnect();
|
||||
DB::disconnect();
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@ -58,6 +59,18 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return ! is_null($this->email_verified_at);
|
||||
}
|
||||
|
||||
public function assignRole(UserRole $role): bool
|
||||
{
|
||||
$this->role()->associate($role);
|
||||
|
||||
return $this->save();
|
||||
}
|
||||
|
||||
public function role(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UserRole::class, 'user_role_id');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
|
24
app/Models/UserRole.php
Normal file
24
app/Models/UserRole.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class UserRole extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'short_name',
|
||||
'description',
|
||||
'color_class',
|
||||
];
|
||||
|
||||
public function users(): HasMany
|
||||
{
|
||||
return $this->hasMany(User::class);
|
||||
}
|
||||
}
|
54
app/Nova/UserRoleResource.php
Normal file
54
app/Nova/UserRoleResource.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Nova;
|
||||
|
||||
use App\Models\UserRole;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Nova\Fields\ID;
|
||||
use Laravel\Nova\Fields\Text;
|
||||
|
||||
class UserRoleResource extends Resource
|
||||
{
|
||||
public static string $model = UserRole::class;
|
||||
|
||||
public static $title = 'name';
|
||||
|
||||
public static $search = [
|
||||
'id', 'name', 'description',
|
||||
];
|
||||
|
||||
public function fields(Request $request): array
|
||||
{
|
||||
return [
|
||||
ID::make()->sortable(),
|
||||
|
||||
Text::make('Name')
|
||||
->sortable()
|
||||
->rules('required'),
|
||||
|
||||
Text::make('Description')
|
||||
->sortable()
|
||||
->rules('required'),
|
||||
];
|
||||
}
|
||||
|
||||
public function cards(Request $request): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function filters(Request $request): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function lenses(Request $request): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function actions(Request $request): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -2,16 +2,10 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Jetstream\Features;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
@ -20,9 +14,7 @@ class UserFactory extends Factory
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
* Define the user's default state.
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
@ -34,13 +26,14 @@ class UserFactory extends Factory
|
||||
'two_factor_secret' => null,
|
||||
'two_factor_recovery_codes' => null,
|
||||
'remember_token' => Str::random(10),
|
||||
'user_role_id' => null,
|
||||
'profile_photo_path' => null,
|
||||
'current_team_id' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
* Indicate that the user's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
@ -48,25 +41,4 @@ class UserFactory extends Factory
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the user should have a personal team.
|
||||
*/
|
||||
public function withPersonalTeam(?callable $callback = null): static
|
||||
{
|
||||
if (! Features::hasTeamFeatures()) {
|
||||
return $this->state([]);
|
||||
}
|
||||
|
||||
return $this->has(
|
||||
Team::factory()
|
||||
->state(fn (array $attributes, User $user) => [
|
||||
'name' => $user->name.'\'s Team',
|
||||
'user_id' => $user->id,
|
||||
'personal_team' => true,
|
||||
])
|
||||
->when(is_callable($callback), $callback),
|
||||
'ownedTeams'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
50
database/factories/UserRoleFactory.php
Normal file
50
database/factories/UserRoleFactory.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\UserRole;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class UserRoleFactory extends Factory
|
||||
{
|
||||
protected $model = UserRole::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->name(),
|
||||
'short_name' => $this->faker->word(),
|
||||
'description' => $this->faker->text(),
|
||||
'color_class' => $this->faker->randomElement(['sky', 'red', 'green', 'emerald', 'lime']),
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "administrator" role.
|
||||
*/
|
||||
public function administrator(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'name' => 'Administrator',
|
||||
'short_name' => 'Admin',
|
||||
'description' => 'An administrator has full access to the site.',
|
||||
'color_class' => 'sky',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "moderator" role.
|
||||
*/
|
||||
public function moderator(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'name' => 'Moderator',
|
||||
'short_name' => 'Mod',
|
||||
'description' => 'A moderator has the ability to moderate user content.',
|
||||
'color_class' => 'emerald',
|
||||
]);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_roles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->unique();
|
||||
$table->string('short_name')->default('');
|
||||
$table->string('description')->default('');
|
||||
$table->string('color_class')->default('');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_roles');
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use App\Models\UserRole;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->foreignIdFor(UserRole::class)
|
||||
->nullable()
|
||||
->after('remember_token')
|
||||
->constrained()
|
||||
->cascadeOnUpdate()
|
||||
->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_role_id']);
|
||||
});
|
||||
}
|
||||
};
|
@ -7,6 +7,7 @@ use App\Models\Mod;
|
||||
use App\Models\ModVersion;
|
||||
use App\Models\SptVersion;
|
||||
use App\Models\User;
|
||||
use App\Models\UserRole;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
@ -22,6 +23,14 @@ class DatabaseSeeder extends Seeder
|
||||
// Create some code licenses.
|
||||
$licenses = License::factory(10)->create();
|
||||
|
||||
// Add 5 administrators.
|
||||
$administrator = UserRole::factory()->administrator()->create();
|
||||
User::factory(5)->create(['user_role_id' => $administrator->id]);
|
||||
|
||||
// Add 10 moderators.
|
||||
$moderator = UserRole::factory()->moderator()->create();
|
||||
User::factory(10)->create(['user_role_id' => $moderator->id]);
|
||||
|
||||
// Add 100 users.
|
||||
$users = User::factory(100)->create();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user