Replace all uses of `forceFill`, `forceCreate`, and `forceFill` with their non-force equivalents across models, actions, controllers, and Livewire components. Add explicit `$fillable` arrays to all affected Eloquent models to enforce mass assignment protection. Add ModelFillableCreationTest and ModelFillableRegressionTest to verify that model creation respects fillable constraints and prevent regressions.
236 lines
8.4 KiB
PHP
236 lines
8.4 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Project\New;
|
|
|
|
use App\Models\Application;
|
|
use App\Models\GithubApp;
|
|
use App\Models\GitlabApp;
|
|
use App\Models\PrivateKey;
|
|
use App\Models\Project;
|
|
use App\Models\StandaloneDocker;
|
|
use App\Models\SwarmDocker;
|
|
use App\Rules\ValidGitBranch;
|
|
use App\Rules\ValidGitRepositoryUrl;
|
|
use App\Support\ValidationPatterns;
|
|
use Illuminate\Support\Str;
|
|
use Livewire\Component;
|
|
use Spatie\Url\Url;
|
|
|
|
class GithubPrivateRepositoryDeployKey extends Component
|
|
{
|
|
public $current_step = 'private_keys';
|
|
|
|
public $parameters;
|
|
|
|
public $query;
|
|
|
|
public $private_keys = [];
|
|
|
|
public int $private_key_id;
|
|
|
|
public int $port = 3000;
|
|
|
|
public string $type;
|
|
|
|
public bool $is_static = false;
|
|
|
|
public ?string $publish_directory = null;
|
|
|
|
// In case of docker compose
|
|
public ?string $base_directory = null;
|
|
|
|
public ?string $docker_compose_location = '/docker-compose.yaml';
|
|
// End of docker compose
|
|
|
|
public string $repository_url;
|
|
|
|
public string $branch;
|
|
|
|
public $build_pack = 'nixpacks';
|
|
|
|
public bool $show_is_static = true;
|
|
|
|
private object $repository_url_parsed;
|
|
|
|
private GithubApp|GitlabApp|string $git_source = 'other';
|
|
|
|
private ?string $git_host = null;
|
|
|
|
private ?string $git_repository = null;
|
|
|
|
protected function rules()
|
|
{
|
|
return [
|
|
'repository_url' => ['required', 'string', new ValidGitRepositoryUrl],
|
|
'branch' => ['required', 'string', new ValidGitBranch],
|
|
'port' => 'required|numeric',
|
|
'is_static' => 'required|boolean',
|
|
'publish_directory' => 'nullable|string',
|
|
'build_pack' => 'required|string',
|
|
'docker_compose_location' => ValidationPatterns::filePathRules(),
|
|
];
|
|
}
|
|
|
|
protected $validationAttributes = [
|
|
'repository_url' => 'Repository',
|
|
'branch' => 'Branch',
|
|
'port' => 'Port',
|
|
'is_static' => 'Is static',
|
|
'publish_directory' => 'Publish directory',
|
|
'build_pack' => 'Build pack',
|
|
];
|
|
|
|
public function mount()
|
|
{
|
|
if (isDev()) {
|
|
$this->repository_url = 'https://github.com/coollabsio/coolify-examples/tree/v4.x';
|
|
}
|
|
$this->parameters = get_route_parameters();
|
|
$this->query = request()->query();
|
|
if (isDev()) {
|
|
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->get();
|
|
} else {
|
|
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->where('id', '!=', 0)->get();
|
|
}
|
|
}
|
|
|
|
public function updatedBuildPack()
|
|
{
|
|
if ($this->build_pack === 'nixpacks') {
|
|
$this->show_is_static = true;
|
|
$this->port = 3000;
|
|
} elseif ($this->build_pack === 'static') {
|
|
$this->show_is_static = false;
|
|
$this->is_static = false;
|
|
$this->port = 80;
|
|
} else {
|
|
$this->show_is_static = false;
|
|
$this->is_static = false;
|
|
}
|
|
}
|
|
|
|
public function instantSave()
|
|
{
|
|
if ($this->is_static) {
|
|
$this->port = 80;
|
|
$this->publish_directory = '/dist';
|
|
} else {
|
|
$this->port = 3000;
|
|
$this->publish_directory = null;
|
|
}
|
|
}
|
|
|
|
public function setPrivateKey($private_key_id)
|
|
{
|
|
$this->private_key_id = $private_key_id;
|
|
$this->current_step = 'repository';
|
|
}
|
|
|
|
public function submit()
|
|
{
|
|
$this->validate();
|
|
try {
|
|
$destination_uuid = $this->query['destination'];
|
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
|
if (! $destination) {
|
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
|
}
|
|
if (! $destination) {
|
|
throw new \Exception('Destination not found. What?!');
|
|
}
|
|
$destination_class = $destination->getMorphClass();
|
|
|
|
$this->get_git_source();
|
|
|
|
// Note: git_repository has already been validated and transformed in get_git_source()
|
|
// It may now be in SSH format (git@host:repo.git) which is valid for deploy keys
|
|
|
|
$project = Project::ownedByCurrentTeam()->where('uuid', $this->parameters['project_uuid'])->firstOrFail();
|
|
$environment = $project->environments()->where('uuid', $this->parameters['environment_uuid'])->firstOrFail();
|
|
if ($this->git_source === 'other') {
|
|
$application_init = [
|
|
'name' => generate_random_name(),
|
|
'git_repository' => $this->git_repository,
|
|
'git_branch' => $this->branch,
|
|
'build_pack' => $this->build_pack,
|
|
'ports_exposes' => $this->port,
|
|
'publish_directory' => $this->publish_directory,
|
|
'environment_id' => $environment->id,
|
|
'destination_id' => $destination->id,
|
|
'destination_type' => $destination_class,
|
|
'private_key_id' => $this->private_key_id,
|
|
];
|
|
} else {
|
|
$application_init = [
|
|
'name' => generate_random_name(),
|
|
'git_repository' => $this->git_repository,
|
|
'git_branch' => $this->branch,
|
|
'build_pack' => $this->build_pack,
|
|
'ports_exposes' => $this->port,
|
|
'publish_directory' => $this->publish_directory,
|
|
'environment_id' => $environment->id,
|
|
'destination_id' => $destination->id,
|
|
'destination_type' => $destination_class,
|
|
'private_key_id' => $this->private_key_id,
|
|
'source_id' => $this->git_source->id,
|
|
'source_type' => $this->git_source->getMorphClass(),
|
|
];
|
|
}
|
|
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
|
$application_init['health_check_enabled'] = false;
|
|
}
|
|
if ($this->build_pack === 'dockercompose') {
|
|
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
|
$application_init['base_directory'] = $this->base_directory;
|
|
}
|
|
$application = Application::create($application_init);
|
|
$application->settings->is_static = $this->is_static;
|
|
$application->settings->save();
|
|
|
|
$fqdn = generateUrl(server: $destination->server, random: $application->uuid);
|
|
$application->fqdn = $fqdn;
|
|
$application->name = generate_random_name($application->uuid);
|
|
$application->save();
|
|
|
|
return redirect()->route('project.application.configuration', [
|
|
'application_uuid' => $application->uuid,
|
|
'environment_uuid' => $environment->uuid,
|
|
'project_uuid' => $project->uuid,
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
private function get_git_source()
|
|
{
|
|
// Validate repository URL before parsing
|
|
$validator = validator(['repository_url' => $this->repository_url], [
|
|
'repository_url' => ['required', 'string', new ValidGitRepositoryUrl],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
throw new \RuntimeException('Invalid repository URL: '.$validator->errors()->first('repository_url'));
|
|
}
|
|
|
|
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
|
$this->git_host = $this->repository_url_parsed->getHost();
|
|
$this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2);
|
|
|
|
if ($this->git_host === 'github.com') {
|
|
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
|
|
|
return;
|
|
}
|
|
if (str($this->repository_url)->startsWith('http')) {
|
|
$this->git_host = $this->repository_url_parsed->getHost();
|
|
$this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2);
|
|
// Convert to SSH format for deploy key usage
|
|
$this->git_repository = Str::finish("git@$this->git_host:$this->git_repository", '.git');
|
|
} else {
|
|
// If it's already in SSH format, just use it as-is
|
|
$this->git_repository = $this->repository_url;
|
|
}
|
|
$this->git_source = 'other';
|
|
}
|
|
}
|