From 081bd6ef8c00c40ba9bc5505fa2064e8b1d211b1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 26 May 2026 17:05:54 +0200 Subject: [PATCH] fix(seeding): ensure root user joins root team Create the root team before production seeding depends on it, reuse the existing root team when creating root users, and cover the production seeder flow with a feature test. --- app/Actions/Fortify/CreateNewUser.php | 6 ++- app/Models/User.php | 10 ++++ database/seeders/ProductionSeeder.php | 10 ++++ database/seeders/RootUserSeeder.php | 7 +++ tests/Feature/ProductionSeederTest.php | 68 ++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/ProductionSeederTest.php diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php index 7ea6a871e..cddf66389 100644 --- a/app/Actions/Fortify/CreateNewUser.php +++ b/app/Actions/Fortify/CreateNewUser.php @@ -2,6 +2,7 @@ namespace App\Actions\Fortify; +use App\Models\Team; use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; @@ -44,7 +45,10 @@ public function create(array $input): User 'password' => Hash::make($input['password']), ]); $user->save(); - $team = $user->teams()->first(); + $team = $user->teams()->first() ?? Team::find(0); + if ($team !== null && ! $user->teams()->where('team_id', $team->id)->exists()) { + $user->teams()->attach($team, ['role' => 'owner']); + } // Disable registration after first user is created $settings = instanceSettings(); diff --git a/app/Models/User.php b/app/Models/User.php index 237f3836f..990c34b0b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -98,8 +98,18 @@ protected static function boot() $team['id'] = 0; $team['name'] = 'Root Team'; } + $new_team = $user->id === 0 ? Team::find(0) : null; + + if ($new_team !== null) { + $new_team->forceFill($team); + $new_team->save(); + + return; + } + $new_team = (new Team)->forceFill($team); $new_team->save(); + $user->teams()->attach($new_team, ['role' => 'owner']); }); diff --git a/database/seeders/ProductionSeeder.php b/database/seeders/ProductionSeeder.php index 511af1a9f..4d492a297 100644 --- a/database/seeders/ProductionSeeder.php +++ b/database/seeders/ProductionSeeder.php @@ -32,6 +32,16 @@ public function run(): void echo " Running in self-hosted mode.\n"; } + if (Team::find(0) === null) { + (new Team)->forceFill([ + 'id' => 0, + 'name' => 'Root Team', + 'description' => 'The root team', + 'personal_team' => true, + 'show_boarding' => true, + ])->save(); + } + if (User::find(0) !== null && Team::find(0) !== null) { if (DB::table('team_user')->where('user_id', 0)->first() === null) { DB::table('team_user')->insert([ diff --git a/database/seeders/RootUserSeeder.php b/database/seeders/RootUserSeeder.php index c4e93af63..9bc93a9a9 100644 --- a/database/seeders/RootUserSeeder.php +++ b/database/seeders/RootUserSeeder.php @@ -3,6 +3,7 @@ namespace Database\Seeders; use App\Models\InstanceSettings; +use App\Models\Team; use App\Models\User; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Hash; @@ -52,6 +53,12 @@ public function run(): void 'password' => Hash::make(env('ROOT_USER_PASSWORD')), ]); $user->save(); + + $team = Team::find(0); + if ($team !== null && ! $user->teams()->where('team_id', 0)->exists()) { + $user->teams()->attach($team, ['role' => 'owner']); + } + echo "\n SUCCESS Root user created successfully.\n\n"; } catch (\Exception $e) { echo "\n ERROR Failed to create root user: {$e->getMessage()}\n\n"; diff --git a/tests/Feature/ProductionSeederTest.php b/tests/Feature/ProductionSeederTest.php new file mode 100644 index 000000000..03da9f952 --- /dev/null +++ b/tests/Feature/ProductionSeederTest.php @@ -0,0 +1,68 @@ + 'log', + 'constants.coolify.is_windows_docker_desktop' => true, + ]); + Queue::fake(); + StartProxy::shouldRun()->andReturn('OK'); + + Server::creating(function (Server $server) { + if ((int) $server->getKey() === 0) { + expect(Team::find(0))->not->toBeNull(); + } + }); + + Server::created(function (Server $server) { + SslCertificate::create([ + 'server_id' => $server->id, + 'common_name' => 'Coolify CA Certificate', + 'ssl_certificate' => 'certificate', + 'ssl_private_key' => 'private-key', + 'valid_until' => now()->addYear(), + 'is_ca_certificate' => true, + ]); + }); + + $this->seed(ProductionSeeder::class); + + $rootTeam = Team::find(0); + $localhostServer = Server::find(0); + + expect($rootTeam)->not->toBeNull() + ->and($localhostServer)->not->toBeNull() + ->and($localhostServer->team_id)->toBe(0); + + expect(SharedEnvironmentVariable::query() + ->where('type', 'server') + ->where('server_id', 0) + ->where('team_id', 0) + ->pluck('key') + ->all() + )->toContain('COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'); + + instanceSettings()->update(['is_registration_enabled' => true]); + + $rootUser = app(CreateNewUser::class)->create([ + 'name' => 'Root User', + 'email' => 'root@example.com', + 'password' => 'Password123!', + 'password_confirmation' => 'Password123!', + ]); + + expect(Team::whereKey(0)->count())->toBe(1) + ->and($rootUser->teams()->where('team_id', 0)->exists())->toBeTrue(); +});