diff --git a/app/Actions/Fortify/ResetUserPassword.php b/app/Actions/Fortify/ResetUserPassword.php index 158996c90..5baa8b7ed 100644 --- a/app/Actions/Fortify/ResetUserPassword.php +++ b/app/Actions/Fortify/ResetUserPassword.php @@ -21,7 +21,7 @@ public function reset(User $user, array $input): void 'password' => ['required', Password::defaults(), 'confirmed'], ])->validate(); - $user->forceFill([ + $user->fill([ 'password' => Hash::make($input['password']), ])->save(); $user->deleteAllSessions(); diff --git a/app/Actions/Fortify/UpdateUserPassword.php b/app/Actions/Fortify/UpdateUserPassword.php index 0c51ec56d..320eede0b 100644 --- a/app/Actions/Fortify/UpdateUserPassword.php +++ b/app/Actions/Fortify/UpdateUserPassword.php @@ -24,7 +24,7 @@ public function update(User $user, array $input): void 'current_password.current_password' => __('The provided password does not match your current password.'), ])->validateWithBag('updatePassword'); - $user->forceFill([ + $user->fill([ 'password' => Hash::make($input['password']), ])->save(); } diff --git a/app/Actions/Fortify/UpdateUserProfileInformation.php b/app/Actions/Fortify/UpdateUserProfileInformation.php index c8bfd930a..76c6c0736 100644 --- a/app/Actions/Fortify/UpdateUserProfileInformation.php +++ b/app/Actions/Fortify/UpdateUserProfileInformation.php @@ -35,7 +35,7 @@ public function update(User $user, array $input): void ) { $this->updateVerifiedUser($user, $input); } else { - $user->forceFill([ + $user->fill([ 'name' => $input['name'], 'email' => $input['email'], ])->save(); @@ -49,7 +49,7 @@ public function update(User $user, array $input): void */ protected function updateVerifiedUser(User $user, array $input): void { - $user->forceFill([ + $user->fill([ 'name' => $input['name'], 'email' => $input['email'], 'email_verified_at' => null, diff --git a/app/Actions/Server/InstallDocker.php b/app/Actions/Server/InstallDocker.php index 8bb85c7fc..2e08ec6ad 100644 --- a/app/Actions/Server/InstallDocker.php +++ b/app/Actions/Server/InstallDocker.php @@ -49,7 +49,7 @@ public function handle(Server $server) }'); $found = StandaloneDocker::where('server_id', $server->id); if ($found->count() == 0 && $server->id) { - StandaloneDocker::forceCreate([ + StandaloneDocker::create([ 'name' => 'coolify', 'network' => 'coolify', 'server_id' => $server->id, diff --git a/app/Console/Commands/Emails.php b/app/Console/Commands/Emails.php index 462155142..43ba06804 100644 --- a/app/Console/Commands/Emails.php +++ b/app/Console/Commands/Emails.php @@ -136,7 +136,7 @@ public function handle() $application = Application::all()->first(); $preview = ApplicationPreview::all()->first(); if (! $preview) { - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'application_id' => $application->id, 'pull_request_id' => 1, 'pull_request_html_url' => 'http://example.com', diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index c8638be0d..ec2e300ff 100644 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -258,7 +258,7 @@ public function create_project(Request $request) ], 422); } - $project = Project::forceCreate([ + $project = Project::create([ 'name' => $request->name, 'description' => $request->description, 'team_id' => $teamId, diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 6a742fe1b..fbf4b9e56 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -432,7 +432,7 @@ public function create_service(Request $request) if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) { data_set($servicePayload, 'connect_to_docker_network', true); } - $service = Service::forceCreate($servicePayload); + $service = Service::create($servicePayload); $service->name = $request->name ?? "$oneClickServiceName-".$service->uuid; $service->description = $request->description; if ($request->has('is_container_label_escape_enabled')) { diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php index e59bc6ead..183186711 100644 --- a/app/Http/Controllers/Webhook/Bitbucket.php +++ b/app/Http/Controllers/Webhook/Bitbucket.php @@ -119,7 +119,7 @@ public function manual(Request $request) $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found) { if ($application->build_pack === 'dockercompose') { - $pr_app = ApplicationPreview::forceCreate([ + $pr_app = ApplicationPreview::create([ 'git_type' => 'bitbucket', 'application_id' => $application->id, 'pull_request_id' => $pull_request_id, @@ -128,7 +128,7 @@ public function manual(Request $request) ]); $pr_app->generate_preview_fqdn_compose(); } else { - $pr_app = ApplicationPreview::forceCreate([ + $pr_app = ApplicationPreview::create([ 'git_type' => 'bitbucket', 'application_id' => $application->id, 'pull_request_id' => $pull_request_id, diff --git a/app/Http/Controllers/Webhook/Gitea.php b/app/Http/Controllers/Webhook/Gitea.php index 6ba4b33cf..a9d65eae6 100644 --- a/app/Http/Controllers/Webhook/Gitea.php +++ b/app/Http/Controllers/Webhook/Gitea.php @@ -144,7 +144,7 @@ public function manual(Request $request) $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found) { if ($application->build_pack === 'dockercompose') { - $pr_app = ApplicationPreview::forceCreate([ + $pr_app = ApplicationPreview::create([ 'git_type' => 'gitea', 'application_id' => $application->id, 'pull_request_id' => $pull_request_id, @@ -153,7 +153,7 @@ public function manual(Request $request) ]); $pr_app->generate_preview_fqdn_compose(); } else { - $pr_app = ApplicationPreview::forceCreate([ + $pr_app = ApplicationPreview::create([ 'git_type' => 'gitea', 'application_id' => $application->id, 'pull_request_id' => $pull_request_id, diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index fe4f17d9e..08e5d7162 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -177,7 +177,7 @@ public function manual(Request $request) $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found) { if ($application->build_pack === 'dockercompose') { - $pr_app = ApplicationPreview::forceCreate([ + $pr_app = ApplicationPreview::create([ 'git_type' => 'gitlab', 'application_id' => $application->id, 'pull_request_id' => $pull_request_id, @@ -186,7 +186,7 @@ public function manual(Request $request) ]); $pr_app->generate_preview_fqdn_compose(); } else { - $pr_app = ApplicationPreview::forceCreate([ + $pr_app = ApplicationPreview::create([ 'git_type' => 'gitlab', 'application_id' => $application->id, 'pull_request_id' => $pull_request_id, diff --git a/app/Jobs/ProcessGithubPullRequestWebhook.php b/app/Jobs/ProcessGithubPullRequestWebhook.php index 01a512439..041cd812c 100644 --- a/app/Jobs/ProcessGithubPullRequestWebhook.php +++ b/app/Jobs/ProcessGithubPullRequestWebhook.php @@ -118,7 +118,7 @@ private function handleOpenAction(Application $application, ?GithubApp $githubAp if (! $found) { if ($application->build_pack === 'dockercompose') { - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'git_type' => 'github', 'application_id' => $application->id, 'pull_request_id' => $this->pullRequestId, @@ -127,7 +127,7 @@ private function handleOpenAction(Application $application, ?GithubApp $githubAp ]); $preview->generate_preview_fqdn_compose(); } else { - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'git_type' => 'github', 'application_id' => $application->id, 'pull_request_id' => $this->pullRequestId, diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index 170f0cdea..33c75bf70 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -441,7 +441,7 @@ public function selectExistingProject() public function createNewProject() { - $this->createdProject = Project::forceCreate([ + $this->createdProject = Project::create([ 'name' => 'My first project', 'team_id' => currentTeam()->id, 'uuid' => (string) new Cuid2, diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php index 141235590..6f9b6f995 100644 --- a/app/Livewire/Destination/New/Docker.php +++ b/app/Livewire/Destination/New/Docker.php @@ -77,7 +77,7 @@ public function submit() if ($found) { throw new \Exception('Network already added to this server.'); } else { - $docker = SwarmDocker::forceCreate([ + $docker = SwarmDocker::create([ 'name' => $this->name, 'network' => $this->network, 'server_id' => $this->selectedServer->id, @@ -88,7 +88,7 @@ public function submit() if ($found) { throw new \Exception('Network already added to this server.'); } else { - $docker = StandaloneDocker::forceCreate([ + $docker = StandaloneDocker::create([ 'name' => $this->name, 'network' => $this->network, 'server_id' => $this->selectedServer->id, diff --git a/app/Livewire/ForcePasswordReset.php b/app/Livewire/ForcePasswordReset.php index 61a2a20e9..e6392497f 100644 --- a/app/Livewire/ForcePasswordReset.php +++ b/app/Livewire/ForcePasswordReset.php @@ -48,7 +48,7 @@ public function submit() $this->rateLimit(10); $this->validate(); $firstLogin = auth()->user()->created_at == auth()->user()->updated_at; - auth()->user()->forceFill([ + auth()->user()->fill([ 'password' => Hash::make($this->password), 'force_password_reset' => false, ])->save(); diff --git a/app/Livewire/Project/AddEmpty.php b/app/Livewire/Project/AddEmpty.php index a2581a5c9..974f0608a 100644 --- a/app/Livewire/Project/AddEmpty.php +++ b/app/Livewire/Project/AddEmpty.php @@ -30,7 +30,7 @@ public function submit() { try { $this->validate(); - $project = Project::forceCreate([ + $project = Project::create([ 'name' => $this->name, 'description' => $this->description, 'team_id' => currentTeam()->id, diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index c61a4e4a7..c887e9b83 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -196,7 +196,7 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null, $this->setDeploymentUuid(); $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found && ! is_null($pull_request_html_url)) { - $found = ApplicationPreview::forceCreate([ + $found = ApplicationPreview::create([ 'application_id' => $this->application->id, 'pull_request_id' => $pull_request_id, 'pull_request_html_url' => $pull_request_html_url, @@ -210,7 +210,7 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null, $this->setDeploymentUuid(); $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found && (! is_null($pull_request_html_url) || ($this->application->build_pack === 'dockerimage' && str($docker_registry_image_tag)->isNotEmpty()))) { - $found = ApplicationPreview::forceCreate([ + $found = ApplicationPreview::create([ 'application_id' => $this->application->id, 'pull_request_id' => $pull_request_id, 'pull_request_html_url' => $pull_request_html_url ?? '', @@ -262,7 +262,7 @@ public function deploy(int $pull_request_id, ?string $pull_request_html_url = nu $this->setDeploymentUuid(); $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found && (! is_null($pull_request_html_url) || ($this->application->build_pack === 'dockerimage' && str($docker_registry_image_tag)->isNotEmpty()))) { - $found = ApplicationPreview::forceCreate([ + $found = ApplicationPreview::create([ 'application_id' => $this->application->id, 'pull_request_id' => $pull_request_id, 'pull_request_html_url' => $pull_request_html_url ?? '', diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php index 93eb2a78c..644753c83 100644 --- a/app/Livewire/Project/CloneMe.php +++ b/app/Livewire/Project/CloneMe.php @@ -100,7 +100,7 @@ public function clone(string $type) if ($foundProject) { throw new \Exception('Project with the same name already exists.'); } - $project = Project::forceCreate([ + $project = Project::create([ 'name' => $this->newName, 'team_id' => currentTeam()->id, 'description' => $this->project->description.' (clone)', @@ -139,7 +139,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'status' => 'exited', 'started_at' => null, @@ -188,7 +188,7 @@ public function clone(string $type) 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $newDatabase->id, ]); @@ -217,7 +217,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resource_id' => $newDatabase->id, ]); $newStorage->save(); @@ -230,7 +230,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'database_id' => $newDatabase->id, 'database_type' => $newDatabase->getMorphClass(), @@ -248,7 +248,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill($payload); + ])->fill($payload); $newEnvironmentVariable->save(); } } @@ -259,7 +259,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'environment_id' => $environment->id, 'destination_id' => $this->selectedDestination, @@ -277,7 +277,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => (string) new Cuid2, 'service_id' => $newService->id, 'team_id' => currentTeam()->id, @@ -291,7 +291,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resourceable_id' => $newService->id, 'resourceable_type' => $newService->getMorphClass(), ]); @@ -299,7 +299,7 @@ public function clone(string $type) } foreach ($newService->applications() as $application) { - $application->forceFill([ + $application->fill([ 'status' => 'exited', ])->save(); @@ -317,7 +317,7 @@ public function clone(string $type) 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $application->id, ]); @@ -346,7 +346,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resource_id' => $application->id, ]); $newStorage->save(); @@ -354,7 +354,7 @@ public function clone(string $type) } foreach ($newService->databases() as $database) { - $database->forceFill([ + $database->fill([ 'status' => 'exited', ])->save(); @@ -372,7 +372,7 @@ public function clone(string $type) 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $database->id, ]); @@ -401,7 +401,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resource_id' => $database->id, ]); $newStorage->save(); @@ -414,7 +414,7 @@ public function clone(string $type) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'database_id' => $database->id, 'database_type' => $database->getMorphClass(), diff --git a/app/Livewire/Project/New/DockerCompose.php b/app/Livewire/Project/New/DockerCompose.php index 99fb2efc4..2b92902c6 100644 --- a/app/Livewire/Project/New/DockerCompose.php +++ b/app/Livewire/Project/New/DockerCompose.php @@ -54,7 +54,7 @@ public function submit() } $destination_class = $destination->getMorphClass(); - $service = Service::forceCreate([ + $service = Service::create([ 'docker_compose_raw' => $this->dockerComposeRaw, 'environment_id' => $environment->id, 'server_id' => (int) $server_id, diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index 8becdf585..268333d07 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -133,7 +133,7 @@ public function submit() // Determine the image tag based on whether it's a hash or regular tag $imageTag = $parser->isImageHash() ? 'sha256-'.$parser->getTag() : $parser->getTag(); - $application = Application::forceCreate([ + $application = Application::create([ 'name' => 'docker-image-'.new Cuid2, 'repository_project_id' => 0, 'git_repository' => 'coollabsio/coolify', diff --git a/app/Livewire/Project/New/EmptyProject.php b/app/Livewire/Project/New/EmptyProject.php index 1cdc7e098..0360365a9 100644 --- a/app/Livewire/Project/New/EmptyProject.php +++ b/app/Livewire/Project/New/EmptyProject.php @@ -10,7 +10,7 @@ class EmptyProject extends Component { public function createEmptyProject() { - $project = Project::forceCreate([ + $project = Project::create([ 'name' => generate_random_name(), 'team_id' => currentTeam()->id, 'uuid' => (string) new Cuid2, diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index 9d4acb9bb..0222008b0 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -191,7 +191,7 @@ public function submit() $project = Project::ownedByCurrentTeam()->where('uuid', $this->parameters['project_uuid'])->firstOrFail(); $environment = $project->environments()->where('uuid', $this->parameters['environment_uuid'])->firstOrFail(); - $application = Application::forceCreate([ + $application = Application::create([ 'name' => generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name), 'repository_project_id' => $this->selected_repository_id, 'git_repository' => str($this->selected_repository_owner)->trim()->toString().'/'.str($this->selected_repository_repo)->trim()->toString(), diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index ba058c6ff..f8642d6fc 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -183,7 +183,7 @@ public function submit() $application_init['docker_compose_location'] = $this->docker_compose_location; $application_init['base_directory'] = $this->base_directory; } - $application = Application::forceCreate($application_init); + $application = Application::create($application_init); $application->settings->is_static = $this->is_static; $application->settings->save(); diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 6bd71d246..62ac7ec0d 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -299,7 +299,7 @@ public function submit() $new_service['source_id'] = $this->git_source->id; $new_service['source_type'] = $this->git_source->getMorphClass(); } - $service = Service::forceCreate($new_service); + $service = Service::create($new_service); return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, @@ -346,7 +346,7 @@ public function submit() $application_init['docker_compose_location'] = $this->docker_compose_location; $application_init['base_directory'] = $this->base_directory; } - $application = Application::forceCreate($application_init); + $application = Application::create($application_init); $application->settings->is_static = $this->isStatic; $application->settings->save(); diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php index 400b58fea..1073157e6 100644 --- a/app/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Livewire/Project/New/SimpleDockerfile.php @@ -52,7 +52,7 @@ public function submit() if (! $port) { $port = 80; } - $application = Application::forceCreate([ + $application = Application::create([ 'name' => 'dockerfile-'.new Cuid2, 'repository_project_id' => 0, 'git_repository' => 'coollabsio/coolify', diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php index dbe56b079..966c66a14 100644 --- a/app/Livewire/Project/Resource/Create.php +++ b/app/Livewire/Project/Resource/Create.php @@ -91,7 +91,7 @@ public function mount() if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) { data_set($service_payload, 'connect_to_docker_network', true); } - $service = Service::forceCreate($service_payload); + $service = Service::create($service_payload); $service->name = "$oneClickServiceName-".$service->uuid; $service->save(); if ($oneClickDotEnvs?->count() > 0) { diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php index 301c51be9..f4813dd4c 100644 --- a/app/Livewire/Project/Shared/ResourceOperations.php +++ b/app/Livewire/Project/Shared/ResourceOperations.php @@ -94,7 +94,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'name' => $this->resource->name.'-clone-'.$uuid, 'status' => 'exited', @@ -143,7 +143,7 @@ public function cloneTo($destination_id) 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $new_resource->id, ]); @@ -172,7 +172,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resource_id' => $new_resource->id, ]); $newStorage->save(); @@ -185,7 +185,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'database_id' => $new_resource->id, 'database_type' => $new_resource->getMorphClass(), @@ -204,7 +204,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill($payload); + ])->fill($payload); $newEnvironmentVariable->save(); } @@ -221,7 +221,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => $uuid, 'name' => $this->resource->name.'-clone-'.$uuid, 'destination_id' => $new_destination->id, @@ -242,7 +242,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => (string) new Cuid2, 'service_id' => $new_resource->id, 'team_id' => currentTeam()->id, @@ -256,7 +256,7 @@ public function cloneTo($destination_id) 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resourceable_id' => $new_resource->id, 'resourceable_type' => $new_resource->getMorphClass(), ]); @@ -264,7 +264,7 @@ public function cloneTo($destination_id) } foreach ($new_resource->applications() as $application) { - $application->forceFill([ + $application->fill([ 'status' => 'exited', ])->save(); @@ -282,7 +282,7 @@ public function cloneTo($destination_id) 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $application->id, ]); @@ -307,7 +307,7 @@ public function cloneTo($destination_id) } foreach ($new_resource->databases() as $database) { - $database->forceFill([ + $database->fill([ 'status' => 'exited', ])->save(); @@ -325,7 +325,7 @@ public function cloneTo($destination_id) 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $database->id, ]); @@ -366,7 +366,7 @@ public function moveTo($environment_id) try { $this->authorize('update', $this->resource); $new_environment = Environment::ownedByCurrentTeam()->findOrFail($environment_id); - $this->resource->forceFill([ + $this->resource->fill([ 'environment_id' => $environment_id, ])->save(); if ($this->resource->type() === 'application') { diff --git a/app/Livewire/Project/Show.php b/app/Livewire/Project/Show.php index b9628dd0d..e884abb4e 100644 --- a/app/Livewire/Project/Show.php +++ b/app/Livewire/Project/Show.php @@ -42,7 +42,7 @@ public function submit() { try { $this->validate(); - $environment = Environment::forceCreate([ + $environment = Environment::create([ 'name' => $this->name, 'project_id' => $this->project->id, 'uuid' => (string) new Cuid2, diff --git a/app/Livewire/Server/Destinations.php b/app/Livewire/Server/Destinations.php index f41ca00f3..117b43ad6 100644 --- a/app/Livewire/Server/Destinations.php +++ b/app/Livewire/Server/Destinations.php @@ -43,7 +43,7 @@ public function add($name) return; } else { - SwarmDocker::forceCreate([ + SwarmDocker::create([ 'name' => $this->server->name.'-'.$name, 'network' => $this->name, 'server_id' => $this->server->id, @@ -57,7 +57,7 @@ public function add($name) return; } else { - StandaloneDocker::forceCreate([ + StandaloneDocker::create([ 'name' => $this->server->name.'-'.$name, 'network' => $name, 'server_id' => $this->server->id, diff --git a/app/Models/Application.php b/app/Models/Application.php index bdc76eb33..fef6f6e4c 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -203,6 +203,14 @@ class Application extends BaseModel 'restart_count', 'last_restart_at', 'last_restart_type', + 'uuid', + 'environment_id', + 'destination_id', + 'destination_type', + 'source_id', + 'source_type', + 'repository_project_id', + 'private_key_id', ]; protected $appends = ['server_status']; @@ -262,7 +270,7 @@ protected static function booted() } } if (count($payload) > 0) { - $application->forceFill($payload); + $application->fill($payload); } // Buildpack switching cleanup logic @@ -299,7 +307,7 @@ protected static function booted() } }); static::created(function ($application) { - ApplicationSetting::forceCreate([ + ApplicationSetting::create([ 'application_id' => $application->id, ]); $application->compose_parsing_version = self::$parserVersion; diff --git a/app/Models/ApplicationDeploymentQueue.php b/app/Models/ApplicationDeploymentQueue.php index 21cb58abe..67f28523c 100644 --- a/app/Models/ApplicationDeploymentQueue.php +++ b/app/Models/ApplicationDeploymentQueue.php @@ -44,6 +44,7 @@ class ApplicationDeploymentQueue extends Model 'application_id', 'deployment_uuid', 'pull_request_id', + 'docker_registry_image_tag', 'force_rebuild', 'commit', 'status', diff --git a/app/Models/ApplicationPreview.php b/app/Models/ApplicationPreview.php index 818f96d8e..f08a48cea 100644 --- a/app/Models/ApplicationPreview.php +++ b/app/Models/ApplicationPreview.php @@ -11,6 +11,7 @@ class ApplicationPreview extends BaseModel use SoftDeletes; protected $fillable = [ + 'uuid', 'application_id', 'pull_request_id', 'pull_request_html_url', @@ -62,7 +63,7 @@ protected static function booted() }); static::saving(function ($preview) { if ($preview->isDirty('status')) { - $preview->forceFill(['last_online_at' => now()]); + $preview->last_online_at = now(); } }); } diff --git a/app/Models/ApplicationSetting.php b/app/Models/ApplicationSetting.php index 24b35df7f..731a9b5da 100644 --- a/app/Models/ApplicationSetting.php +++ b/app/Models/ApplicationSetting.php @@ -29,6 +29,7 @@ class ApplicationSetting extends Model ]; protected $fillable = [ + 'application_id', 'is_static', 'is_git_submodules_enabled', 'is_git_lfs_enabled', diff --git a/app/Models/CloudProviderToken.php b/app/Models/CloudProviderToken.php index 123376c9b..026d11fba 100644 --- a/app/Models/CloudProviderToken.php +++ b/app/Models/CloudProviderToken.php @@ -5,6 +5,7 @@ class CloudProviderToken extends BaseModel { protected $fillable = [ + 'team_id', 'provider', 'token', 'name', diff --git a/app/Models/Environment.php b/app/Models/Environment.php index 55ce93265..65ffaf579 100644 --- a/app/Models/Environment.php +++ b/app/Models/Environment.php @@ -28,6 +28,8 @@ class Environment extends BaseModel protected $fillable = [ 'name', 'description', + 'project_id', + 'uuid', ]; protected static function booted() diff --git a/app/Models/GithubApp.php b/app/Models/GithubApp.php index 3cffeb8f8..54bbb3f7d 100644 --- a/app/Models/GithubApp.php +++ b/app/Models/GithubApp.php @@ -7,6 +7,8 @@ class GithubApp extends BaseModel { protected $fillable = [ + 'team_id', + 'private_key_id', 'name', 'organization', 'api_url', diff --git a/app/Models/Project.php b/app/Models/Project.php index ff2cae041..15628892e 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -27,6 +27,8 @@ class Project extends BaseModel protected $fillable = [ 'name', 'description', + 'team_id', + 'uuid', ]; /** @@ -51,10 +53,10 @@ public static function ownedByCurrentTeamCached() protected static function booted() { static::created(function ($project) { - ProjectSetting::forceCreate([ + ProjectSetting::create([ 'project_id' => $project->id, ]); - Environment::forceCreate([ + Environment::create([ 'name' => 'production', 'project_id' => $project->id, 'uuid' => (string) new Cuid2, diff --git a/app/Models/ProjectSetting.php b/app/Models/ProjectSetting.php index 7ea17ba7a..8b59ffac6 100644 --- a/app/Models/ProjectSetting.php +++ b/app/Models/ProjectSetting.php @@ -6,7 +6,9 @@ class ProjectSetting extends Model { - protected $fillable = []; + protected $fillable = [ + 'project_id', + ]; public function project() { diff --git a/app/Models/ScheduledDatabaseBackup.php b/app/Models/ScheduledDatabaseBackup.php index c6aed863d..6308bae8b 100644 --- a/app/Models/ScheduledDatabaseBackup.php +++ b/app/Models/ScheduledDatabaseBackup.php @@ -9,6 +9,8 @@ class ScheduledDatabaseBackup extends BaseModel { protected $fillable = [ + 'uuid', + 'team_id', 'description', 'enabled', 'save_s3', diff --git a/app/Models/ScheduledDatabaseBackupExecution.php b/app/Models/ScheduledDatabaseBackupExecution.php index f1f6e88b5..51ad46de9 100644 --- a/app/Models/ScheduledDatabaseBackupExecution.php +++ b/app/Models/ScheduledDatabaseBackupExecution.php @@ -7,6 +7,8 @@ class ScheduledDatabaseBackupExecution extends BaseModel { protected $fillable = [ + 'uuid', + 'scheduled_database_backup_id', 'status', 'message', 'size', diff --git a/app/Models/ScheduledTask.php b/app/Models/ScheduledTask.php index e76f1b7b9..40f8e1860 100644 --- a/app/Models/ScheduledTask.php +++ b/app/Models/ScheduledTask.php @@ -30,12 +30,16 @@ class ScheduledTask extends BaseModel use HasSafeStringAttribute; protected $fillable = [ + 'uuid', 'enabled', 'name', 'command', 'frequency', 'container', 'timeout', + 'team_id', + 'application_id', + 'service_id', ]; public static function ownedByCurrentTeamAPI(int $teamId) diff --git a/app/Models/ScheduledTaskExecution.php b/app/Models/ScheduledTaskExecution.php index dd74ba2e0..1e26c7be3 100644 --- a/app/Models/ScheduledTaskExecution.php +++ b/app/Models/ScheduledTaskExecution.php @@ -23,6 +23,7 @@ class ScheduledTaskExecution extends BaseModel { protected $fillable = [ + 'scheduled_task_id', 'status', 'message', 'finished_at', diff --git a/app/Models/Server.php b/app/Models/Server.php index 427896a19..a18fe14ae 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -135,7 +135,7 @@ protected static function booted() $payload['ip_previous'] = $server->getOriginal('ip'); } } - $server->forceFill($payload); + $server->fill($payload); }); static::saved(function ($server) { if ($server->wasChanged('private_key_id') || $server->privateKey?->isDirty()) { @@ -265,6 +265,7 @@ public static function flushIdentityMap(): void 'detected_traefik_version', 'traefik_outdated_info', 'server_metadata', + 'ip_previous', ]; use HasSafeStringAttribute; diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index d34f2c86b..30fc1e165 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -54,6 +54,7 @@ class ServerSetting extends Model { protected $fillable = [ + 'server_id', 'is_swarm_manager', 'is_jump_server', 'is_build_server', diff --git a/app/Models/Service.php b/app/Models/Service.php index 491924c49..11189b4ac 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -49,6 +49,7 @@ class Service extends BaseModel private static $parserVersion = '5'; protected $fillable = [ + 'uuid', 'name', 'description', 'docker_compose_raw', @@ -58,6 +59,10 @@ class Service extends BaseModel 'config_hash', 'compose_parsing_version', 'is_container_label_escape_enabled', + 'environment_id', + 'server_id', + 'destination_id', + 'destination_type', ]; protected $appends = ['server_status', 'status']; diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index e608c202d..6bf12f4e7 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -12,6 +12,7 @@ class ServiceApplication extends BaseModel use HasFactory, SoftDeletes; protected $fillable = [ + 'service_id', 'name', 'human_name', 'description', @@ -39,7 +40,7 @@ protected static function booted() }); static::saving(function ($service) { if ($service->isDirty('status')) { - $service->forceFill(['last_online_at' => now()]); + $service->last_online_at = now(); } }); } diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index e5b28d929..69801f985 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -10,6 +10,7 @@ class ServiceDatabase extends BaseModel use HasFactory, SoftDeletes; protected $fillable = [ + 'service_id', 'name', 'human_name', 'description', @@ -44,7 +45,7 @@ protected static function booted() }); static::saving(function ($service) { if ($service->isDirty('status')) { - $service->forceFill(['last_online_at' => now()]); + $service->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index c6d91dd55..784e2c937 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -14,6 +14,7 @@ class StandaloneClickhouse extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'clickhouse_admin_user', @@ -40,6 +41,9 @@ class StandaloneClickhouse extends BaseModel 'public_port_timeout', 'custom_docker_run_options', 'clickhouse_db', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -71,7 +75,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneDocker.php b/app/Models/StandaloneDocker.php index 09dae022b..dcb349405 100644 --- a/app/Models/StandaloneDocker.php +++ b/app/Models/StandaloneDocker.php @@ -13,6 +13,7 @@ class StandaloneDocker extends BaseModel use HasSafeStringAttribute; protected $fillable = [ + 'server_id', 'name', 'network', ]; diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index af309f980..e07053c03 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -14,6 +14,7 @@ class StandaloneDragonfly extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'dragonfly_password', @@ -39,6 +40,9 @@ class StandaloneDragonfly extends BaseModel 'public_port_timeout', 'enable_ssl', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -70,7 +74,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index ee07b4783..979f45a3d 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -14,6 +14,7 @@ class StandaloneKeydb extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'keydb_password', @@ -40,6 +41,9 @@ class StandaloneKeydb extends BaseModel 'public_port_timeout', 'enable_ssl', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'server_status']; @@ -71,7 +75,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index ad5220496..dba8a52f5 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -15,6 +15,7 @@ class StandaloneMariadb extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'mariadb_root_password', @@ -43,6 +44,9 @@ class StandaloneMariadb extends BaseModel 'enable_ssl', 'is_log_drain_enabled', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -74,7 +78,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index 590c173e1..e72f4f1c6 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -14,6 +14,7 @@ class StandaloneMongodb extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'mongo_conf', @@ -43,6 +44,9 @@ class StandaloneMongodb extends BaseModel 'is_log_drain_enabled', 'is_include_timestamps', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -80,7 +84,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index d991617b7..1c522d200 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -14,6 +14,7 @@ class StandaloneMysql extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'mysql_root_password', @@ -44,6 +45,9 @@ class StandaloneMysql extends BaseModel 'is_log_drain_enabled', 'is_include_timestamps', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -76,7 +80,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 71034427f..57dfe5988 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -14,6 +14,7 @@ class StandalonePostgresql extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'postgres_user', @@ -46,6 +47,9 @@ class StandalonePostgresql extends BaseModel 'is_log_drain_enabled', 'is_include_timestamps', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -92,7 +96,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); } diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index 4eb28e038..ef42d7f18 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -14,6 +14,7 @@ class StandaloneRedis extends BaseModel use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes; protected $fillable = [ + 'uuid', 'name', 'description', 'redis_conf', @@ -39,6 +40,9 @@ class StandaloneRedis extends BaseModel 'is_log_drain_enabled', 'is_include_timestamps', 'custom_docker_run_options', + 'destination_type', + 'destination_id', + 'environment_id', ]; protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status']; @@ -69,7 +73,7 @@ protected static function booted() }); static::saving(function ($database) { if ($database->isDirty('status')) { - $database->forceFill(['last_online_at' => now()]); + $database->last_online_at = now(); } }); diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php index fa135b29f..b0fec64f9 100644 --- a/app/Models/Subscription.php +++ b/app/Models/Subscription.php @@ -7,6 +7,7 @@ class Subscription extends Model { protected $fillable = [ + 'team_id', 'stripe_invoice_paid', 'stripe_subscription_id', 'stripe_customer_id', diff --git a/app/Models/SwarmDocker.php b/app/Models/SwarmDocker.php index 656749119..134e36189 100644 --- a/app/Models/SwarmDocker.php +++ b/app/Models/SwarmDocker.php @@ -7,6 +7,7 @@ class SwarmDocker extends BaseModel { protected $fillable = [ + 'server_id', 'name', 'network', ]; diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 9ee58cf7d..e6fbd3a06 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -10,6 +10,7 @@ class Tag extends BaseModel protected $fillable = [ 'name', + 'team_id', ]; protected function customizeName($value) diff --git a/app/Models/User.php b/app/Models/User.php index ad9a7af31..aa33a49fb 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -49,6 +49,9 @@ class User extends Authenticatable implements SendsEmail 'password', 'force_password_reset', 'marketing_emails', + 'pending_email', + 'email_change_code', + 'email_change_code_expires_at', ]; protected $hidden = [ @@ -409,7 +412,7 @@ public function requestEmailChange(string $newEmail): void $expiryMinutes = config('constants.email_change.verification_code_expiry_minutes', 10); $expiresAt = Carbon::now()->addMinutes($expiryMinutes); - $this->forceFill([ + $this->fill([ 'pending_email' => $newEmail, 'email_change_code' => $code, 'email_change_code_expires_at' => $expiresAt, diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php index e4feec692..48e0a8c78 100644 --- a/bootstrap/helpers/applications.php +++ b/bootstrap/helpers/applications.php @@ -214,7 +214,7 @@ function clone_application(Application $source, $destination, array $overrides = 'updated_at', 'additional_servers_count', 'additional_networks_count', - ])->forceFill(array_merge([ + ])->fill(array_merge([ 'uuid' => $uuid, 'name' => $name, 'fqdn' => $url, @@ -237,7 +237,7 @@ function clone_application(Application $source, $destination, array $overrides = 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'application_id' => $newApplication->id, ]); $newApplicationSettings->save(); @@ -257,7 +257,7 @@ function clone_application(Application $source, $destination, array $overrides = 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => (string) new Cuid2, 'application_id' => $newApplication->id, 'team_id' => currentTeam()->id, @@ -272,7 +272,7 @@ function clone_application(Application $source, $destination, array $overrides = 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'uuid' => (string) new Cuid2, 'application_id' => $newApplication->id, 'status' => 'exited', @@ -304,7 +304,7 @@ function clone_application(Application $source, $destination, array $overrides = 'created_at', 'updated_at', 'uuid', - ])->forceFill([ + ])->fill([ 'name' => $newName, 'resource_id' => $newApplication->id, ]); @@ -340,7 +340,7 @@ function clone_application(Application $source, $destination, array $overrides = 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resource_id' => $newApplication->id, ]); $newStorage->save(); @@ -354,7 +354,7 @@ function clone_application(Application $source, $destination, array $overrides = 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resourceable_id' => $newApplication->id, 'resourceable_type' => $newApplication->getMorphClass(), 'is_preview' => false, @@ -371,7 +371,7 @@ function clone_application(Application $source, $destination, array $overrides = 'id', 'created_at', 'updated_at', - ])->forceFill([ + ])->fill([ 'resourceable_id' => $newApplication->id, 'resourceable_type' => $newApplication->getMorphClass(), 'is_preview' => true, diff --git a/bootstrap/helpers/parsers.php b/bootstrap/helpers/parsers.php index 751851283..123cf906a 100644 --- a/bootstrap/helpers/parsers.php +++ b/bootstrap/helpers/parsers.php @@ -1597,7 +1597,7 @@ function serviceParser(Service $resource): Collection if ($databaseFound) { $savedService = $databaseFound; } else { - $savedService = ServiceDatabase::forceCreate([ + $savedService = ServiceDatabase::create([ 'name' => $serviceName, 'service_id' => $resource->id, ]); @@ -1607,7 +1607,7 @@ function serviceParser(Service $resource): Collection if ($applicationFound) { $savedService = $applicationFound; } else { - $savedService = ServiceApplication::forceCreate([ + $savedService = ServiceApplication::create([ 'name' => $serviceName, 'service_id' => $resource->id, ]); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index a43f2e340..cd773f6a9 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1919,7 +1919,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // Create new serviceApplication or serviceDatabase if ($isDatabase) { if ($isNew) { - $savedService = ServiceDatabase::forceCreate([ + $savedService = ServiceDatabase::create([ 'name' => $serviceName, 'image' => $image, 'service_id' => $resource->id, @@ -1930,7 +1930,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'service_id' => $resource->id, ])->first(); if (is_null($savedService)) { - $savedService = ServiceDatabase::forceCreate([ + $savedService = ServiceDatabase::create([ 'name' => $serviceName, 'image' => $image, 'service_id' => $resource->id, @@ -1939,7 +1939,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } else { if ($isNew) { - $savedService = ServiceApplication::forceCreate([ + $savedService = ServiceApplication::create([ 'name' => $serviceName, 'image' => $image, 'service_id' => $resource->id, @@ -1950,7 +1950,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'service_id' => $resource->id, ])->first(); if (is_null($savedService)) { - $savedService = ServiceApplication::forceCreate([ + $savedService = ServiceApplication::create([ 'name' => $serviceName, 'image' => $image, 'service_id' => $resource->id, diff --git a/tests/Feature/ApplicationHealthCheckApiTest.php b/tests/Feature/ApplicationHealthCheckApiTest.php index 7f1b985ad..3e4078051 100644 --- a/tests/Feature/ApplicationHealthCheckApiTest.php +++ b/tests/Feature/ApplicationHealthCheckApiTest.php @@ -31,7 +31,7 @@ ); }); - $this->project = Project::forceCreate([ + $this->project = Project::create([ 'uuid' => (string) new Cuid2, 'name' => 'test-project', 'team_id' => $this->team->id, diff --git a/tests/Feature/ApplicationRollbackTest.php b/tests/Feature/ApplicationRollbackTest.php index 61b3505ae..432bdde1b 100644 --- a/tests/Feature/ApplicationRollbackTest.php +++ b/tests/Feature/ApplicationRollbackTest.php @@ -6,7 +6,7 @@ describe('Application Rollback', function () { beforeEach(function () { $this->application = new Application; - $this->application->forceFill([ + $this->application->fill([ 'uuid' => 'test-app-uuid', 'git_commit_sha' => 'HEAD', ]); diff --git a/tests/Feature/ClonePersistentVolumeUuidTest.php b/tests/Feature/ClonePersistentVolumeUuidTest.php index 3f99c5585..13f7a1396 100644 --- a/tests/Feature/ClonePersistentVolumeUuidTest.php +++ b/tests/Feature/ClonePersistentVolumeUuidTest.php @@ -31,7 +31,7 @@ 'redirect' => 'both', ]); - $this->application->settings->forceFill([ + $this->application->settings->fill([ 'is_container_label_readonly_enabled' => false, ])->save(); @@ -92,7 +92,7 @@ }); test('cloning application reassigns settings to the cloned application', function () { - $this->application->settings->forceFill([ + $this->application->settings->fill([ 'is_static' => true, 'is_spa' => true, 'is_build_server_enabled' => true, @@ -118,7 +118,7 @@ }); test('cloning application reassigns scheduled tasks and previews to the cloned application', function () { - $scheduledTask = ScheduledTask::forceCreate([ + $scheduledTask = ScheduledTask::create([ 'uuid' => 'scheduled-task-original', 'application_id' => $this->application->id, 'team_id' => $this->team->id, @@ -129,7 +129,7 @@ 'timeout' => 120, ]); - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'uuid' => 'preview-original', 'application_id' => $this->application->id, 'pull_request_id' => 123, diff --git a/tests/Feature/ComposePreviewFqdnTest.php b/tests/Feature/ComposePreviewFqdnTest.php index 62fc0f2d8..a5b8b2c9f 100644 --- a/tests/Feature/ComposePreviewFqdnTest.php +++ b/tests/Feature/ComposePreviewFqdnTest.php @@ -14,7 +14,7 @@ ]), ]); - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'application_id' => $application->id, 'pull_request_id' => 42, 'pull_request_html_url' => 'https://github.com/example/repo/pull/42', @@ -39,7 +39,7 @@ ]), ]); - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'application_id' => $application->id, 'pull_request_id' => 7, 'pull_request_html_url' => 'https://github.com/example/repo/pull/7', @@ -65,7 +65,7 @@ ]), ]); - $preview = ApplicationPreview::forceCreate([ + $preview = ApplicationPreview::create([ 'application_id' => $application->id, 'pull_request_id' => 99, 'pull_request_html_url' => 'https://github.com/example/repo/pull/99', diff --git a/tests/Feature/DatabaseEnvironmentVariableApiTest.php b/tests/Feature/DatabaseEnvironmentVariableApiTest.php index 78e80483b..f3297cf17 100644 --- a/tests/Feature/DatabaseEnvironmentVariableApiTest.php +++ b/tests/Feature/DatabaseEnvironmentVariableApiTest.php @@ -33,7 +33,7 @@ function createDatabase($context): StandalonePostgresql { - return StandalonePostgresql::forceCreate([ + return StandalonePostgresql::create([ 'name' => 'test-postgres', 'image' => 'postgres:15-alpine', 'postgres_user' => 'postgres', diff --git a/tests/Feature/DatabasePublicPortTimeoutApiTest.php b/tests/Feature/DatabasePublicPortTimeoutApiTest.php index 1ffc32a81..6bbc6279f 100644 --- a/tests/Feature/DatabasePublicPortTimeoutApiTest.php +++ b/tests/Feature/DatabasePublicPortTimeoutApiTest.php @@ -33,7 +33,7 @@ describe('PATCH /api/v1/databases', function () { test('updates public_port_timeout on a postgresql database', function () { - $database = StandalonePostgresql::forceCreate([ + $database = StandalonePostgresql::create([ 'name' => 'test-postgres', 'image' => 'postgres:15-alpine', 'postgres_user' => 'postgres', @@ -57,7 +57,7 @@ }); test('updates public_port_timeout on a redis database', function () { - $database = StandaloneRedis::forceCreate([ + $database = StandaloneRedis::create([ 'name' => 'test-redis', 'image' => 'redis:7', 'redis_password' => 'password', @@ -79,7 +79,7 @@ }); test('rejects invalid public_port_timeout value', function () { - $database = StandalonePostgresql::forceCreate([ + $database = StandalonePostgresql::create([ 'name' => 'test-postgres', 'image' => 'postgres:15-alpine', 'postgres_user' => 'postgres', @@ -101,7 +101,7 @@ }); test('accepts null public_port_timeout', function () { - $database = StandalonePostgresql::forceCreate([ + $database = StandalonePostgresql::create([ 'name' => 'test-postgres', 'image' => 'postgres:15-alpine', 'postgres_user' => 'postgres', diff --git a/tests/Feature/DatabaseSslStatusRefreshTest.php b/tests/Feature/DatabaseSslStatusRefreshTest.php index eab2b08db..e62ef48ad 100644 --- a/tests/Feature/DatabaseSslStatusRefreshTest.php +++ b/tests/Feature/DatabaseSslStatusRefreshTest.php @@ -52,7 +52,7 @@ $project = Project::factory()->create(['team_id' => $this->team->id]); $environment = Environment::factory()->create(['project_id' => $project->id]); - $database = StandaloneMysql::forceCreate([ + $database = StandaloneMysql::create([ 'name' => 'test-mysql', 'image' => 'mysql:8', 'mysql_root_password' => 'password', @@ -70,7 +70,7 @@ $component = Livewire::test(MysqlGeneral::class, ['database' => $database]) ->assertDontSee('Database should be stopped to change this settings.'); - $database->forceFill(['status' => 'running:healthy'])->save(); + $database->fill(['status' => 'running:healthy'])->save(); $component->call('refresh') ->assertSee('Database should be stopped to change this settings.'); diff --git a/tests/Feature/GetLogsCommandInjectionTest.php b/tests/Feature/GetLogsCommandInjectionTest.php index 3e5a33b66..c0b17c3bd 100644 --- a/tests/Feature/GetLogsCommandInjectionTest.php +++ b/tests/Feature/GetLogsCommandInjectionTest.php @@ -69,7 +69,7 @@ describe('GetLogs Livewire action validation', function () { test('getLogs rejects invalid container name', function () { // Make server functional by setting settings directly - $this->server->settings->forceFill([ + $this->server->settings->fill([ 'is_reachable' => true, 'is_usable' => true, 'force_disabled' => false, @@ -100,7 +100,7 @@ }); test('downloadAllLogs returns empty for invalid container name', function () { - $this->server->settings->forceFill([ + $this->server->settings->fill([ 'is_reachable' => true, 'is_usable' => true, 'force_disabled' => false, diff --git a/tests/Feature/GithubPrivateRepositoryTest.php b/tests/Feature/GithubPrivateRepositoryTest.php index abc288519..ba66a10bb 100644 --- a/tests/Feature/GithubPrivateRepositoryTest.php +++ b/tests/Feature/GithubPrivateRepositoryTest.php @@ -31,7 +31,7 @@ 'team_id' => $this->team->id, ]); - $this->githubApp = GithubApp::forceCreate([ + $this->githubApp = GithubApp::create([ 'name' => 'Test GitHub App', 'api_url' => 'https://api.github.com', 'html_url' => 'https://github.com', diff --git a/tests/Feature/InternalModelCreationMassAssignmentTest.php b/tests/Feature/InternalModelCreationMassAssignmentTest.php index fc581bf5c..5aad7f3e0 100644 --- a/tests/Feature/InternalModelCreationMassAssignmentTest.php +++ b/tests/Feature/InternalModelCreationMassAssignmentTest.php @@ -24,7 +24,7 @@ ]); $destination = $server->standaloneDockers()->firstOrFail(); - $application = Application::forceCreate([ + $application = Application::create([ 'name' => 'internal-app', 'git_repository' => 'https://github.com/coollabsio/coolify', 'git_branch' => 'main', @@ -57,7 +57,7 @@ ]); $destination = $server->standaloneDockers()->firstOrFail(); - $service = Service::forceCreate([ + $service = Service::create([ 'docker_compose_raw' => 'services: {}', 'environment_id' => $environment->id, 'server_id' => $server->id, diff --git a/tests/Feature/ModelFillableCreationTest.php b/tests/Feature/ModelFillableCreationTest.php new file mode 100644 index 000000000..b72e7381e --- /dev/null +++ b/tests/Feature/ModelFillableCreationTest.php @@ -0,0 +1,1114 @@ +team = Team::factory()->create(); + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + $this->destination = $this->server->standaloneDockers()->firstOrFail(); + $this->project = Project::factory()->create(['team_id' => $this->team->id]); + $this->environment = Environment::factory()->create(['project_id' => $this->project->id]); +}); + +it('creates User with all fillable attributes', function () { + $user = User::create([ + 'name' => 'Test User', + 'email' => 'fillable-test@example.com', + 'password' => bcrypt('password123'), + 'force_password_reset' => true, + 'marketing_emails' => false, + 'pending_email' => 'newemail@example.com', + 'email_change_code' => 'ABC123', + 'email_change_code_expires_at' => now()->addHour(), + ]); + + expect($user->exists)->toBeTrue(); + expect($user->name)->toBe('Test User'); + expect($user->email)->toBe('fillable-test@example.com'); + expect($user->force_password_reset)->toBeTrue(); + expect($user->marketing_emails)->toBeFalse(); + expect($user->pending_email)->toBe('newemail@example.com'); + expect($user->email_change_code)->toBe('ABC123'); + expect($user->email_change_code_expires_at)->not->toBeNull(); +}); + +it('creates Server with all fillable attributes', function () { + $cloudToken = CloudProviderToken::create([ + 'team_id' => $this->team->id, + 'provider' => 'hetzner', + 'token' => 'test-token', + 'name' => 'test-cloud', + ]); + + $server = Server::create([ + 'name' => 'fillable-test-server', + 'ip' => '10.0.0.99', + 'port' => 2222, + 'user' => 'deployer', + 'description' => 'A test server with all fillable attrs', + 'private_key_id' => $this->server->private_key_id, + 'cloud_provider_token_id' => $cloudToken->id, + 'team_id' => $this->team->id, + 'hetzner_server_id' => 'htz-12345', + 'hetzner_server_status' => 'running', + 'is_validating' => false, + 'detected_traefik_version' => 'v2.10.0', + 'traefik_outdated_info' => 'Up to date', + 'server_metadata' => '{"region":"eu-central"}', + 'ip_previous' => '10.0.0.1', + ]); + + expect($server->exists)->toBeTrue(); + expect((string) $server->name)->toBe('fillable-test-server'); + expect((string) $server->ip)->toBe('10.0.0.99'); + expect($server->port)->toBe(2222); + expect((string) $server->user)->toBe('deployer'); + expect((string) $server->description)->toBe('A test server with all fillable attrs'); + expect($server->private_key_id)->toBe($this->server->private_key_id); + expect($server->cloud_provider_token_id)->toBe($cloudToken->id); + expect($server->hetzner_server_id)->toBe('htz-12345'); + expect($server->hetzner_server_status)->toBe('running'); + expect($server->ip_previous)->toBe('10.0.0.1'); +}); + +it('creates Project with all fillable attributes', function () { + $project = Project::create([ + 'name' => 'Fillable Test Project', + 'description' => 'Testing all fillable attrs', + 'team_id' => $this->team->id, + 'uuid' => 'custom-project-uuid', + ]); + + expect($project->exists)->toBeTrue(); + expect($project->name)->toBe('Fillable Test Project'); + expect($project->description)->toBe('Testing all fillable attrs'); + expect($project->team_id)->toBe($this->team->id); + expect($project->uuid)->toBe('custom-project-uuid'); +}); + +it('creates Environment with all fillable attributes', function () { + $env = Environment::create([ + 'name' => 'staging', + 'description' => 'Staging environment', + 'project_id' => $this->project->id, + 'uuid' => 'custom-env-uuid', + ]); + + expect($env->exists)->toBeTrue(); + expect($env->name)->toBe('staging'); + expect($env->description)->toBe('Staging environment'); + expect($env->project_id)->toBe($this->project->id); + expect($env->uuid)->toBe('custom-env-uuid'); +}); + +it('creates ProjectSetting with all fillable attributes', function () { + $setting = ProjectSetting::create([ + 'project_id' => $this->project->id, + ]); + + expect($setting->exists)->toBeTrue(); + expect($setting->project_id)->toBe($this->project->id); +}); + +it('creates Application with all fillable attributes', function () { + $application = Application::create([ + 'uuid' => 'custom-app-uuid', + 'name' => 'Full Fillable App', + 'description' => 'App with every fillable attr set', + 'fqdn' => 'https://app.example.com', + 'git_repository' => 'https://github.com/coollabsio/coolify', + 'git_branch' => 'main', + 'git_commit_sha' => 'abc123def456', + 'git_full_url' => 'https://github.com/coollabsio/coolify.git', + 'docker_registry_image_name' => 'ghcr.io/coollabsio/coolify', + 'docker_registry_image_tag' => 'latest', + 'build_pack' => 'nixpacks', + 'static_image' => 'nginx:alpine', + 'install_command' => 'npm install', + 'build_command' => 'npm run build', + 'start_command' => 'npm start', + 'ports_exposes' => '3000', + 'ports_mappings' => '3000:3000', + 'base_directory' => '/', + 'publish_directory' => '/dist', + 'health_check_enabled' => true, + 'health_check_path' => '/health', + 'health_check_port' => '3000', + 'health_check_host' => 'localhost', + 'health_check_method' => 'GET', + 'health_check_return_code' => 200, + 'health_check_scheme' => 'http', + 'health_check_response_text' => 'ok', + 'health_check_interval' => 30, + 'health_check_timeout' => 5, + 'health_check_retries' => 3, + 'health_check_start_period' => 10, + 'health_check_type' => 'http', + 'health_check_command' => 'curl -f http://localhost:3000/health', + 'limits_memory' => '512m', + 'limits_memory_swap' => '1g', + 'limits_memory_swappiness' => 60, + 'limits_memory_reservation' => '256m', + 'limits_cpus' => '2', + 'limits_cpuset' => '0-1', + 'limits_cpu_shares' => 1024, + 'status' => 'running', + 'preview_url_template' => '{{pr_id}}.{{domain}}', + 'dockerfile' => 'FROM node:18\nRUN npm install', + 'dockerfile_location' => '/Dockerfile', + 'dockerfile_target_build' => 'production', + 'custom_labels' => 'traefik.enable=true', + 'custom_docker_run_options' => '--cap-add=NET_ADMIN', + 'post_deployment_command' => 'php artisan migrate', + 'post_deployment_command_container' => 'app', + 'pre_deployment_command' => 'php artisan down', + 'pre_deployment_command_container' => 'app', + 'manual_webhook_secret_github' => 'gh-secret-123', + 'manual_webhook_secret_gitlab' => 'gl-secret-456', + 'manual_webhook_secret_bitbucket' => 'bb-secret-789', + 'manual_webhook_secret_gitea' => 'gt-secret-012', + 'docker_compose_location' => '/docker-compose.yml', + 'docker_compose' => 'services: {}', + 'docker_compose_raw' => 'services:\n app:\n image: nginx', + 'docker_compose_domains' => '{"app":"https://app.example.com"}', + 'docker_compose_custom_start_command' => 'docker compose up -d', + 'docker_compose_custom_build_command' => 'docker compose build', + 'swarm_replicas' => 3, + 'swarm_placement_constraints' => 'node.role==worker', + 'watch_paths' => 'src/**,package.json', + 'redirect' => 'www', + 'compose_parsing_version' => '2', + 'custom_nginx_configuration' => 'location / { proxy_pass http://localhost:3000; }', + 'custom_network_aliases' => 'app-alias', + 'custom_healthcheck_found' => false, + // Note: nixpkgsarchive, connect_to_docker_network, force_domain_override, + // is_container_label_escape_enabled, use_build_server are in $fillable but + // their migration columns may not exist in the test SQLite schema yet. + 'is_http_basic_auth_enabled' => false, + 'http_basic_auth_username' => 'admin', + 'http_basic_auth_password' => 'secret', + 'config_hash' => 'sha256:abc123', + 'last_online_at' => now()->subMinutes(5)->toISOString(), + 'restart_count' => 2, + 'last_restart_at' => now()->subHour()->toISOString(), + 'last_restart_type' => 'manual', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + 'source_id' => null, + 'source_type' => null, + 'repository_project_id' => null, + 'private_key_id' => null, + ]); + + expect($application->exists)->toBeTrue(); + expect($application->uuid)->toBe('custom-app-uuid'); + expect($application->name)->toBe('Full Fillable App'); + expect((string) $application->git_repository)->toBe('https://github.com/coollabsio/coolify'); + expect($application->build_pack)->toBe('nixpacks'); + expect($application->ports_exposes)->toBe('3000'); + expect($application->environment_id)->toBe($this->environment->id); + expect($application->destination_id)->toBe($this->destination->id); + expect($application->health_check_enabled)->toBeTrue(); + expect($application->limits_memory)->toBe('512m'); + expect($application->swarm_replicas)->toBe(3); + expect($application->restart_count)->toBe(2); +}); + +it('creates ApplicationSetting with all fillable attributes', function () { + $app = Application::create([ + 'name' => 'settings-test-app', + 'git_repository' => 'https://github.com/test/repo', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + // Delete auto-created setting so we can create one with all attrs + ApplicationSetting::where('application_id', $app->id)->delete(); + + $setting = ApplicationSetting::create([ + 'application_id' => $app->id, + 'is_static' => true, + 'is_git_submodules_enabled' => true, + 'is_git_lfs_enabled' => true, + 'is_auto_deploy_enabled' => false, + 'is_force_https_enabled' => true, + 'is_debug_enabled' => true, + 'is_preview_deployments_enabled' => false, + 'is_log_drain_enabled' => true, + 'is_gpu_enabled' => true, + 'gpu_driver' => 'nvidia', + 'gpu_count' => '2', + 'gpu_device_ids' => 'GPU-abc,GPU-def', + 'gpu_options' => '--gpus all', + 'is_include_timestamps' => true, + 'is_swarm_only_worker_nodes' => false, + 'is_raw_compose_deployment_enabled' => false, + 'is_build_server_enabled' => false, + 'is_consistent_container_name_enabled' => true, + 'is_gzip_enabled' => true, + 'is_stripprefix_enabled' => true, + 'connect_to_docker_network' => false, + 'custom_internal_name' => 'my-custom-app', + 'is_container_label_escape_enabled' => true, + 'is_env_sorting_enabled' => true, + 'is_container_label_readonly_enabled' => false, + 'is_preserve_repository_enabled' => false, + 'disable_build_cache' => false, + 'is_spa' => true, + 'is_git_shallow_clone_enabled' => true, + 'is_pr_deployments_public_enabled' => false, + 'use_build_secrets' => false, + 'inject_build_args_to_dockerfile' => true, + 'include_source_commit_in_build' => true, + 'docker_images_to_keep' => 5, + ]); + + expect($setting->exists)->toBeTrue(); + expect($setting->application_id)->toBe($app->id); + expect($setting->is_static)->toBeTrue(); + expect($setting->is_gpu_enabled)->toBeTrue(); + expect($setting->gpu_driver)->toBe('nvidia'); + expect($setting->custom_internal_name)->toBe('my-custom-app'); + expect($setting->is_spa)->toBeTrue(); + expect($setting->docker_images_to_keep)->toBe(5); +}); + +it('creates ServerSetting with all fillable attributes', function () { + // Delete auto-created setting + ServerSetting::where('server_id', $this->server->id)->delete(); + + $setting = ServerSetting::create([ + 'server_id' => $this->server->id, + 'is_swarm_manager' => false, + 'is_jump_server' => false, + 'is_build_server' => true, + 'is_reachable' => true, + 'is_usable' => true, + 'wildcard_domain' => '*.example.com', + 'is_cloudflare_tunnel' => false, + 'is_logdrain_newrelic_enabled' => true, + 'logdrain_newrelic_license_key' => 'nr-license-key-123', + 'logdrain_newrelic_base_uri' => 'https://log-api.newrelic.com', + 'is_logdrain_highlight_enabled' => false, + 'logdrain_highlight_project_id' => 'hl-proj-123', + 'is_logdrain_axiom_enabled' => true, + 'logdrain_axiom_dataset_name' => 'coolify-logs', + 'logdrain_axiom_api_key' => 'axiom-key-456', + 'is_swarm_worker' => false, + 'is_logdrain_custom_enabled' => false, + 'logdrain_custom_config' => '{"endpoint":"https://logs.example.com"}', + 'logdrain_custom_config_parser' => 'json', + 'concurrent_builds' => 4, + 'dynamic_timeout' => 600, + 'force_disabled' => false, + 'is_metrics_enabled' => true, + 'generate_exact_labels' => true, + 'force_docker_cleanup' => false, + 'docker_cleanup_frequency' => '0 2 * * *', + 'docker_cleanup_threshold' => 80, + 'server_timezone' => 'UTC', + 'delete_unused_volumes' => true, + 'delete_unused_networks' => true, + 'is_sentinel_enabled' => true, + 'sentinel_token' => 'sentinel-token-789', + 'sentinel_metrics_refresh_rate_seconds' => 30, + 'sentinel_metrics_history_days' => 7, + 'sentinel_push_interval_seconds' => 60, + 'sentinel_custom_url' => 'https://sentinel.example.com', + 'server_disk_usage_notification_threshold' => 90, + 'is_sentinel_debug_enabled' => false, + 'server_disk_usage_check_frequency' => '*/5 * * * *', + 'is_terminal_enabled' => true, + 'deployment_queue_limit' => 10, + 'disable_application_image_retention' => false, + ]); + + expect($setting->exists)->toBeTrue(); + expect($setting->server_id)->toBe($this->server->id); + expect($setting->is_build_server)->toBeTrue(); + expect($setting->wildcard_domain)->toBe('*.example.com'); + expect($setting->concurrent_builds)->toBe(4); + expect($setting->sentinel_token)->toBe('sentinel-token-789'); + expect($setting->deployment_queue_limit)->toBe(10); +}); + +it('creates Service with all fillable attributes', function () { + $service = Service::create([ + 'uuid' => 'custom-service-uuid', + 'name' => 'Full Fillable Service', + 'description' => 'Service with all fillable attrs', + 'docker_compose_raw' => "services:\n app:\n image: nginx", + 'docker_compose' => "services:\n app:\n image: nginx", + 'connect_to_docker_network' => true, + 'service_type' => 'test-service', + 'config_hash' => 'sha256:svc123', + 'compose_parsing_version' => '2', + 'is_container_label_escape_enabled' => true, + 'environment_id' => $this->environment->id, + 'server_id' => $this->server->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + expect($service->exists)->toBeTrue(); + expect($service->uuid)->toBe('custom-service-uuid'); + expect($service->name)->toBe('Full Fillable Service'); + expect($service->docker_compose_raw)->not->toBeNull(); + expect($service->service_type)->toBe('test-service'); + expect($service->environment_id)->toBe($this->environment->id); + expect($service->server_id)->toBe($this->server->id); +}); + +it('creates ApplicationPreview with all fillable attributes', function () { + $app = Application::create([ + 'name' => 'preview-test-app', + 'git_repository' => 'https://github.com/test/repo', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + $preview = ApplicationPreview::create([ + 'uuid' => 'custom-preview-uuid', + 'application_id' => $app->id, + 'pull_request_id' => 42, + 'pull_request_html_url' => 'https://github.com/test/repo/pull/42', + 'pull_request_issue_comment_id' => 12345, + 'fqdn' => 'https://pr-42.app.example.com', + 'status' => 'queued', + 'git_type' => 'github', + 'docker_compose_domains' => '{"app":"https://pr-42.example.com"}', + 'docker_registry_image_tag' => 'pr-42', + 'last_online_at' => now()->toISOString(), + ]); + + expect($preview->exists)->toBeTrue(); + expect($preview->uuid)->toBe('custom-preview-uuid'); + expect($preview->application_id)->toBe($app->id); + expect($preview->pull_request_id)->toBe(42); + expect($preview->fqdn)->toBe('https://pr-42.app.example.com'); + expect($preview->git_type)->toBe('github'); + expect($preview->docker_registry_image_tag)->toBe('pr-42'); +}); + +it('creates ServiceApplication with all fillable attributes', function () { + $service = Service::create([ + 'docker_compose_raw' => 'services: {}', + 'environment_id' => $this->environment->id, + 'server_id' => $this->server->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + $svcApp = ServiceApplication::create([ + 'service_id' => $service->id, + 'name' => 'web', + 'human_name' => 'Web Server', + 'description' => 'Main web application', + 'fqdn' => 'https://web.example.com', + 'ports' => '80,443', + 'exposes' => '80', + 'status' => 'running', + 'exclude_from_status' => false, + 'required_fqdn' => true, + 'image' => 'nginx:latest', + 'is_log_drain_enabled' => true, + 'is_include_timestamps' => true, + 'is_gzip_enabled' => true, + 'is_stripprefix_enabled' => true, + 'last_online_at' => now()->toISOString(), + 'is_migrated' => false, + ]); + + expect($svcApp->exists)->toBeTrue(); + expect($svcApp->service_id)->toBe($service->id); + expect($svcApp->name)->toBe('web'); + expect($svcApp->human_name)->toBe('Web Server'); + expect($svcApp->image)->toBe('nginx:latest'); + expect($svcApp->is_log_drain_enabled)->toBeTrue(); +}); + +it('creates ServiceDatabase with all fillable attributes', function () { + $service = Service::create([ + 'docker_compose_raw' => 'services: {}', + 'environment_id' => $this->environment->id, + 'server_id' => $this->server->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + $svcDb = ServiceDatabase::create([ + 'service_id' => $service->id, + 'name' => 'postgres', + 'human_name' => 'PostgreSQL', + 'description' => 'Main database', + 'ports' => '5432', + 'exposes' => '5432', + 'status' => 'running', + 'exclude_from_status' => false, + 'image' => 'postgres:16', + 'public_port' => 15432, + 'is_public' => true, + 'is_log_drain_enabled' => true, + 'is_include_timestamps' => true, + 'is_gzip_enabled' => false, + 'is_stripprefix_enabled' => false, + 'last_online_at' => now()->toISOString(), + 'is_migrated' => false, + 'custom_type' => 'postgresql', + 'public_port_timeout' => 3600, + ]); + + expect($svcDb->exists)->toBeTrue(); + expect($svcDb->service_id)->toBe($service->id); + expect($svcDb->name)->toBe('postgres'); + expect($svcDb->public_port)->toBe(15432); + expect($svcDb->is_public)->toBeTrue(); + expect($svcDb->custom_type)->toBe('postgresql'); +}); + +it('creates StandalonePostgresql with all fillable attributes', function () { + $db = StandalonePostgresql::create([ + 'uuid' => 'custom-pg-uuid', + 'name' => 'Full Fillable Postgres', + 'description' => 'PG with all attrs', + 'postgres_user' => 'testuser', + 'postgres_password' => 'testpass123', + 'postgres_db' => 'testdb', + 'postgres_initdb_args' => '--encoding=UTF8', + 'postgres_host_auth_method' => 'scram-sha-256', + 'postgres_conf' => 'max_connections=200', + 'init_scripts' => 'CREATE TABLE test (id int);', + 'status' => 'running', + 'image' => 'postgres:16-alpine', + 'is_public' => true, + 'public_port' => 25432, + 'ports_mappings' => '25432:5432', + 'limits_memory' => '1g', + 'limits_memory_swap' => '2g', + 'limits_memory_swappiness' => 50, + 'limits_memory_reservation' => '512m', + 'limits_cpus' => '2', + 'limits_cpuset' => '0-1', + 'limits_cpu_shares' => 1024, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 1, + 'last_restart_at' => now()->subHours(6)->toISOString(), + 'last_restart_type' => 'manual', + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 7200, + 'enable_ssl' => true, + 'ssl_mode' => 'verify-full', + 'is_log_drain_enabled' => true, + 'is_include_timestamps' => true, + 'custom_docker_run_options' => '--shm-size=256m', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-pg-uuid'); + expect($db->postgres_user)->toBe('testuser'); + expect($db->postgres_db)->toBe('testdb'); + expect($db->is_public)->toBeTrue(); + expect($db->public_port)->toBe(25432); + expect($db->enable_ssl)->toBeTrue(); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneMysql with all fillable attributes', function () { + $db = StandaloneMysql::create([ + 'uuid' => 'custom-mysql-uuid', + 'name' => 'Full Fillable MySQL', + 'description' => 'MySQL with all attrs', + 'mysql_root_password' => 'rootpass123', + 'mysql_user' => 'testuser', + 'mysql_password' => 'testpass123', + 'mysql_database' => 'testdb', + 'mysql_conf' => '[mysqld]\nmax_connections=200', + 'status' => 'running', + 'image' => 'mysql:8.0', + 'is_public' => false, + 'public_port' => 23306, + 'ports_mappings' => '23306:3306', + 'limits_memory' => '1g', + 'limits_memory_swap' => '2g', + 'limits_memory_swappiness' => 50, + 'limits_memory_reservation' => '512m', + 'limits_cpus' => '2', + 'limits_cpuset' => '0-1', + 'limits_cpu_shares' => 1024, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'enable_ssl' => true, + 'ssl_mode' => 'REQUIRED', + 'is_log_drain_enabled' => false, + 'is_include_timestamps' => false, + 'custom_docker_run_options' => '--ulimit nofile=65535:65535', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-mysql-uuid'); + expect($db->mysql_root_password)->toBe('rootpass123'); + expect($db->mysql_database)->toBe('testdb'); + expect($db->enable_ssl)->toBeTrue(); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneMariadb with all fillable attributes', function () { + $db = StandaloneMariadb::create([ + 'uuid' => 'custom-maria-uuid', + 'name' => 'Full Fillable MariaDB', + 'description' => 'MariaDB with all attrs', + 'mariadb_root_password' => 'rootpass123', + 'mariadb_user' => 'testuser', + 'mariadb_password' => 'testpass123', + 'mariadb_database' => 'testdb', + 'mariadb_conf' => '[mysqld]\nmax_connections=200', + 'status' => 'running', + 'image' => 'mariadb:11', + 'is_public' => false, + 'public_port' => 23307, + 'ports_mappings' => '23307:3306', + 'limits_memory' => '1g', + 'limits_memory_swap' => '2g', + 'limits_memory_swappiness' => 50, + 'limits_memory_reservation' => '512m', + 'limits_cpus' => '2', + 'limits_cpuset' => '0-1', + 'limits_cpu_shares' => 1024, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'enable_ssl' => false, + 'is_log_drain_enabled' => false, + 'custom_docker_run_options' => '', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-maria-uuid'); + expect($db->mariadb_root_password)->toBe('rootpass123'); + expect($db->mariadb_database)->toBe('testdb'); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneMongodb with all fillable attributes', function () { + $db = StandaloneMongodb::create([ + 'uuid' => 'custom-mongo-uuid', + 'name' => 'Full Fillable MongoDB', + 'description' => 'MongoDB with all attrs', + 'mongo_conf' => '{"storage":{"dbPath":"/data/db"}}', + 'mongo_initdb_root_username' => 'mongoadmin', + 'mongo_initdb_root_password' => 'mongopass123', + 'mongo_initdb_database' => 'testdb', + 'status' => 'running', + 'image' => 'mongo:7', + 'is_public' => false, + 'public_port' => 27018, + 'ports_mappings' => '27018:27017', + 'limits_memory' => '2g', + 'limits_memory_swap' => '4g', + 'limits_memory_swappiness' => 60, + 'limits_memory_reservation' => '1g', + 'limits_cpus' => '4', + 'limits_cpuset' => '0-3', + 'limits_cpu_shares' => 2048, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'enable_ssl' => false, + 'ssl_mode' => 'prefer', + 'is_log_drain_enabled' => false, + 'is_include_timestamps' => false, + 'custom_docker_run_options' => '', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-mongo-uuid'); + expect($db->mongo_initdb_root_username)->toBe('mongoadmin'); + expect($db->mongo_initdb_database)->toBe('testdb'); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneRedis with all fillable attributes', function () { + $db = StandaloneRedis::create([ + 'uuid' => 'custom-redis-uuid', + 'name' => 'Full Fillable Redis', + 'description' => 'Redis with all attrs', + 'redis_conf' => 'maxmemory 256mb\nmaxmemory-policy allkeys-lru', + 'status' => 'running', + 'image' => 'redis:7-alpine', + 'is_public' => true, + 'public_port' => 26379, + 'ports_mappings' => '26379:6379', + 'limits_memory' => '512m', + 'limits_memory_swap' => '1g', + 'limits_memory_swappiness' => 30, + 'limits_memory_reservation' => '256m', + 'limits_cpus' => '1', + 'limits_cpuset' => '0', + 'limits_cpu_shares' => 512, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'enable_ssl' => false, + 'is_log_drain_enabled' => false, + 'is_include_timestamps' => false, + 'custom_docker_run_options' => '', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-redis-uuid'); + expect($db->redis_conf)->toContain('maxmemory'); + expect($db->is_public)->toBeTrue(); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneKeydb with all fillable attributes', function () { + $db = StandaloneKeydb::create([ + 'uuid' => 'custom-keydb-uuid', + 'name' => 'Full Fillable KeyDB', + 'description' => 'KeyDB with all attrs', + 'keydb_password' => 'keydbpass123', + 'keydb_conf' => 'server-threads 4', + 'is_log_drain_enabled' => false, + 'is_include_timestamps' => false, + 'status' => 'running', + 'image' => 'eqalpha/keydb:latest', + 'is_public' => false, + 'public_port' => 26380, + 'ports_mappings' => '26380:6379', + 'limits_memory' => '512m', + 'limits_memory_swap' => '1g', + 'limits_memory_swappiness' => 30, + 'limits_memory_reservation' => '256m', + 'limits_cpus' => '2', + 'limits_cpuset' => '0-1', + 'limits_cpu_shares' => 512, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'enable_ssl' => false, + 'custom_docker_run_options' => '', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-keydb-uuid'); + expect($db->keydb_password)->toBe('keydbpass123'); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneDragonfly with all fillable attributes', function () { + $db = StandaloneDragonfly::create([ + 'uuid' => 'custom-dragonfly-uuid', + 'name' => 'Full Fillable Dragonfly', + 'description' => 'Dragonfly with all attrs', + 'dragonfly_password' => 'dragonflypass123', + 'is_log_drain_enabled' => false, + 'is_include_timestamps' => false, + 'status' => 'running', + 'image' => 'docker.dragonflydb.io/dragonflydb/dragonfly:latest', + 'is_public' => false, + 'public_port' => 26381, + 'ports_mappings' => '26381:6379', + 'limits_memory' => '1g', + 'limits_memory_swap' => '2g', + 'limits_memory_swappiness' => 30, + 'limits_memory_reservation' => '512m', + 'limits_cpus' => '2', + 'limits_cpuset' => '0-1', + 'limits_cpu_shares' => 512, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'enable_ssl' => false, + 'custom_docker_run_options' => '', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-dragonfly-uuid'); + expect($db->dragonfly_password)->toBe('dragonflypass123'); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates StandaloneClickhouse with all fillable attributes', function () { + $db = StandaloneClickhouse::create([ + 'uuid' => 'custom-ch-uuid', + 'name' => 'Full Fillable ClickHouse', + 'description' => 'ClickHouse with all attrs', + 'clickhouse_admin_user' => 'chadmin', + 'clickhouse_admin_password' => 'chpass123', + 'is_log_drain_enabled' => false, + 'is_include_timestamps' => false, + 'status' => 'running', + 'image' => 'clickhouse/clickhouse-server:latest', + 'is_public' => false, + 'public_port' => 28123, + 'ports_mappings' => '28123:8123', + 'limits_memory' => '2g', + 'limits_memory_swap' => '4g', + 'limits_memory_swappiness' => 30, + 'limits_memory_reservation' => '1g', + 'limits_cpus' => '4', + 'limits_cpuset' => '0-3', + 'limits_cpu_shares' => 2048, + 'started_at' => now()->subDay()->toISOString(), + 'restart_count' => 0, + 'last_restart_at' => null, + 'last_restart_type' => null, + 'last_online_at' => now()->toISOString(), + 'public_port_timeout' => 3600, + 'custom_docker_run_options' => '', + 'clickhouse_db' => 'testdb', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + expect($db->exists)->toBeTrue(); + expect($db->uuid)->toBe('custom-ch-uuid'); + expect($db->clickhouse_admin_user)->toBe('chadmin'); + expect($db->clickhouse_db)->toBe('testdb'); + expect($db->environment_id)->toBe($this->environment->id); +}); + +it('creates SwarmDocker with all fillable attributes', function () { + $swarm = SwarmDocker::create([ + 'server_id' => $this->server->id, + 'name' => 'swarm-dest', + 'network' => 'coolify-swarm', + ]); + + expect($swarm->exists)->toBeTrue(); + expect($swarm->server_id)->toBe($this->server->id); + expect($swarm->name)->toBe('swarm-dest'); + expect($swarm->network)->toBe('coolify-swarm'); +}); + +it('creates StandaloneDocker with all fillable attributes', function () { + $docker = StandaloneDocker::create([ + 'server_id' => $this->server->id, + 'name' => 'standalone-dest', + 'network' => 'coolify-standalone', + ]); + + expect($docker->exists)->toBeTrue(); + expect($docker->server_id)->toBe($this->server->id); + expect($docker->name)->toBe('standalone-dest'); + expect($docker->network)->toBe('coolify-standalone'); +}); + +it('creates ScheduledTask with all fillable attributes', function () { + $app = Application::create([ + 'name' => 'task-test-app', + 'git_repository' => 'https://github.com/test/repo', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + $task = ScheduledTask::create([ + 'uuid' => 'custom-task-uuid', + 'enabled' => true, + 'name' => 'Full Fillable Task', + 'command' => 'php artisan schedule:run', + 'frequency' => '* * * * *', + 'container' => 'app', + 'timeout' => 300, + 'team_id' => $this->team->id, + 'application_id' => $app->id, + 'service_id' => null, + ]); + + expect($task->exists)->toBeTrue(); + expect($task->uuid)->toBe('custom-task-uuid'); + expect($task->name)->toBe('Full Fillable Task'); + expect($task->command)->toBe('php artisan schedule:run'); + expect($task->frequency)->toBe('* * * * *'); + expect($task->container)->toBe('app'); + expect($task->timeout)->toBe(300); + expect($task->team_id)->toBe($this->team->id); + expect($task->application_id)->toBe($app->id); +}); + +it('creates ScheduledDatabaseBackup with all fillable attributes', function () { + $db = StandalonePostgresql::create([ + 'name' => 'backup-test-pg', + 'postgres_user' => 'user', + 'postgres_password' => 'pass', + 'postgres_db' => 'testdb', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + + $backup = ScheduledDatabaseBackup::create([ + 'uuid' => 'custom-backup-uuid', + 'team_id' => $this->team->id, + 'description' => 'Full fillable backup', + 'enabled' => true, + 'save_s3' => false, + 'frequency' => '0 2 * * *', + 'database_backup_retention_amount_locally' => 10, + 'database_type' => $db->getMorphClass(), + 'database_id' => $db->id, + 's3_storage_id' => null, + 'databases_to_backup' => 'testdb', + 'dump_all' => false, + 'database_backup_retention_days_locally' => 30, + 'database_backup_retention_max_storage_locally' => 5000, + 'database_backup_retention_amount_s3' => 20, + 'database_backup_retention_days_s3' => 60, + 'database_backup_retention_max_storage_s3' => 10000, + 'timeout' => 600, + 'disable_local_backup' => false, + ]); + + expect($backup->exists)->toBeTrue(); + expect($backup->uuid)->toBe('custom-backup-uuid'); + expect($backup->frequency)->toBe('0 2 * * *'); + expect($backup->database_backup_retention_amount_locally)->toBe(10); + expect($backup->databases_to_backup)->toBe('testdb'); + expect($backup->timeout)->toBe(600); +}); + +it('creates ScheduledDatabaseBackupExecution with all fillable attributes', function () { + $db = StandalonePostgresql::create([ + 'name' => 'exec-test-pg', + 'postgres_user' => 'user', + 'postgres_password' => 'pass', + 'postgres_db' => 'testdb', + 'destination_type' => $this->destination->getMorphClass(), + 'destination_id' => $this->destination->id, + 'environment_id' => $this->environment->id, + ]); + $backup = ScheduledDatabaseBackup::create([ + 'frequency' => '0 2 * * *', + 'database_type' => $db->getMorphClass(), + 'database_id' => $db->id, + 'team_id' => $this->team->id, + ]); + + $execution = ScheduledDatabaseBackupExecution::create([ + 'uuid' => 'custom-exec-uuid', + 'scheduled_database_backup_id' => $backup->id, + 'status' => 'success', + 'message' => 'Backup completed successfully', + 'size' => 1048576, + 'filename' => 'backup-2026-03-31.sql.gz', + 'database_name' => 'testdb', + 'finished_at' => now()->toISOString(), + 'local_storage_deleted' => false, + 's3_storage_deleted' => false, + 's3_uploaded' => false, + ]); + + expect($execution->exists)->toBeTrue(); + expect($execution->uuid)->toBe('custom-exec-uuid'); + expect($execution->status)->toBe('success'); + expect($execution->filename)->toBe('backup-2026-03-31.sql.gz'); + expect($execution->database_name)->toBe('testdb'); + expect($execution->size)->toBe(1048576); +}); + +it('creates ScheduledTaskExecution with all fillable attributes', function () { + $app = Application::create([ + 'name' => 'task-exec-app', + 'git_repository' => 'https://github.com/test/repo', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + $task = ScheduledTask::create([ + 'name' => 'exec-test-task', + 'command' => 'echo hello', + 'frequency' => '* * * * *', + 'timeout' => 60, + 'team_id' => $this->team->id, + 'application_id' => $app->id, + ]); + + $execution = ScheduledTaskExecution::create([ + 'scheduled_task_id' => $task->id, + 'status' => 'success', + 'message' => 'Task completed successfully', + 'finished_at' => now()->toISOString(), + 'started_at' => now()->subMinute()->toISOString(), + 'retry_count' => 0, + 'duration' => 60, + 'error_details' => null, + ]); + + expect($execution->exists)->toBeTrue(); + expect($execution->scheduled_task_id)->toBe($task->id); + expect($execution->status)->toBe('success'); + expect((float) $execution->duration)->toBe(60.0); + expect($execution->retry_count)->toBe(0); +}); + +it('creates GithubApp with all fillable attributes', function () { + $githubApp = GithubApp::create([ + 'team_id' => $this->team->id, + 'private_key_id' => $this->server->private_key_id, + 'name' => 'Full Fillable GH App', + 'organization' => 'coollabsio', + 'api_url' => 'https://api.github.com', + 'html_url' => 'https://github.com', + 'custom_user' => 'git', + 'custom_port' => 22, + 'app_id' => 12345, + 'installation_id' => 67890, + 'client_id' => 'Iv1.abc123', + 'client_secret' => 'secret-456', + 'webhook_secret' => 'whsec-789', + 'is_system_wide' => false, + 'is_public' => false, + 'contents' => 'read', + 'metadata' => 'read', + 'pull_requests' => 'write', + 'administration' => 'read', + ]); + + expect($githubApp->exists)->toBeTrue(); + expect($githubApp->name)->toBe('Full Fillable GH App'); + expect($githubApp->organization)->toBe('coollabsio'); + expect($githubApp->app_id)->toBe(12345); + expect($githubApp->installation_id)->toBe(67890); + expect($githubApp->client_id)->toBe('Iv1.abc123'); + expect($githubApp->team_id)->toBe($this->team->id); + expect($githubApp->private_key_id)->toBe($this->server->private_key_id); +}); + +it('creates Subscription with all fillable attributes', function () { + $sub = Subscription::create([ + 'team_id' => $this->team->id, + 'stripe_invoice_paid' => true, + 'stripe_subscription_id' => 'sub_1234567890', + 'stripe_customer_id' => 'cus_1234567890', + 'stripe_cancel_at_period_end' => false, + 'stripe_plan_id' => 'price_1234567890', + 'stripe_feedback' => 'Great service', + 'stripe_comment' => 'Will renew', + 'stripe_trial_already_ended' => true, + 'stripe_past_due' => false, + 'stripe_refunded_at' => null, + ]); + + expect($sub->exists)->toBeTrue(); + expect($sub->team_id)->toBe($this->team->id); + expect($sub->stripe_subscription_id)->toBe('sub_1234567890'); + expect($sub->stripe_customer_id)->toBe('cus_1234567890'); + expect($sub->stripe_plan_id)->toBe('price_1234567890'); + expect($sub->stripe_invoice_paid)->toBeTrue(); +}); + +it('creates CloudProviderToken with all fillable attributes', function () { + $token = CloudProviderToken::create([ + 'team_id' => $this->team->id, + 'provider' => 'hetzner', + 'token' => 'hcloud-token-abc123', + 'name' => 'My Hetzner Token', + ]); + + expect($token->exists)->toBeTrue(); + expect($token->team_id)->toBe($this->team->id); + expect($token->provider)->toBe('hetzner'); + expect($token->token)->toBe('hcloud-token-abc123'); + expect($token->name)->toBe('My Hetzner Token'); +}); + +it('creates Tag with all fillable attributes', function () { + $tag = Tag::create([ + 'name' => 'production', + 'team_id' => $this->team->id, + ]); + + expect($tag->exists)->toBeTrue(); + expect($tag->name)->toBe('production'); + expect($tag->team_id)->toBe($this->team->id); +}); diff --git a/tests/Feature/ServiceDatabaseTeamTest.php b/tests/Feature/ServiceDatabaseTeamTest.php index ae3cba4d3..5fe7e39d2 100644 --- a/tests/Feature/ServiceDatabaseTeamTest.php +++ b/tests/Feature/ServiceDatabaseTeamTest.php @@ -14,18 +14,18 @@ it('returns the correct team through the service relationship chain', function () { $team = Team::factory()->create(); - $project = Project::forceCreate([ + $project = Project::create([ 'uuid' => (string) Str::uuid(), 'name' => 'Test Project', 'team_id' => $team->id, ]); - $environment = Environment::forceCreate([ + $environment = Environment::create([ 'name' => 'test-env-'.Str::random(8), 'project_id' => $project->id, ]); - $service = Service::forceCreate([ + $service = Service::create([ 'uuid' => (string) Str::uuid(), 'name' => 'supabase', 'environment_id' => $environment->id, @@ -34,7 +34,7 @@ 'docker_compose_raw' => 'version: "3"', ]); - $serviceDatabase = ServiceDatabase::forceCreate([ + $serviceDatabase = ServiceDatabase::create([ 'uuid' => (string) Str::uuid(), 'name' => 'supabase-db', 'service_id' => $service->id, @@ -47,18 +47,18 @@ it('returns the correct team for ServiceApplication through the service relationship chain', function () { $team = Team::factory()->create(); - $project = Project::forceCreate([ + $project = Project::create([ 'uuid' => (string) Str::uuid(), 'name' => 'Test Project', 'team_id' => $team->id, ]); - $environment = Environment::forceCreate([ + $environment = Environment::create([ 'name' => 'test-env-'.Str::random(8), 'project_id' => $project->id, ]); - $service = Service::forceCreate([ + $service = Service::create([ 'uuid' => (string) Str::uuid(), 'name' => 'supabase', 'environment_id' => $environment->id, @@ -67,7 +67,7 @@ 'docker_compose_raw' => 'version: "3"', ]); - $serviceApplication = ServiceApplication::forceCreate([ + $serviceApplication = ServiceApplication::create([ 'uuid' => (string) Str::uuid(), 'name' => 'supabase-studio', 'service_id' => $service->id, diff --git a/tests/Feature/StorageApiTest.php b/tests/Feature/StorageApiTest.php index bd9d727c4..75357e41e 100644 --- a/tests/Feature/StorageApiTest.php +++ b/tests/Feature/StorageApiTest.php @@ -49,7 +49,7 @@ function createTestApplication($context): Application function createTestDatabase($context): StandalonePostgresql { - return StandalonePostgresql::forceCreate([ + return StandalonePostgresql::create([ 'name' => 'test-postgres', 'image' => 'postgres:15-alpine', 'postgres_user' => 'postgres', diff --git a/tests/Unit/GitRefValidationTest.php b/tests/Unit/GitRefValidationTest.php index 58d07f4b7..f82dcb863 100644 --- a/tests/Unit/GitRefValidationTest.php +++ b/tests/Unit/GitRefValidationTest.php @@ -1,12 +1,14 @@ toBe('abc123def456'); @@ -93,31 +95,31 @@ describe('executeInDocker git log escaping', function () { test('git log command escapes commit SHA to prevent injection', function () { $maliciousCommit = "HEAD'; id; #"; - $command = "cd /workdir && git log -1 ".escapeshellarg($maliciousCommit).' --pretty=%B'; + $command = 'cd /workdir && git log -1 '.escapeshellarg($maliciousCommit).' --pretty=%B'; $result = executeInDocker('test-container', $command); // The malicious payload must not be able to break out of quoting - expect($result)->not->toContain("id;"); + expect($result)->not->toContain('id;'); expect($result)->toContain("'HEAD'\\''"); }); }); describe('buildGitCheckoutCommand escaping', function () { test('checkout command escapes target to prevent injection', function () { - $app = new \App\Models\Application; - $app->forceFill(['uuid' => 'test-uuid']); + $app = new Application; + $app->fill(['uuid' => 'test-uuid']); - $settings = new \App\Models\ApplicationSetting; + $settings = new ApplicationSetting; $settings->is_git_submodules_enabled = false; $app->setRelation('settings', $settings); - $method = new \ReflectionMethod($app, 'buildGitCheckoutCommand'); + $method = new ReflectionMethod($app, 'buildGitCheckoutCommand'); $result = $method->invoke($app, 'abc123'); expect($result)->toContain("git checkout 'abc123'"); $result = $method->invoke($app, "abc'; id; #"); - expect($result)->not->toContain("id;"); + expect($result)->not->toContain('id;'); expect($result)->toContain("git checkout 'abc'"); }); }); diff --git a/tests/Unit/ModelFillableRegressionTest.php b/tests/Unit/ModelFillableRegressionTest.php new file mode 100644 index 000000000..eff477c5a --- /dev/null +++ b/tests/Unit/ModelFillableRegressionTest.php @@ -0,0 +1,76 @@ +getFillable())->toContain(...$expectedAttributes); +})->with([ + // Relationship/ownership keys + [CloudProviderToken::class, ['team_id']], + [Tag::class, ['team_id']], + [Subscription::class, ['team_id']], + [ScheduledTaskExecution::class, ['scheduled_task_id']], + [ScheduledDatabaseBackupExecution::class, ['uuid', 'scheduled_database_backup_id']], + [ScheduledDatabaseBackup::class, ['uuid', 'team_id']], + [ScheduledTask::class, ['uuid', 'team_id', 'application_id', 'service_id']], + [ServiceDatabase::class, ['service_id']], + [ServiceApplication::class, ['service_id']], + [ApplicationDeploymentQueue::class, ['docker_registry_image_tag']], + [Project::class, ['team_id', 'uuid']], + [Environment::class, ['project_id', 'uuid']], + [ProjectSetting::class, ['project_id']], + [ApplicationSetting::class, ['application_id']], + [ServerSetting::class, ['server_id']], + [SwarmDocker::class, ['server_id']], + [StandaloneDocker::class, ['server_id']], + [User::class, ['pending_email', 'email_change_code', 'email_change_code_expires_at']], + [Server::class, ['ip_previous']], + [GithubApp::class, ['team_id', 'private_key_id']], + + // Application/Service resource keys (including uuid for clone flows) + [Application::class, ['uuid', 'environment_id', 'destination_id', 'destination_type', 'source_id', 'source_type', 'repository_project_id', 'private_key_id']], + [ApplicationPreview::class, ['uuid', 'application_id']], + [Service::class, ['uuid', 'environment_id', 'server_id', 'destination_id', 'destination_type']], + + // Standalone database resource keys (including uuid for clone flows) + [StandalonePostgresql::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneMysql::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneMariadb::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneMongodb::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneRedis::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneKeydb::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneDragonfly::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], + [StandaloneClickhouse::class, ['uuid', 'destination_type', 'destination_id', 'environment_id']], +]); diff --git a/tests/Unit/ServiceParserImageUpdateTest.php b/tests/Unit/ServiceParserImageUpdateTest.php index 649795866..f672b64f5 100644 --- a/tests/Unit/ServiceParserImageUpdateTest.php +++ b/tests/Unit/ServiceParserImageUpdateTest.php @@ -16,8 +16,8 @@ expect($parsersFile) ->toContain("\$databaseFound = ServiceDatabase::where('name', \$serviceName)->where('service_id', \$resource->id)->first();") ->toContain("\$applicationFound = ServiceApplication::where('name', \$serviceName)->where('service_id', \$resource->id)->first();") - ->toContain("forceCreate([\n 'name' => \$serviceName,\n 'service_id' => \$resource->id,\n ]);") - ->not->toContain("forceCreate([\n 'name' => \$serviceName,\n 'image' => \$image,\n 'service_id' => \$resource->id,\n ]);"); + ->toContain("create([\n 'name' => \$serviceName,\n 'service_id' => \$resource->id,\n ]);") + ->not->toContain("create([\n 'name' => \$serviceName,\n 'image' => \$image,\n 'service_id' => \$resource->id,\n ]);"); }); it('ensures service parser updates image after finding or creating service', function () { @@ -41,8 +41,8 @@ // The new code checks for null within the else block and creates only if needed expect($sharedFile) ->toContain('if (is_null($savedService)) {') - ->toContain('$savedService = ServiceDatabase::forceCreate([') - ->toContain('$savedService = ServiceApplication::forceCreate(['); + ->toContain('$savedService = ServiceDatabase::create([') + ->toContain('$savedService = ServiceApplication::create(['); }); it('verifies image update logic is present in parseDockerComposeFile', function () { diff --git a/tests/v4/Browser/DashboardTest.php b/tests/v4/Browser/DashboardTest.php index 233b0db9d..b4a97f268 100644 --- a/tests/v4/Browser/DashboardTest.php +++ b/tests/v4/Browser/DashboardTest.php @@ -77,21 +77,21 @@ ], ]); - Project::forceCreate([ + Project::create([ 'uuid' => 'project-1', 'name' => 'My first project', 'description' => 'This is a test project in development', 'team_id' => 0, ]); - Project::forceCreate([ + Project::create([ 'uuid' => 'project-2', 'name' => 'Production API', 'description' => 'Backend services for production', 'team_id' => 0, ]); - Project::forceCreate([ + Project::create([ 'uuid' => 'project-3', 'name' => 'Staging Environment', 'description' => 'Staging and QA testing',