refactor: use forceCreate() for internal model creation
Replace create() with forceCreate() across internal model creation operations to bypass mass assignment protection. This is appropriate for internal code that constructs complete model state without user input. Add InternalModelCreationMassAssignmentTest to ensure internal model creation behavior is properly tested. Optimize imports by using shortened Livewire attribute references and removing unused imports.
This commit is contained in:
parent
71cde5a063
commit
1da1f32f0e
41 changed files with 265 additions and 145 deletions
|
|
@ -49,7 +49,7 @@ public function handle(Server $server)
|
||||||
}');
|
}');
|
||||||
$found = StandaloneDocker::where('server_id', $server->id);
|
$found = StandaloneDocker::where('server_id', $server->id);
|
||||||
if ($found->count() == 0 && $server->id) {
|
if ($found->count() == 0 && $server->id) {
|
||||||
StandaloneDocker::create([
|
StandaloneDocker::forceCreate([
|
||||||
'name' => 'coolify',
|
'name' => 'coolify',
|
||||||
'network' => 'coolify',
|
'network' => 'coolify',
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ public function handle()
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
$preview = ApplicationPreview::all()->first();
|
$preview = ApplicationPreview::all()->first();
|
||||||
if (! $preview) {
|
if (! $preview) {
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::forceCreate([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => 1,
|
'pull_request_id' => 1,
|
||||||
'pull_request_html_url' => 'http://example.com',
|
'pull_request_html_url' => 'http://example.com',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Support\ValidationPatterns;
|
use App\Support\ValidationPatterns;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
|
@ -234,7 +235,7 @@ public function create_project(Request $request)
|
||||||
}
|
}
|
||||||
|
|
||||||
$return = validateIncomingRequest($request);
|
$return = validateIncomingRequest($request);
|
||||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
if ($return instanceof JsonResponse) {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
|
|
@ -257,7 +258,7 @@ public function create_project(Request $request)
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$project = Project::create([
|
$project = Project::forceCreate([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'description' => $request->description,
|
'description' => $request->description,
|
||||||
'team_id' => $teamId,
|
'team_id' => $teamId,
|
||||||
|
|
@ -347,7 +348,7 @@ public function update_project(Request $request)
|
||||||
}
|
}
|
||||||
|
|
||||||
$return = validateIncomingRequest($request);
|
$return = validateIncomingRequest($request);
|
||||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
if ($return instanceof JsonResponse) {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
|
|
@ -600,7 +601,7 @@ public function create_environment(Request $request)
|
||||||
}
|
}
|
||||||
|
|
||||||
$return = validateIncomingRequest($request);
|
$return = validateIncomingRequest($request);
|
||||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
if ($return instanceof JsonResponse) {
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
|
|
|
||||||
|
|
@ -432,7 +432,7 @@ public function create_service(Request $request)
|
||||||
if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) {
|
if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) {
|
||||||
data_set($servicePayload, 'connect_to_docker_network', true);
|
data_set($servicePayload, 'connect_to_docker_network', true);
|
||||||
}
|
}
|
||||||
$service = Service::create($servicePayload);
|
$service = Service::forceCreate($servicePayload);
|
||||||
$service->name = $request->name ?? "$oneClickServiceName-".$service->uuid;
|
$service->name = $request->name ?? "$oneClickServiceName-".$service->uuid;
|
||||||
$service->description = $request->description;
|
$service->description = $request->description;
|
||||||
if ($request->has('is_container_label_escape_enabled')) {
|
if ($request->has('is_container_label_escape_enabled')) {
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ public function manual(Request $request)
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
$pr_app = ApplicationPreview::create([
|
$pr_app = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'bitbucket',
|
'git_type' => 'bitbucket',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
|
|
@ -128,7 +128,7 @@ public function manual(Request $request)
|
||||||
]);
|
]);
|
||||||
$pr_app->generate_preview_fqdn_compose();
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
} else {
|
} else {
|
||||||
$pr_app = ApplicationPreview::create([
|
$pr_app = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'bitbucket',
|
'git_type' => 'bitbucket',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ public function manual(Request $request)
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
$pr_app = ApplicationPreview::create([
|
$pr_app = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'gitea',
|
'git_type' => 'gitea',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
|
|
@ -153,7 +153,7 @@ public function manual(Request $request)
|
||||||
]);
|
]);
|
||||||
$pr_app->generate_preview_fqdn_compose();
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
} else {
|
} else {
|
||||||
$pr_app = ApplicationPreview::create([
|
$pr_app = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'gitea',
|
'git_type' => 'gitea',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ public function manual(Request $request)
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
$pr_app = ApplicationPreview::create([
|
$pr_app = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'gitlab',
|
'git_type' => 'gitlab',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
|
|
@ -186,7 +186,7 @@ public function manual(Request $request)
|
||||||
]);
|
]);
|
||||||
$pr_app->generate_preview_fqdn_compose();
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
} else {
|
} else {
|
||||||
$pr_app = ApplicationPreview::create([
|
$pr_app = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'gitlab',
|
'git_type' => 'gitlab',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ private function handleOpenAction(Application $application, ?GithubApp $githubAp
|
||||||
|
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'github',
|
'git_type' => 'github',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $this->pullRequestId,
|
'pull_request_id' => $this->pullRequestId,
|
||||||
|
|
@ -127,7 +127,7 @@ private function handleOpenAction(Application $application, ?GithubApp $githubAp
|
||||||
]);
|
]);
|
||||||
$preview->generate_preview_fqdn_compose();
|
$preview->generate_preview_fqdn_compose();
|
||||||
} else {
|
} else {
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::forceCreate([
|
||||||
'git_type' => 'github',
|
'git_type' => 'github',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => $this->pullRequestId,
|
'pull_request_id' => $this->pullRequestId,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Services\ConfigurationRepository;
|
use App\Services\ConfigurationRepository;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Attributes\Url;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
|
@ -19,18 +20,18 @@ class Index extends Component
|
||||||
'prerequisitesInstalled' => 'handlePrerequisitesInstalled',
|
'prerequisitesInstalled' => 'handlePrerequisitesInstalled',
|
||||||
];
|
];
|
||||||
|
|
||||||
#[\Livewire\Attributes\Url(as: 'step', history: true)]
|
#[Url(as: 'step', history: true)]
|
||||||
public string $currentState = 'welcome';
|
public string $currentState = 'welcome';
|
||||||
|
|
||||||
#[\Livewire\Attributes\Url(keep: true)]
|
#[Url(keep: true)]
|
||||||
public ?string $selectedServerType = null;
|
public ?string $selectedServerType = null;
|
||||||
|
|
||||||
public ?Collection $privateKeys = null;
|
public ?Collection $privateKeys = null;
|
||||||
|
|
||||||
#[\Livewire\Attributes\Url(keep: true)]
|
#[Url(keep: true)]
|
||||||
public ?int $selectedExistingPrivateKey = null;
|
public ?int $selectedExistingPrivateKey = null;
|
||||||
|
|
||||||
#[\Livewire\Attributes\Url(keep: true)]
|
#[Url(keep: true)]
|
||||||
public ?string $privateKeyType = null;
|
public ?string $privateKeyType = null;
|
||||||
|
|
||||||
public ?string $privateKey = null;
|
public ?string $privateKey = null;
|
||||||
|
|
@ -45,7 +46,7 @@ class Index extends Component
|
||||||
|
|
||||||
public ?Collection $servers = null;
|
public ?Collection $servers = null;
|
||||||
|
|
||||||
#[\Livewire\Attributes\Url(keep: true)]
|
#[Url(keep: true)]
|
||||||
public ?int $selectedExistingServer = null;
|
public ?int $selectedExistingServer = null;
|
||||||
|
|
||||||
public ?string $remoteServerName = null;
|
public ?string $remoteServerName = null;
|
||||||
|
|
@ -66,7 +67,7 @@ class Index extends Component
|
||||||
|
|
||||||
public Collection $projects;
|
public Collection $projects;
|
||||||
|
|
||||||
#[\Livewire\Attributes\Url(keep: true)]
|
#[Url(keep: true)]
|
||||||
public ?int $selectedProject = null;
|
public ?int $selectedProject = null;
|
||||||
|
|
||||||
public ?Project $createdProject = null;
|
public ?Project $createdProject = null;
|
||||||
|
|
@ -440,7 +441,7 @@ public function selectExistingProject()
|
||||||
|
|
||||||
public function createNewProject()
|
public function createNewProject()
|
||||||
{
|
{
|
||||||
$this->createdProject = Project::create([
|
$this->createdProject = Project::forceCreate([
|
||||||
'name' => 'My first project',
|
'name' => 'My first project',
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
'uuid' => (string) new Cuid2,
|
'uuid' => (string) new Cuid2,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Support\ValidationPatterns;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Livewire\Attributes\Locked;
|
use Livewire\Attributes\Locked;
|
||||||
use Livewire\Attributes\Validate;
|
use Livewire\Attributes\Validate;
|
||||||
|
|
@ -78,7 +77,7 @@ public function submit()
|
||||||
if ($found) {
|
if ($found) {
|
||||||
throw new \Exception('Network already added to this server.');
|
throw new \Exception('Network already added to this server.');
|
||||||
} else {
|
} else {
|
||||||
$docker = SwarmDocker::create([
|
$docker = SwarmDocker::forceCreate([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'network' => $this->network,
|
'network' => $this->network,
|
||||||
'server_id' => $this->selectedServer->id,
|
'server_id' => $this->selectedServer->id,
|
||||||
|
|
@ -89,7 +88,7 @@ public function submit()
|
||||||
if ($found) {
|
if ($found) {
|
||||||
throw new \Exception('Network already added to this server.');
|
throw new \Exception('Network already added to this server.');
|
||||||
} else {
|
} else {
|
||||||
$docker = StandaloneDocker::create([
|
$docker = StandaloneDocker::forceCreate([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'network' => $this->network,
|
'network' => $this->network,
|
||||||
'server_id' => $this->selectedServer->id,
|
'server_id' => $this->selectedServer->id,
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$project = Project::create([
|
$project = Project::forceCreate([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null)
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found && ! is_null($pull_request_html_url)) {
|
if (! $found && ! is_null($pull_request_html_url)) {
|
||||||
$found = ApplicationPreview::create([
|
$found = ApplicationPreview::forceCreate([
|
||||||
'application_id' => $this->application->id,
|
'application_id' => $this->application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
|
@ -196,7 +196,7 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null)
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found && ! is_null($pull_request_html_url)) {
|
if (! $found && ! is_null($pull_request_html_url)) {
|
||||||
$found = ApplicationPreview::create([
|
$found = ApplicationPreview::forceCreate([
|
||||||
'application_id' => $this->application->id,
|
'application_id' => $this->application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
|
@ -236,7 +236,7 @@ public function deploy(int $pull_request_id, ?string $pull_request_html_url = nu
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found && ! is_null($pull_request_html_url)) {
|
if (! $found && ! is_null($pull_request_html_url)) {
|
||||||
ApplicationPreview::create([
|
ApplicationPreview::forceCreate([
|
||||||
'application_id' => $this->application->id,
|
'application_id' => $this->application->id,
|
||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ public function clone(string $type)
|
||||||
if ($foundProject) {
|
if ($foundProject) {
|
||||||
throw new \Exception('Project with the same name already exists.');
|
throw new \Exception('Project with the same name already exists.');
|
||||||
}
|
}
|
||||||
$project = Project::create([
|
$project = Project::forceCreate([
|
||||||
'name' => $this->newName,
|
'name' => $this->newName,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
'description' => $this->project->description.' (clone)',
|
'description' => $this->project->description.' (clone)',
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ public function submit()
|
||||||
}
|
}
|
||||||
$destination_class = $destination->getMorphClass();
|
$destination_class = $destination->getMorphClass();
|
||||||
|
|
||||||
$service = Service::create([
|
$service = Service::forceCreate([
|
||||||
'docker_compose_raw' => $this->dockerComposeRaw,
|
'docker_compose_raw' => $this->dockerComposeRaw,
|
||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'server_id' => (int) $server_id,
|
'server_id' => (int) $server_id,
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ public function submit()
|
||||||
// Determine the image tag based on whether it's a hash or regular tag
|
// Determine the image tag based on whether it's a hash or regular tag
|
||||||
$imageTag = $parser->isImageHash() ? 'sha256-'.$parser->getTag() : $parser->getTag();
|
$imageTag = $parser->isImageHash() ? 'sha256-'.$parser->getTag() : $parser->getTag();
|
||||||
|
|
||||||
$application = Application::create([
|
$application = Application::forceCreate([
|
||||||
'name' => 'docker-image-'.new Cuid2,
|
'name' => 'docker-image-'.new Cuid2,
|
||||||
'repository_project_id' => 0,
|
'repository_project_id' => 0,
|
||||||
'git_repository' => 'coollabsio/coolify',
|
'git_repository' => 'coollabsio/coolify',
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ class EmptyProject extends Component
|
||||||
{
|
{
|
||||||
public function createEmptyProject()
|
public function createEmptyProject()
|
||||||
{
|
{
|
||||||
$project = Project::create([
|
$project = Project::forceCreate([
|
||||||
'name' => generate_random_name(),
|
'name' => generate_random_name(),
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
'uuid' => (string) new Cuid2,
|
'uuid' => (string) new Cuid2,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Rules\ValidGitBranch;
|
use App\Rules\ValidGitBranch;
|
||||||
|
use App\Support\ValidationPatterns;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
@ -168,7 +169,7 @@ public function submit()
|
||||||
'selected_repository_owner' => 'required|string|regex:/^[a-zA-Z0-9\-_]+$/',
|
'selected_repository_owner' => 'required|string|regex:/^[a-zA-Z0-9\-_]+$/',
|
||||||
'selected_repository_repo' => 'required|string|regex:/^[a-zA-Z0-9\-_\.]+$/',
|
'selected_repository_repo' => 'required|string|regex:/^[a-zA-Z0-9\-_\.]+$/',
|
||||||
'selected_branch_name' => ['required', 'string', new ValidGitBranch],
|
'selected_branch_name' => ['required', 'string', new ValidGitBranch],
|
||||||
'docker_compose_location' => \App\Support\ValidationPatterns::filePathRules(),
|
'docker_compose_location' => ValidationPatterns::filePathRules(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
|
|
@ -188,7 +189,7 @@ public function submit()
|
||||||
$project = Project::ownedByCurrentTeam()->where('uuid', $this->parameters['project_uuid'])->firstOrFail();
|
$project = Project::ownedByCurrentTeam()->where('uuid', $this->parameters['project_uuid'])->firstOrFail();
|
||||||
$environment = $project->environments()->where('uuid', $this->parameters['environment_uuid'])->firstOrFail();
|
$environment = $project->environments()->where('uuid', $this->parameters['environment_uuid'])->firstOrFail();
|
||||||
|
|
||||||
$application = Application::create([
|
$application = Application::forceCreate([
|
||||||
'name' => generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name),
|
'name' => generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name),
|
||||||
'repository_project_id' => $this->selected_repository_id,
|
'repository_project_id' => $this->selected_repository_id,
|
||||||
'git_repository' => str($this->selected_repository_owner)->trim()->toString().'/'.str($this->selected_repository_repo)->trim()->toString(),
|
'git_repository' => str($this->selected_repository_owner)->trim()->toString().'/'.str($this->selected_repository_repo)->trim()->toString(),
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Rules\ValidGitBranch;
|
use App\Rules\ValidGitBranch;
|
||||||
use App\Rules\ValidGitRepositoryUrl;
|
use App\Rules\ValidGitRepositoryUrl;
|
||||||
|
use App\Support\ValidationPatterns;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
|
@ -66,7 +67,7 @@ protected function rules()
|
||||||
'is_static' => 'required|boolean',
|
'is_static' => 'required|boolean',
|
||||||
'publish_directory' => 'nullable|string',
|
'publish_directory' => 'nullable|string',
|
||||||
'build_pack' => 'required|string',
|
'build_pack' => 'required|string',
|
||||||
'docker_compose_location' => \App\Support\ValidationPatterns::filePathRules(),
|
'docker_compose_location' => ValidationPatterns::filePathRules(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,7 +183,7 @@ public function submit()
|
||||||
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
||||||
$application_init['base_directory'] = $this->base_directory;
|
$application_init['base_directory'] = $this->base_directory;
|
||||||
}
|
}
|
||||||
$application = Application::create($application_init);
|
$application = Application::forceCreate($application_init);
|
||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Rules\ValidGitBranch;
|
use App\Rules\ValidGitBranch;
|
||||||
use App\Rules\ValidGitRepositoryUrl;
|
use App\Rules\ValidGitRepositoryUrl;
|
||||||
|
use App\Support\ValidationPatterns;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
|
@ -72,7 +73,7 @@ protected function rules()
|
||||||
'publish_directory' => 'nullable|string',
|
'publish_directory' => 'nullable|string',
|
||||||
'build_pack' => 'required|string',
|
'build_pack' => 'required|string',
|
||||||
'base_directory' => 'nullable|string',
|
'base_directory' => 'nullable|string',
|
||||||
'docker_compose_location' => \App\Support\ValidationPatterns::filePathRules(),
|
'docker_compose_location' => ValidationPatterns::filePathRules(),
|
||||||
'git_branch' => ['required', 'string', new ValidGitBranch],
|
'git_branch' => ['required', 'string', new ValidGitBranch],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +234,7 @@ private function getBranch()
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->git_source->getMorphClass() === \App\Models\GithubApp::class) {
|
if ($this->git_source->getMorphClass() === GithubApp::class) {
|
||||||
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
||||||
$this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s');
|
$this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s');
|
||||||
$this->branchFound = true;
|
$this->branchFound = true;
|
||||||
|
|
@ -298,7 +299,7 @@ public function submit()
|
||||||
$new_service['source_id'] = $this->git_source->id;
|
$new_service['source_id'] = $this->git_source->id;
|
||||||
$new_service['source_type'] = $this->git_source->getMorphClass();
|
$new_service['source_type'] = $this->git_source->getMorphClass();
|
||||||
}
|
}
|
||||||
$service = Service::create($new_service);
|
$service = Service::forceCreate($new_service);
|
||||||
|
|
||||||
return redirect()->route('project.service.configuration', [
|
return redirect()->route('project.service.configuration', [
|
||||||
'service_uuid' => $service->uuid,
|
'service_uuid' => $service->uuid,
|
||||||
|
|
@ -345,7 +346,7 @@ public function submit()
|
||||||
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
||||||
$application_init['base_directory'] = $this->base_directory;
|
$application_init['base_directory'] = $this->base_directory;
|
||||||
}
|
}
|
||||||
$application = Application::create($application_init);
|
$application = Application::forceCreate($application_init);
|
||||||
|
|
||||||
$application->settings->is_static = $this->isStatic;
|
$application->settings->is_static = $this->isStatic;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ public function submit()
|
||||||
if (! $port) {
|
if (! $port) {
|
||||||
$port = 80;
|
$port = 80;
|
||||||
}
|
}
|
||||||
$application = Application::create([
|
$application = Application::forceCreate([
|
||||||
'name' => 'dockerfile-'.new Cuid2,
|
'name' => 'dockerfile-'.new Cuid2,
|
||||||
'repository_project_id' => 0,
|
'repository_project_id' => 0,
|
||||||
'git_repository' => 'coollabsio/coolify',
|
'git_repository' => 'coollabsio/coolify',
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ public function mount()
|
||||||
if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) {
|
if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) {
|
||||||
data_set($service_payload, 'connect_to_docker_network', true);
|
data_set($service_payload, 'connect_to_docker_network', true);
|
||||||
}
|
}
|
||||||
$service = Service::create($service_payload);
|
$service = Service::forceCreate($service_payload);
|
||||||
$service->name = "$oneClickServiceName-".$service->uuid;
|
$service->name = "$oneClickServiceName-".$service->uuid;
|
||||||
$service->save();
|
$service->save();
|
||||||
if ($oneClickDotEnvs?->count() > 0) {
|
if ($oneClickDotEnvs?->count() > 0) {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$environment = Environment::create([
|
$environment = Environment::forceCreate([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'project_id' => $this->project->id,
|
'project_id' => $this->project->id,
|
||||||
'uuid' => (string) new Cuid2,
|
'uuid' => (string) new Cuid2,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public function add($name)
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
SwarmDocker::create([
|
SwarmDocker::forceCreate([
|
||||||
'name' => $this->server->name.'-'.$name,
|
'name' => $this->server->name.'-'.$name,
|
||||||
'network' => $this->name,
|
'network' => $this->name,
|
||||||
'server_id' => $this->server->id,
|
'server_id' => $this->server->id,
|
||||||
|
|
@ -57,7 +57,7 @@ public function add($name)
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
StandaloneDocker::create([
|
StandaloneDocker::forceCreate([
|
||||||
'name' => $this->server->name.'-'.$name,
|
'name' => $this->server->name.'-'.$name,
|
||||||
'network' => $name,
|
'network' => $name,
|
||||||
'server_id' => $this->server->id,
|
'server_id' => $this->server->id,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Livewire\Attributes\Locked;
|
use Livewire\Attributes\Locked;
|
||||||
use Livewire\Attributes\Validate;
|
use Livewire\Attributes\Validate;
|
||||||
|
|
@ -82,7 +83,7 @@ public function addCoolifyDatabase()
|
||||||
$postgres_password = $envs['POSTGRES_PASSWORD'];
|
$postgres_password = $envs['POSTGRES_PASSWORD'];
|
||||||
$postgres_user = $envs['POSTGRES_USER'];
|
$postgres_user = $envs['POSTGRES_USER'];
|
||||||
$postgres_db = $envs['POSTGRES_DB'];
|
$postgres_db = $envs['POSTGRES_DB'];
|
||||||
$this->database = StandalonePostgresql::create([
|
$this->database = StandalonePostgresql::forceCreate([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => 'coolify-db',
|
'name' => 'coolify-db',
|
||||||
'description' => 'Coolify database',
|
'description' => 'Coolify database',
|
||||||
|
|
@ -90,7 +91,7 @@ public function addCoolifyDatabase()
|
||||||
'postgres_password' => $postgres_password,
|
'postgres_password' => $postgres_password,
|
||||||
'postgres_db' => $postgres_db,
|
'postgres_db' => $postgres_db,
|
||||||
'status' => 'running',
|
'status' => 'running',
|
||||||
'destination_type' => \App\Models\StandaloneDocker::class,
|
'destination_type' => StandaloneDocker::class,
|
||||||
'destination_id' => 0,
|
'destination_id' => 0,
|
||||||
]);
|
]);
|
||||||
$this->backup = ScheduledDatabaseBackup::create([
|
$this->backup = ScheduledDatabaseBackup::create([
|
||||||
|
|
@ -99,7 +100,7 @@ public function addCoolifyDatabase()
|
||||||
'save_s3' => false,
|
'save_s3' => false,
|
||||||
'frequency' => '0 0 * * *',
|
'frequency' => '0 0 * * *',
|
||||||
'database_id' => $this->database->id,
|
'database_id' => $this->database->id,
|
||||||
'database_type' => \App\Models\StandalonePostgresql::class,
|
'database_type' => StandalonePostgresql::class,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ protected static function booted()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
static::created(function ($application) {
|
static::created(function ($application) {
|
||||||
ApplicationSetting::create([
|
ApplicationSetting::forceCreate([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
]);
|
]);
|
||||||
$application->compose_parsing_version = self::$parserVersion;
|
$application->compose_parsing_version = self::$parserVersion;
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@ public static function ownedByCurrentTeamCached()
|
||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
static::created(function ($project) {
|
static::created(function ($project) {
|
||||||
ProjectSetting::create([
|
ProjectSetting::forceCreate([
|
||||||
'project_id' => $project->id,
|
'project_id' => $project->id,
|
||||||
]);
|
]);
|
||||||
Environment::create([
|
Environment::forceCreate([
|
||||||
'name' => 'production',
|
'name' => 'production',
|
||||||
'project_id' => $project->id,
|
'project_id' => $project->id,
|
||||||
'uuid' => (string) new Cuid2,
|
'uuid' => (string) new Cuid2,
|
||||||
|
|
|
||||||
|
|
@ -143,19 +143,19 @@ protected static function booted()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
static::created(function ($server) {
|
static::created(function ($server) {
|
||||||
ServerSetting::create([
|
ServerSetting::forceCreate([
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
if ($server->id === 0) {
|
if ($server->id === 0) {
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
SwarmDocker::create([
|
SwarmDocker::forceCreate([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => 'coolify',
|
'name' => 'coolify',
|
||||||
'network' => 'coolify-overlay',
|
'network' => 'coolify-overlay',
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
StandaloneDocker::create([
|
StandaloneDocker::forceCreate([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => 'coolify',
|
'name' => 'coolify',
|
||||||
'network' => 'coolify',
|
'network' => 'coolify',
|
||||||
|
|
@ -164,13 +164,14 @@ protected static function booted()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
SwarmDocker::create([
|
SwarmDocker::forceCreate([
|
||||||
'name' => 'coolify-overlay',
|
'name' => 'coolify-overlay',
|
||||||
'network' => 'coolify-overlay',
|
'network' => 'coolify-overlay',
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$standaloneDocker = new StandaloneDocker([
|
$standaloneDocker = new StandaloneDocker;
|
||||||
|
$standaloneDocker->forceFill([
|
||||||
'name' => 'coolify',
|
'name' => 'coolify',
|
||||||
'uuid' => (string) new Cuid2,
|
'uuid' => (string) new Cuid2,
|
||||||
'network' => 'coolify',
|
'network' => 'coolify',
|
||||||
|
|
|
||||||
|
|
@ -22,25 +22,25 @@
|
||||||
*
|
*
|
||||||
* @param string $composeYaml The raw Docker Compose YAML content
|
* @param string $composeYaml The raw Docker Compose YAML content
|
||||||
*
|
*
|
||||||
* @throws \Exception If the compose file contains command injection attempts
|
* @throws Exception If the compose file contains command injection attempts
|
||||||
*/
|
*/
|
||||||
function validateDockerComposeForInjection(string $composeYaml): void
|
function validateDockerComposeForInjection(string $composeYaml): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$parsed = Yaml::parse($composeYaml);
|
$parsed = Yaml::parse($composeYaml);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception('Invalid YAML format: '.$e->getMessage(), 0, $e);
|
throw new Exception('Invalid YAML format: '.$e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! is_array($parsed) || ! isset($parsed['services']) || ! is_array($parsed['services'])) {
|
if (! is_array($parsed) || ! isset($parsed['services']) || ! is_array($parsed['services'])) {
|
||||||
throw new \Exception('Docker Compose file must contain a "services" section');
|
throw new Exception('Docker Compose file must contain a "services" section');
|
||||||
}
|
}
|
||||||
// Validate service names
|
// Validate service names
|
||||||
foreach ($parsed['services'] as $serviceName => $serviceConfig) {
|
foreach ($parsed['services'] as $serviceName => $serviceConfig) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($serviceName, 'service name');
|
validateShellSafePath($serviceName, 'service name');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker Compose service name: '.$e->getMessage().
|
'Invalid Docker Compose service name: '.$e->getMessage().
|
||||||
' Service names must not contain shell metacharacters.',
|
' Service names must not contain shell metacharacters.',
|
||||||
0,
|
0,
|
||||||
|
|
@ -68,8 +68,8 @@ function validateDockerComposeForInjection(string $composeYaml): void
|
||||||
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
|
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($source, 'volume source');
|
validateShellSafePath($source, 'volume source');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.',
|
' Please use safe path names without shell metacharacters.',
|
||||||
0,
|
0,
|
||||||
|
|
@ -84,8 +84,8 @@ function validateDockerComposeForInjection(string $composeYaml): void
|
||||||
if (is_string($target)) {
|
if (is_string($target)) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($target, 'volume target');
|
validateShellSafePath($target, 'volume target');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.',
|
' Please use safe path names without shell metacharacters.',
|
||||||
0,
|
0,
|
||||||
|
|
@ -105,7 +105,7 @@ function validateDockerComposeForInjection(string $composeYaml): void
|
||||||
*
|
*
|
||||||
* @param string $volumeString The volume string to validate
|
* @param string $volumeString The volume string to validate
|
||||||
*
|
*
|
||||||
* @throws \Exception If the volume string contains command injection attempts
|
* @throws Exception If the volume string contains command injection attempts
|
||||||
*/
|
*/
|
||||||
function validateVolumeStringForInjection(string $volumeString): void
|
function validateVolumeStringForInjection(string $volumeString): void
|
||||||
{
|
{
|
||||||
|
|
@ -325,9 +325,9 @@ function parseDockerVolumeString(string $volumeString): array
|
||||||
if (! $isSimpleEnvVar && ! $isEnvVarWithPath) {
|
if (! $isSimpleEnvVar && ! $isEnvVarWithPath) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($sourceStr, 'volume source');
|
validateShellSafePath($sourceStr, 'volume source');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
// Re-throw with more context about the volume string
|
// Re-throw with more context about the volume string
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition: '.$e->getMessage().
|
'Invalid Docker volume definition: '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.'
|
' Please use safe path names without shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -343,8 +343,8 @@ function parseDockerVolumeString(string $volumeString): array
|
||||||
// Still, defense in depth is important
|
// Still, defense in depth is important
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($targetStr, 'volume target');
|
validateShellSafePath($targetStr, 'volume target');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition: '.$e->getMessage().
|
'Invalid Docker volume definition: '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.'
|
' Please use safe path names without shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -375,7 +375,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$yaml = Yaml::parse($compose);
|
$yaml = Yaml::parse($compose);
|
||||||
} catch (\Exception) {
|
} catch (Exception) {
|
||||||
return collect([]);
|
return collect([]);
|
||||||
}
|
}
|
||||||
$services = data_get($yaml, 'services', collect([]));
|
$services = data_get($yaml, 'services', collect([]));
|
||||||
|
|
@ -409,8 +409,8 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||||
// Validate service name for command injection
|
// Validate service name for command injection
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($serviceName, 'service name');
|
validateShellSafePath($serviceName, 'service name');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker Compose service name: '.$e->getMessage().
|
'Invalid Docker Compose service name: '.$e->getMessage().
|
||||||
' Service names must not contain shell metacharacters.'
|
' Service names must not contain shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -465,7 +465,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||||
$fqdn = generateFqdn(server: $server, random: "$uuid", parserVersion: $resource->compose_parsing_version);
|
$fqdn = generateFqdn(server: $server, random: "$uuid", parserVersion: $resource->compose_parsing_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value && get_class($value) === \Illuminate\Support\Stringable::class && $value->startsWith('/')) {
|
if ($value && get_class($value) === Illuminate\Support\Stringable::class && $value->startsWith('/')) {
|
||||||
$path = $value->value();
|
$path = $value->value();
|
||||||
if ($path !== '/') {
|
if ($path !== '/') {
|
||||||
$fqdn = "$fqdn$path";
|
$fqdn = "$fqdn$path";
|
||||||
|
|
@ -738,8 +738,8 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||||
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
|
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($sourceValue, 'volume source');
|
validateShellSafePath($sourceValue, 'volume source');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.'
|
' Please use safe path names without shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -749,8 +749,8 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||||
if ($target !== null && ! empty($target->value())) {
|
if ($target !== null && ! empty($target->value())) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($target->value(), 'volume target');
|
validateShellSafePath($target->value(), 'volume target');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.'
|
' Please use safe path names without shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -1489,7 +1489,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
|
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
// If parsing fails, keep the original docker_compose_raw unchanged
|
// If parsing fails, keep the original docker_compose_raw unchanged
|
||||||
ray('Failed to update docker_compose_raw in applicationParser: '.$e->getMessage());
|
ray('Failed to update docker_compose_raw in applicationParser: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -1519,7 +1519,7 @@ function serviceParser(Service $resource): Collection
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$yaml = Yaml::parse($compose);
|
$yaml = Yaml::parse($compose);
|
||||||
} catch (\Exception) {
|
} catch (Exception) {
|
||||||
return collect([]);
|
return collect([]);
|
||||||
}
|
}
|
||||||
$services = data_get($yaml, 'services', collect([]));
|
$services = data_get($yaml, 'services', collect([]));
|
||||||
|
|
@ -1566,8 +1566,8 @@ function serviceParser(Service $resource): Collection
|
||||||
// Validate service name for command injection
|
// Validate service name for command injection
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($serviceName, 'service name');
|
validateShellSafePath($serviceName, 'service name');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker Compose service name: '.$e->getMessage().
|
'Invalid Docker Compose service name: '.$e->getMessage().
|
||||||
' Service names must not contain shell metacharacters.'
|
' Service names must not contain shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -1593,20 +1593,25 @@ function serviceParser(Service $resource): Collection
|
||||||
// Use image detection for non-migrated services
|
// Use image detection for non-migrated services
|
||||||
$isDatabase = isDatabaseImage($image, $service);
|
$isDatabase = isDatabaseImage($image, $service);
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first();
|
$databaseFound = ServiceDatabase::where('name', $serviceName)->where('service_id', $resource->id)->first();
|
||||||
if ($applicationFound) {
|
if ($databaseFound) {
|
||||||
$savedService = $applicationFound;
|
$savedService = $databaseFound;
|
||||||
} else {
|
} else {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::forceCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
'service_id' => $resource->id,
|
'service_id' => $resource->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$savedService = ServiceApplication::firstOrCreate([
|
$applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first();
|
||||||
'name' => $serviceName,
|
if ($applicationFound) {
|
||||||
'service_id' => $resource->id,
|
$savedService = $applicationFound;
|
||||||
]);
|
} else {
|
||||||
|
$savedService = ServiceApplication::forceCreate([
|
||||||
|
'name' => $serviceName,
|
||||||
|
'service_id' => $resource->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update image if it changed
|
// Update image if it changed
|
||||||
|
|
@ -1772,7 +1777,7 @@ function serviceParser(Service $resource): Collection
|
||||||
// Strip scheme for environment variable values
|
// Strip scheme for environment variable values
|
||||||
$fqdnValueForEnv = str($fqdn)->after('://')->value();
|
$fqdnValueForEnv = str($fqdn)->after('://')->value();
|
||||||
|
|
||||||
if ($value && get_class($value) === \Illuminate\Support\Stringable::class && $value->startsWith('/')) {
|
if ($value && get_class($value) === Illuminate\Support\Stringable::class && $value->startsWith('/')) {
|
||||||
$path = $value->value();
|
$path = $value->value();
|
||||||
if ($path !== '/') {
|
if ($path !== '/') {
|
||||||
// Only add path if it's not already present (prevents duplication on subsequent parse() calls)
|
// Only add path if it's not already present (prevents duplication on subsequent parse() calls)
|
||||||
|
|
@ -2120,8 +2125,8 @@ function serviceParser(Service $resource): Collection
|
||||||
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
|
if (! $isSimpleEnvVar && ! $isEnvVarWithDefault && ! $isEnvVarWithPath) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($sourceValue, 'volume source');
|
validateShellSafePath($sourceValue, 'volume source');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.'
|
' Please use safe path names without shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -2131,8 +2136,8 @@ function serviceParser(Service $resource): Collection
|
||||||
if ($target !== null && ! empty($target->value())) {
|
if ($target !== null && ! empty($target->value())) {
|
||||||
try {
|
try {
|
||||||
validateShellSafePath($target->value(), 'volume target');
|
validateShellSafePath($target->value(), 'volume target');
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception(
|
throw new Exception(
|
||||||
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
'Invalid Docker volume definition (array syntax): '.$e->getMessage().
|
||||||
' Please use safe path names without shell metacharacters.'
|
' Please use safe path names without shell metacharacters.'
|
||||||
);
|
);
|
||||||
|
|
@ -2741,7 +2746,7 @@ function serviceParser(Service $resource): Collection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
|
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
// If parsing fails, keep the original docker_compose_raw unchanged
|
// If parsing fails, keep the original docker_compose_raw unchanged
|
||||||
ray('Failed to update docker_compose_raw in serviceParser: '.$e->getMessage());
|
ray('Failed to update docker_compose_raw in serviceParser: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2722,8 +2722,7 @@
|
||||||
},
|
},
|
||||||
"is_preserve_repository_enabled": {
|
"is_preserve_repository_enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"description": "Preserve git repository during application update. If false, the existing repository will be removed and replaced with the new one. If true, the existing repository will be kept and the new one will be ignored. Default is false."
|
||||||
"description": "Preserve repository during deployment."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
|
|
||||||
|
|
@ -1755,8 +1755,7 @@ paths:
|
||||||
description: 'Escape special characters in labels. By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$. If you want to use env variables inside the labels, turn this off.'
|
description: 'Escape special characters in labels. By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$. If you want to use env variables inside the labels, turn this off.'
|
||||||
is_preserve_repository_enabled:
|
is_preserve_repository_enabled:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
description: 'Preserve git repository during application update. If false, the existing repository will be removed and replaced with the new one. If true, the existing repository will be kept and the new one will be ignored. Default is false.'
|
||||||
description: 'Preserve repository during deployment.'
|
|
||||||
type: object
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -25,13 +25,13 @@
|
||||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||||
|
|
||||||
StandaloneDocker::withoutEvents(function () {
|
StandaloneDocker::withoutEvents(function () {
|
||||||
$this->destination = StandaloneDocker::firstOrCreate(
|
$this->destination = $this->server->standaloneDockers()->firstOrCreate(
|
||||||
['server_id' => $this->server->id, 'network' => 'coolify'],
|
['network' => 'coolify'],
|
||||||
['uuid' => (string) new Cuid2, 'name' => 'test-docker']
|
['uuid' => (string) new Cuid2, 'name' => 'test-docker']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->project = Project::create([
|
$this->project = Project::forceCreate([
|
||||||
'uuid' => (string) new Cuid2,
|
'uuid' => (string) new Cuid2,
|
||||||
'name' => 'test-project',
|
'name' => 'test-project',
|
||||||
'team_id' => $this->team->id,
|
'team_id' => $this->team->id,
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,10 @@
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::forceCreate([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => 42,
|
'pull_request_id' => 42,
|
||||||
|
'pull_request_html_url' => 'https://github.com/example/repo/pull/42',
|
||||||
'docker_compose_domains' => $application->docker_compose_domains,
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -38,9 +39,10 @@
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::forceCreate([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => 7,
|
'pull_request_id' => 7,
|
||||||
|
'pull_request_html_url' => 'https://github.com/example/repo/pull/7',
|
||||||
'docker_compose_domains' => $application->docker_compose_domains,
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -63,9 +65,10 @@
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::forceCreate([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => 99,
|
'pull_request_id' => 99,
|
||||||
|
'pull_request_html_url' => 'https://github.com/example/repo/pull/99',
|
||||||
'docker_compose_domains' => $application->docker_compose_domains,
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
function createDatabase($context): StandalonePostgresql
|
function createDatabase($context): StandalonePostgresql
|
||||||
{
|
{
|
||||||
return StandalonePostgresql::create([
|
return StandalonePostgresql::forceCreate([
|
||||||
'name' => 'test-postgres',
|
'name' => 'test-postgres',
|
||||||
'image' => 'postgres:15-alpine',
|
'image' => 'postgres:15-alpine',
|
||||||
'postgres_user' => 'postgres',
|
'postgres_user' => 'postgres',
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
describe('PATCH /api/v1/databases', function () {
|
describe('PATCH /api/v1/databases', function () {
|
||||||
test('updates public_port_timeout on a postgresql database', function () {
|
test('updates public_port_timeout on a postgresql database', function () {
|
||||||
$database = StandalonePostgresql::create([
|
$database = StandalonePostgresql::forceCreate([
|
||||||
'name' => 'test-postgres',
|
'name' => 'test-postgres',
|
||||||
'image' => 'postgres:15-alpine',
|
'image' => 'postgres:15-alpine',
|
||||||
'postgres_user' => 'postgres',
|
'postgres_user' => 'postgres',
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
test('updates public_port_timeout on a redis database', function () {
|
test('updates public_port_timeout on a redis database', function () {
|
||||||
$database = StandaloneRedis::create([
|
$database = StandaloneRedis::forceCreate([
|
||||||
'name' => 'test-redis',
|
'name' => 'test-redis',
|
||||||
'image' => 'redis:7',
|
'image' => 'redis:7',
|
||||||
'redis_password' => 'password',
|
'redis_password' => 'password',
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
test('rejects invalid public_port_timeout value', function () {
|
test('rejects invalid public_port_timeout value', function () {
|
||||||
$database = StandalonePostgresql::create([
|
$database = StandalonePostgresql::forceCreate([
|
||||||
'name' => 'test-postgres',
|
'name' => 'test-postgres',
|
||||||
'image' => 'postgres:15-alpine',
|
'image' => 'postgres:15-alpine',
|
||||||
'postgres_user' => 'postgres',
|
'postgres_user' => 'postgres',
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
test('accepts null public_port_timeout', function () {
|
test('accepts null public_port_timeout', function () {
|
||||||
$database = StandalonePostgresql::create([
|
$database = StandalonePostgresql::forceCreate([
|
||||||
'name' => 'test-postgres',
|
'name' => 'test-postgres',
|
||||||
'image' => 'postgres:15-alpine',
|
'image' => 'postgres:15-alpine',
|
||||||
'postgres_user' => 'postgres',
|
'postgres_user' => 'postgres',
|
||||||
|
|
|
||||||
73
tests/Feature/InternalModelCreationMassAssignmentTest.php
Normal file
73
tests/Feature/InternalModelCreationMassAssignmentTest.php
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationSetting;
|
||||||
|
use App\Models\Environment;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\Team;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
it('creates application settings for internally created applications', function () {
|
||||||
|
$team = Team::factory()->create();
|
||||||
|
$project = Project::factory()->create([
|
||||||
|
'team_id' => $team->id,
|
||||||
|
]);
|
||||||
|
$environment = Environment::factory()->create([
|
||||||
|
'project_id' => $project->id,
|
||||||
|
]);
|
||||||
|
$server = Server::factory()->create([
|
||||||
|
'team_id' => $team->id,
|
||||||
|
]);
|
||||||
|
$destination = $server->standaloneDockers()->firstOrFail();
|
||||||
|
|
||||||
|
$application = Application::forceCreate([
|
||||||
|
'name' => 'internal-app',
|
||||||
|
'git_repository' => 'https://github.com/coollabsio/coolify',
|
||||||
|
'git_branch' => 'main',
|
||||||
|
'build_pack' => 'nixpacks',
|
||||||
|
'ports_exposes' => '3000',
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$setting = ApplicationSetting::query()
|
||||||
|
->where('application_id', $application->id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
expect($application->environment_id)->toBe($environment->id);
|
||||||
|
expect($setting)->not->toBeNull();
|
||||||
|
expect($setting?->application_id)->toBe($application->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates services with protected relationship ids in trusted internal paths', function () {
|
||||||
|
$team = Team::factory()->create();
|
||||||
|
$project = Project::factory()->create([
|
||||||
|
'team_id' => $team->id,
|
||||||
|
]);
|
||||||
|
$environment = Environment::factory()->create([
|
||||||
|
'project_id' => $project->id,
|
||||||
|
]);
|
||||||
|
$server = Server::factory()->create([
|
||||||
|
'team_id' => $team->id,
|
||||||
|
]);
|
||||||
|
$destination = $server->standaloneDockers()->firstOrFail();
|
||||||
|
|
||||||
|
$service = Service::forceCreate([
|
||||||
|
'docker_compose_raw' => 'services: {}',
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'server_id' => $server->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
|
'service_type' => 'test-service',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($service->environment_id)->toBe($environment->id);
|
||||||
|
expect($service->server_id)->toBe($server->id);
|
||||||
|
expect($service->destination_id)->toBe($destination->id);
|
||||||
|
expect($service->destination_type)->toBe($destination->getMorphClass());
|
||||||
|
});
|
||||||
|
|
@ -7,25 +7,26 @@
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
it('returns the correct team through the service relationship chain', function () {
|
it('returns the correct team through the service relationship chain', function () {
|
||||||
$team = Team::factory()->create();
|
$team = Team::factory()->create();
|
||||||
|
|
||||||
$project = Project::create([
|
$project = Project::forceCreate([
|
||||||
'uuid' => (string) Illuminate\Support\Str::uuid(),
|
'uuid' => (string) Str::uuid(),
|
||||||
'name' => 'Test Project',
|
'name' => 'Test Project',
|
||||||
'team_id' => $team->id,
|
'team_id' => $team->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$environment = Environment::create([
|
$environment = Environment::forceCreate([
|
||||||
'name' => 'test-env-'.Illuminate\Support\Str::random(8),
|
'name' => 'test-env-'.Str::random(8),
|
||||||
'project_id' => $project->id,
|
'project_id' => $project->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$service = Service::create([
|
$service = Service::forceCreate([
|
||||||
'uuid' => (string) Illuminate\Support\Str::uuid(),
|
'uuid' => (string) Str::uuid(),
|
||||||
'name' => 'supabase',
|
'name' => 'supabase',
|
||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => 1,
|
'destination_id' => 1,
|
||||||
|
|
@ -33,8 +34,8 @@
|
||||||
'docker_compose_raw' => 'version: "3"',
|
'docker_compose_raw' => 'version: "3"',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$serviceDatabase = ServiceDatabase::create([
|
$serviceDatabase = ServiceDatabase::forceCreate([
|
||||||
'uuid' => (string) Illuminate\Support\Str::uuid(),
|
'uuid' => (string) Str::uuid(),
|
||||||
'name' => 'supabase-db',
|
'name' => 'supabase-db',
|
||||||
'service_id' => $service->id,
|
'service_id' => $service->id,
|
||||||
]);
|
]);
|
||||||
|
|
@ -46,19 +47,19 @@
|
||||||
it('returns the correct team for ServiceApplication through the service relationship chain', function () {
|
it('returns the correct team for ServiceApplication through the service relationship chain', function () {
|
||||||
$team = Team::factory()->create();
|
$team = Team::factory()->create();
|
||||||
|
|
||||||
$project = Project::create([
|
$project = Project::forceCreate([
|
||||||
'uuid' => (string) Illuminate\Support\Str::uuid(),
|
'uuid' => (string) Str::uuid(),
|
||||||
'name' => 'Test Project',
|
'name' => 'Test Project',
|
||||||
'team_id' => $team->id,
|
'team_id' => $team->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$environment = Environment::create([
|
$environment = Environment::forceCreate([
|
||||||
'name' => 'test-env-'.Illuminate\Support\Str::random(8),
|
'name' => 'test-env-'.Str::random(8),
|
||||||
'project_id' => $project->id,
|
'project_id' => $project->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$service = Service::create([
|
$service = Service::forceCreate([
|
||||||
'uuid' => (string) Illuminate\Support\Str::uuid(),
|
'uuid' => (string) Str::uuid(),
|
||||||
'name' => 'supabase',
|
'name' => 'supabase',
|
||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => 1,
|
'destination_id' => 1,
|
||||||
|
|
@ -66,8 +67,8 @@
|
||||||
'docker_compose_raw' => 'version: "3"',
|
'docker_compose_raw' => 'version: "3"',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$serviceApplication = ServiceApplication::create([
|
$serviceApplication = ServiceApplication::forceCreate([
|
||||||
'uuid' => (string) Illuminate\Support\Str::uuid(),
|
'uuid' => (string) Str::uuid(),
|
||||||
'name' => 'supabase-studio',
|
'name' => 'supabase-studio',
|
||||||
'service_id' => $service->id,
|
'service_id' => $service->id,
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ function createTestApplication($context): Application
|
||||||
|
|
||||||
function createTestDatabase($context): StandalonePostgresql
|
function createTestDatabase($context): StandalonePostgresql
|
||||||
{
|
{
|
||||||
return StandalonePostgresql::create([
|
return StandalonePostgresql::forceCreate([
|
||||||
'name' => 'test-postgres',
|
'name' => 'test-postgres',
|
||||||
'image' => 'postgres:15-alpine',
|
'image' => 'postgres:15-alpine',
|
||||||
'postgres_user' => 'postgres',
|
'postgres_user' => 'postgres',
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,24 @@
|
||||||
* These tests verify the fix for the issue where changing an image in a
|
* These tests verify the fix for the issue where changing an image in a
|
||||||
* docker-compose file would create a new service instead of updating the existing one.
|
* docker-compose file would create a new service instead of updating the existing one.
|
||||||
*/
|
*/
|
||||||
it('ensures service parser does not include image in firstOrCreate query', function () {
|
it('ensures service parser does not include image in trusted service creation query', function () {
|
||||||
// Read the serviceParser function from parsers.php
|
// Read the serviceParser function from parsers.php
|
||||||
$parsersFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/parsers.php');
|
$parsersFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/parsers.php');
|
||||||
|
|
||||||
// Check that firstOrCreate is called with only name and service_id
|
// Check that trusted creation only uses name and service_id
|
||||||
// and NOT with image parameter in the ServiceApplication presave loop
|
// and does not include image in the creation payload
|
||||||
expect($parsersFile)
|
expect($parsersFile)
|
||||||
->toContain("firstOrCreate([\n 'name' => \$serviceName,\n 'service_id' => \$resource->id,\n ]);")
|
->toContain("\$databaseFound = ServiceDatabase::where('name', \$serviceName)->where('service_id', \$resource->id)->first();")
|
||||||
->not->toContain("firstOrCreate([\n 'name' => \$serviceName,\n 'image' => \$image,\n 'service_id' => \$resource->id,\n ]);");
|
->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 ]);");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ensures service parser updates image after finding or creating service', function () {
|
it('ensures service parser updates image after finding or creating service', function () {
|
||||||
// Read the serviceParser function from parsers.php
|
// Read the serviceParser function from parsers.php
|
||||||
$parsersFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/parsers.php');
|
$parsersFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/parsers.php');
|
||||||
|
|
||||||
// Check that image update logic exists after firstOrCreate
|
// Check that image update logic exists after the trusted create/find branch
|
||||||
expect($parsersFile)
|
expect($parsersFile)
|
||||||
->toContain('// Update image if it changed')
|
->toContain('// Update image if it changed')
|
||||||
->toContain('if ($savedService->image !== $image) {')
|
->toContain('if ($savedService->image !== $image) {')
|
||||||
|
|
|
||||||
|
|
@ -77,21 +77,21 @@
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Project::create([
|
Project::forceCreate([
|
||||||
'uuid' => 'project-1',
|
'uuid' => 'project-1',
|
||||||
'name' => 'My first project',
|
'name' => 'My first project',
|
||||||
'description' => 'This is a test project in development',
|
'description' => 'This is a test project in development',
|
||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Project::create([
|
Project::forceCreate([
|
||||||
'uuid' => 'project-2',
|
'uuid' => 'project-2',
|
||||||
'name' => 'Production API',
|
'name' => 'Production API',
|
||||||
'description' => 'Backend services for production',
|
'description' => 'Backend services for production',
|
||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Project::create([
|
Project::forceCreate([
|
||||||
'uuid' => 'project-3',
|
'uuid' => 'project-3',
|
||||||
'name' => 'Staging Environment',
|
'name' => 'Staging Environment',
|
||||||
'description' => 'Staging and QA testing',
|
'description' => 'Staging and QA testing',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue