refactor: define explicit fillable attributes on all Eloquent models
Replace $guarded usage with explicit $fillable arrays across all models. Sync fillable definitions with current database schema and add tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a5840501b4
commit
9f46586d4a
51 changed files with 1071 additions and 128 deletions
|
|
@ -1158,7 +1158,7 @@ private function create_application(Request $request, $type)
|
|||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
$application->fill($request->only($allowedFields));
|
||||
$dockerComposeDomainsJson = collect();
|
||||
if ($request->has('docker_compose_domains')) {
|
||||
$dockerComposeDomains = collect($request->docker_compose_domains);
|
||||
|
|
@ -1385,7 +1385,7 @@ private function create_application(Request $request, $type)
|
|||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
$application->fill($request->only($allowedFields));
|
||||
|
||||
$dockerComposeDomainsJson = collect();
|
||||
if ($request->has('docker_compose_domains')) {
|
||||
|
|
@ -1585,7 +1585,7 @@ private function create_application(Request $request, $type)
|
|||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
$application->fill($request->only($allowedFields));
|
||||
|
||||
$dockerComposeDomainsJson = collect();
|
||||
if ($request->has('docker_compose_domains')) {
|
||||
|
|
@ -1772,7 +1772,7 @@ private function create_application(Request $request, $type)
|
|||
}
|
||||
|
||||
$application = new Application;
|
||||
$application->fill($request->all());
|
||||
$application->fill($request->only($allowedFields));
|
||||
$application->fqdn = $fqdn;
|
||||
$application->ports_exposes = $port;
|
||||
$application->build_pack = 'dockerfile';
|
||||
|
|
@ -1884,7 +1884,7 @@ private function create_application(Request $request, $type)
|
|||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
$application->fill($request->only($allowedFields));
|
||||
$application->fqdn = $fqdn;
|
||||
$application->build_pack = 'dockerimage';
|
||||
$application->destination_id = $destination->id;
|
||||
|
|
@ -2000,7 +2000,7 @@ private function create_application(Request $request, $type)
|
|||
|
||||
$service = new Service;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$service->fill($request->all());
|
||||
$service->fill($request->only($allowedFields));
|
||||
|
||||
$service->docker_compose_raw = $dockerComposeRaw;
|
||||
$service->environment_id = $environment->id;
|
||||
|
|
@ -2760,7 +2760,7 @@ public function update_by_uuid(Request $request)
|
|||
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$data = $request->all();
|
||||
$data = $request->only($allowedFields);
|
||||
if ($requestHasDomains && $server->isProxyShouldRun()) {
|
||||
data_set($data, 'fqdn', $domains);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1751,7 +1751,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
$request->offsetSet('postgres_conf', $postgresConf);
|
||||
}
|
||||
$database = create_standalone_postgresql($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_postgresql($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -1806,7 +1806,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
$request->offsetSet('mariadb_conf', $mariadbConf);
|
||||
}
|
||||
$database = create_standalone_mariadb($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_mariadb($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -1865,7 +1865,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
$request->offsetSet('mysql_conf', $mysqlConf);
|
||||
}
|
||||
$database = create_standalone_mysql($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_mysql($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -1921,7 +1921,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
$request->offsetSet('redis_conf', $redisConf);
|
||||
}
|
||||
$database = create_standalone_redis($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_redis($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -1958,7 +1958,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -2007,7 +2007,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
$request->offsetSet('keydb_conf', $keydbConf);
|
||||
}
|
||||
$database = create_standalone_keydb($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_keydb($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -2043,7 +2043,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
], 422);
|
||||
}
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
@ -2101,7 +2101,7 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
}
|
||||
$request->offsetSet('mongo_conf', $mongoConf);
|
||||
}
|
||||
$database = create_standalone_mongodb($environment->id, $destination->uuid, $request->all());
|
||||
$database = create_standalone_mongodb($environment->id, $destination->uuid, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PrivateKey;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
|
|
@ -176,7 +177,7 @@ public function create_key(Request $request)
|
|||
return invalidTokenResponse();
|
||||
}
|
||||
$return = validateIncomingRequest($request);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
if ($return instanceof JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
|
|
@ -300,7 +301,7 @@ public function update_key(Request $request)
|
|||
return invalidTokenResponse();
|
||||
}
|
||||
$return = validateIncomingRequest($request);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
if ($return instanceof JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
|
@ -330,7 +331,7 @@ public function update_key(Request $request)
|
|||
'message' => 'Private Key not found.',
|
||||
], 404);
|
||||
}
|
||||
$foundKey->update($request->all());
|
||||
$foundKey->update($request->only($allowedFields));
|
||||
|
||||
return response()->json(serializeApiResponse([
|
||||
'uuid' => $foundKey->uuid,
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'status' => 'exited',
|
||||
'started_at' => null,
|
||||
|
|
@ -187,7 +187,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'name' => $newName,
|
||||
'resource_id' => $newDatabase->id,
|
||||
]);
|
||||
|
|
@ -216,7 +216,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'resource_id' => $newDatabase->id,
|
||||
]);
|
||||
$newStorage->save();
|
||||
|
|
@ -229,7 +229,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'database_id' => $newDatabase->id,
|
||||
'database_type' => $newDatabase->getMorphClass(),
|
||||
|
|
@ -247,7 +247,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill($payload);
|
||||
])->forceFill($payload);
|
||||
$newEnvironmentVariable->save();
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +258,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'environment_id' => $environment->id,
|
||||
'destination_id' => $this->selectedDestination,
|
||||
|
|
@ -276,7 +276,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => (string) new Cuid2,
|
||||
'service_id' => $newService->id,
|
||||
'team_id' => currentTeam()->id,
|
||||
|
|
@ -290,7 +290,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'resourceable_id' => $newService->id,
|
||||
'resourceable_type' => $newService->getMorphClass(),
|
||||
]);
|
||||
|
|
@ -298,9 +298,9 @@ public function clone(string $type)
|
|||
}
|
||||
|
||||
foreach ($newService->applications() as $application) {
|
||||
$application->update([
|
||||
$application->forceFill([
|
||||
'status' => 'exited',
|
||||
]);
|
||||
])->save();
|
||||
|
||||
$persistentVolumes = $application->persistentStorages()->get();
|
||||
foreach ($persistentVolumes as $volume) {
|
||||
|
|
@ -315,7 +315,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'name' => $newName,
|
||||
'resource_id' => $application->id,
|
||||
]);
|
||||
|
|
@ -344,7 +344,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'resource_id' => $application->id,
|
||||
]);
|
||||
$newStorage->save();
|
||||
|
|
@ -352,9 +352,9 @@ public function clone(string $type)
|
|||
}
|
||||
|
||||
foreach ($newService->databases() as $database) {
|
||||
$database->update([
|
||||
$database->forceFill([
|
||||
'status' => 'exited',
|
||||
]);
|
||||
])->save();
|
||||
|
||||
$persistentVolumes = $database->persistentStorages()->get();
|
||||
foreach ($persistentVolumes as $volume) {
|
||||
|
|
@ -369,7 +369,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'name' => $newName,
|
||||
'resource_id' => $database->id,
|
||||
]);
|
||||
|
|
@ -398,7 +398,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'resource_id' => $database->id,
|
||||
]);
|
||||
$newStorage->save();
|
||||
|
|
@ -411,7 +411,7 @@ public function clone(string $type)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'database_id' => $database->id,
|
||||
'database_type' => $database->getMorphClass(),
|
||||
|
|
|
|||
|
|
@ -7,9 +7,18 @@
|
|||
use App\Actions\Service\StartService;
|
||||
use App\Actions\Service\StopService;
|
||||
use App\Jobs\VolumeCloneJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use App\Models\SwarmDocker;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
|
@ -60,7 +69,7 @@ public function cloneTo($destination_id)
|
|||
$uuid = (string) new Cuid2;
|
||||
$server = $new_destination->server;
|
||||
|
||||
if ($this->resource->getMorphClass() === \App\Models\Application::class) {
|
||||
if ($this->resource->getMorphClass() === Application::class) {
|
||||
$new_resource = clone_application($this->resource, $new_destination, ['uuid' => $uuid], $this->cloneVolumeData);
|
||||
|
||||
$route = route('project.application.configuration', [
|
||||
|
|
@ -71,21 +80,21 @@ public function cloneTo($destination_id)
|
|||
|
||||
return redirect()->to($route);
|
||||
} elseif (
|
||||
$this->resource->getMorphClass() === \App\Models\StandalonePostgresql::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneMongodb::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneMysql::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneMariadb::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneRedis::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneKeydb::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneDragonfly::class ||
|
||||
$this->resource->getMorphClass() === \App\Models\StandaloneClickhouse::class
|
||||
$this->resource->getMorphClass() === StandalonePostgresql::class ||
|
||||
$this->resource->getMorphClass() === StandaloneMongodb::class ||
|
||||
$this->resource->getMorphClass() === StandaloneMysql::class ||
|
||||
$this->resource->getMorphClass() === StandaloneMariadb::class ||
|
||||
$this->resource->getMorphClass() === StandaloneRedis::class ||
|
||||
$this->resource->getMorphClass() === StandaloneKeydb::class ||
|
||||
$this->resource->getMorphClass() === StandaloneDragonfly::class ||
|
||||
$this->resource->getMorphClass() === StandaloneClickhouse::class
|
||||
) {
|
||||
$uuid = (string) new Cuid2;
|
||||
$new_resource = $this->resource->replicate([
|
||||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'name' => $this->resource->name.'-clone-'.$uuid,
|
||||
'status' => 'exited',
|
||||
|
|
@ -133,7 +142,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'name' => $newName,
|
||||
'resource_id' => $new_resource->id,
|
||||
]);
|
||||
|
|
@ -162,7 +171,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'resource_id' => $new_resource->id,
|
||||
]);
|
||||
$newStorage->save();
|
||||
|
|
@ -175,7 +184,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'database_id' => $new_resource->id,
|
||||
'database_type' => $new_resource->getMorphClass(),
|
||||
|
|
@ -194,7 +203,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill($payload);
|
||||
])->forceFill($payload);
|
||||
$newEnvironmentVariable->save();
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +220,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => $uuid,
|
||||
'name' => $this->resource->name.'-clone-'.$uuid,
|
||||
'destination_id' => $new_destination->id,
|
||||
|
|
@ -232,7 +241,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'uuid' => (string) new Cuid2,
|
||||
'service_id' => $new_resource->id,
|
||||
'team_id' => currentTeam()->id,
|
||||
|
|
@ -246,7 +255,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'resourceable_id' => $new_resource->id,
|
||||
'resourceable_type' => $new_resource->getMorphClass(),
|
||||
]);
|
||||
|
|
@ -254,9 +263,9 @@ public function cloneTo($destination_id)
|
|||
}
|
||||
|
||||
foreach ($new_resource->applications() as $application) {
|
||||
$application->update([
|
||||
$application->forceFill([
|
||||
'status' => 'exited',
|
||||
]);
|
||||
])->save();
|
||||
|
||||
$persistentVolumes = $application->persistentStorages()->get();
|
||||
foreach ($persistentVolumes as $volume) {
|
||||
|
|
@ -271,7 +280,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'name' => $newName,
|
||||
'resource_id' => $application->id,
|
||||
]);
|
||||
|
|
@ -296,9 +305,9 @@ public function cloneTo($destination_id)
|
|||
}
|
||||
|
||||
foreach ($new_resource->databases() as $database) {
|
||||
$database->update([
|
||||
$database->forceFill([
|
||||
'status' => 'exited',
|
||||
]);
|
||||
])->save();
|
||||
|
||||
$persistentVolumes = $database->persistentStorages()->get();
|
||||
foreach ($persistentVolumes as $volume) {
|
||||
|
|
@ -313,7 +322,7 @@ public function cloneTo($destination_id)
|
|||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
])->forceFill([
|
||||
'name' => $newName,
|
||||
'resource_id' => $database->id,
|
||||
]);
|
||||
|
|
@ -354,9 +363,9 @@ public function moveTo($environment_id)
|
|||
try {
|
||||
$this->authorize('update', $this->resource);
|
||||
$new_environment = Environment::ownedByCurrentTeam()->findOrFail($environment_id);
|
||||
$this->resource->update([
|
||||
$this->resource->forceFill([
|
||||
'environment_id' => $environment_id,
|
||||
]);
|
||||
])->save();
|
||||
if ($this->resource->type() === 'application') {
|
||||
$route = route('project.application.configuration', [
|
||||
'project_uuid' => $new_environment->project->uuid,
|
||||
|
|
|
|||
|
|
@ -118,7 +118,91 @@ class Application extends BaseModel
|
|||
|
||||
private static $parserVersion = '5';
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'fqdn',
|
||||
'git_repository',
|
||||
'git_branch',
|
||||
'git_commit_sha',
|
||||
'git_full_url',
|
||||
'docker_registry_image_name',
|
||||
'docker_registry_image_tag',
|
||||
'build_pack',
|
||||
'static_image',
|
||||
'install_command',
|
||||
'build_command',
|
||||
'start_command',
|
||||
'ports_exposes',
|
||||
'ports_mappings',
|
||||
'base_directory',
|
||||
'publish_directory',
|
||||
'health_check_enabled',
|
||||
'health_check_path',
|
||||
'health_check_port',
|
||||
'health_check_host',
|
||||
'health_check_method',
|
||||
'health_check_return_code',
|
||||
'health_check_scheme',
|
||||
'health_check_response_text',
|
||||
'health_check_interval',
|
||||
'health_check_timeout',
|
||||
'health_check_retries',
|
||||
'health_check_start_period',
|
||||
'health_check_type',
|
||||
'health_check_command',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'status',
|
||||
'preview_url_template',
|
||||
'dockerfile',
|
||||
'dockerfile_location',
|
||||
'dockerfile_target_build',
|
||||
'custom_labels',
|
||||
'custom_docker_run_options',
|
||||
'post_deployment_command',
|
||||
'post_deployment_command_container',
|
||||
'pre_deployment_command',
|
||||
'pre_deployment_command_container',
|
||||
'manual_webhook_secret_github',
|
||||
'manual_webhook_secret_gitlab',
|
||||
'manual_webhook_secret_bitbucket',
|
||||
'manual_webhook_secret_gitea',
|
||||
'docker_compose_location',
|
||||
'docker_compose',
|
||||
'docker_compose_raw',
|
||||
'docker_compose_domains',
|
||||
'docker_compose_custom_start_command',
|
||||
'docker_compose_custom_build_command',
|
||||
'swarm_replicas',
|
||||
'swarm_placement_constraints',
|
||||
'watch_paths',
|
||||
'redirect',
|
||||
'compose_parsing_version',
|
||||
'custom_nginx_configuration',
|
||||
'custom_network_aliases',
|
||||
'custom_healthcheck_found',
|
||||
'is_http_basic_auth_enabled',
|
||||
'http_basic_auth_username',
|
||||
'http_basic_auth_password',
|
||||
'config_hash',
|
||||
'last_online_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'environment_id',
|
||||
'destination_id',
|
||||
'destination_type',
|
||||
'source_id',
|
||||
'source_type',
|
||||
'private_key_id',
|
||||
'repository_project_id',
|
||||
];
|
||||
|
||||
protected $appends = ['server_status'];
|
||||
|
||||
|
|
@ -1145,7 +1229,7 @@ public function getGitRemoteStatus(string $deployment_uuid)
|
|||
'is_accessible' => true,
|
||||
'error' => null,
|
||||
];
|
||||
} catch (\RuntimeException $ex) {
|
||||
} catch (RuntimeException $ex) {
|
||||
return [
|
||||
'is_accessible' => false,
|
||||
'error' => $ex->getMessage(),
|
||||
|
|
@ -1202,7 +1286,7 @@ public function generateGitLsRemoteCommands(string $deployment_uuid, bool $exec_
|
|||
];
|
||||
}
|
||||
|
||||
if ($this->source->getMorphClass() === \App\Models\GitlabApp::class) {
|
||||
if ($this->source->getMorphClass() === GitlabApp::class) {
|
||||
$gitlabSource = $this->source;
|
||||
$private_key = data_get($gitlabSource, 'privateKey.private_key');
|
||||
|
||||
|
|
@ -1354,7 +1438,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
|
|||
$source_html_url_host = $url['host'];
|
||||
$source_html_url_scheme = $url['scheme'];
|
||||
|
||||
if ($this->source->getMorphClass() === \App\Models\GithubApp::class) {
|
||||
if ($this->source->getMorphClass() === GithubApp::class) {
|
||||
if ($this->source->is_public) {
|
||||
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
|
||||
$escapedRepoUrl = escapeshellarg("{$this->source->html_url}/{$customRepository}");
|
||||
|
|
@ -1409,7 +1493,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
|
|||
];
|
||||
}
|
||||
|
||||
if ($this->source->getMorphClass() === \App\Models\GitlabApp::class) {
|
||||
if ($this->source->getMorphClass() === GitlabApp::class) {
|
||||
$gitlabSource = $this->source;
|
||||
$private_key = data_get($gitlabSource, 'privateKey.private_key');
|
||||
|
||||
|
|
@ -1600,7 +1684,7 @@ public function oldRawParser()
|
|||
try {
|
||||
$yaml = Yaml::parse($this->docker_compose_raw);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException($e->getMessage());
|
||||
throw new RuntimeException($e->getMessage());
|
||||
}
|
||||
$services = data_get($yaml, 'services');
|
||||
|
||||
|
|
@ -1682,7 +1766,7 @@ public function loadComposeFile($isInit = false, ?string $restoreBaseDirectory =
|
|||
$fileList = collect([".$workdir$composeFile"]);
|
||||
$gitRemoteStatus = $this->getGitRemoteStatus(deployment_uuid: $uuid);
|
||||
if (! $gitRemoteStatus['is_accessible']) {
|
||||
throw new \RuntimeException("Failed to read Git source:\n\n{$gitRemoteStatus['error']}");
|
||||
throw new RuntimeException("Failed to read Git source:\n\n{$gitRemoteStatus['error']}");
|
||||
}
|
||||
$getGitVersion = instant_remote_process(['git --version'], $this->destination->server, false);
|
||||
$gitVersion = str($getGitVersion)->explode(' ')->last();
|
||||
|
|
@ -1732,15 +1816,15 @@ public function loadComposeFile($isInit = false, ?string $restoreBaseDirectory =
|
|||
$this->save();
|
||||
|
||||
if (str($e->getMessage())->contains('No such file')) {
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile (branch: {$this->git_branch})<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
throw new RuntimeException("Docker Compose file not found at: $workdir$composeFile (branch: {$this->git_branch})<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
if (str($e->getMessage())->contains('fatal: repository') && str($e->getMessage())->contains('does not exist')) {
|
||||
if ($this->deploymentType() === 'deploy_key') {
|
||||
throw new \RuntimeException('Your deploy key does not have access to the repository. Please check your deploy key and try again.');
|
||||
throw new RuntimeException('Your deploy key does not have access to the repository. Please check your deploy key and try again.');
|
||||
}
|
||||
throw new \RuntimeException('Repository does not exist. Please check your repository URL and try again.');
|
||||
throw new RuntimeException('Repository does not exist. Please check your repository URL and try again.');
|
||||
}
|
||||
throw new \RuntimeException($e->getMessage());
|
||||
throw new RuntimeException($e->getMessage());
|
||||
} finally {
|
||||
// Cleanup only - restoration happens in catch block
|
||||
$commands = collect([
|
||||
|
|
@ -1793,7 +1877,7 @@ public function loadComposeFile($isInit = false, ?string $restoreBaseDirectory =
|
|||
$this->base_directory = $initialBaseDirectory;
|
||||
$this->save();
|
||||
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile (branch: {$this->git_branch})<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
throw new RuntimeException("Docker Compose file not found at: $workdir$composeFile (branch: {$this->git_branch})<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,32 @@
|
|||
)]
|
||||
class ApplicationDeploymentQueue extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'application_id',
|
||||
'deployment_uuid',
|
||||
'pull_request_id',
|
||||
'force_rebuild',
|
||||
'commit',
|
||||
'status',
|
||||
'is_webhook',
|
||||
'logs',
|
||||
'current_process_id',
|
||||
'restart_only',
|
||||
'git_type',
|
||||
'server_id',
|
||||
'application_name',
|
||||
'server_name',
|
||||
'deployment_url',
|
||||
'destination_id',
|
||||
'only_this_server',
|
||||
'rollback',
|
||||
'commit_message',
|
||||
'is_api',
|
||||
'build_server_id',
|
||||
'horizon_job_id',
|
||||
'horizon_job_worker',
|
||||
'finished_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'finished_at' => 'datetime',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,16 @@ class ApplicationPreview extends BaseModel
|
|||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'pull_request_id',
|
||||
'pull_request_html_url',
|
||||
'pull_request_issue_comment_id',
|
||||
'fqdn',
|
||||
'status',
|
||||
'git_type',
|
||||
'docker_compose_domains',
|
||||
'last_online_at',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
|
|
@ -69,7 +78,7 @@ public function application()
|
|||
|
||||
public function persistentStorages()
|
||||
{
|
||||
return $this->morphMany(\App\Models\LocalPersistentVolume::class, 'resource');
|
||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function generate_preview_fqdn()
|
||||
|
|
|
|||
|
|
@ -28,7 +28,42 @@ class ApplicationSetting extends Model
|
|||
'docker_images_to_keep' => 'integer',
|
||||
];
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'is_static',
|
||||
'is_git_submodules_enabled',
|
||||
'is_git_lfs_enabled',
|
||||
'is_auto_deploy_enabled',
|
||||
'is_force_https_enabled',
|
||||
'is_debug_enabled',
|
||||
'is_preview_deployments_enabled',
|
||||
'is_log_drain_enabled',
|
||||
'is_gpu_enabled',
|
||||
'gpu_driver',
|
||||
'gpu_count',
|
||||
'gpu_device_ids',
|
||||
'gpu_options',
|
||||
'is_include_timestamps',
|
||||
'is_swarm_only_worker_nodes',
|
||||
'is_raw_compose_deployment_enabled',
|
||||
'is_build_server_enabled',
|
||||
'is_consistent_container_name_enabled',
|
||||
'is_gzip_enabled',
|
||||
'is_stripprefix_enabled',
|
||||
'connect_to_docker_network',
|
||||
'custom_internal_name',
|
||||
'is_container_label_escape_enabled',
|
||||
'is_env_sorting_enabled',
|
||||
'is_container_label_readonly_enabled',
|
||||
'is_preserve_repository_enabled',
|
||||
'disable_build_cache',
|
||||
'is_spa',
|
||||
'is_git_shallow_clone_enabled',
|
||||
'is_pr_deployments_public_enabled',
|
||||
'use_build_secrets',
|
||||
'inject_build_args_to_dockerfile',
|
||||
'include_source_commit_in_build',
|
||||
'docker_images_to_keep',
|
||||
];
|
||||
|
||||
public function isStatic(): Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
class CloudProviderToken extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'provider',
|
||||
'token',
|
||||
'name',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'token' => 'encrypted',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ class DiscordNotificationSettings extends Model
|
|||
'backup_failure_discord_notifications',
|
||||
'scheduled_task_success_discord_notifications',
|
||||
'scheduled_task_failure_discord_notifications',
|
||||
'docker_cleanup_discord_notifications',
|
||||
'docker_cleanup_success_discord_notifications',
|
||||
'docker_cleanup_failure_discord_notifications',
|
||||
'server_disk_usage_discord_notifications',
|
||||
'server_reachable_discord_notifications',
|
||||
'server_unreachable_discord_notifications',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,12 @@
|
|||
|
||||
class DockerCleanupExecution extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'status',
|
||||
'message',
|
||||
'cleanup_log',
|
||||
'finished_at',
|
||||
];
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@ class EmailNotificationSettings extends Model
|
|||
'backup_failure_email_notifications',
|
||||
'scheduled_task_success_email_notifications',
|
||||
'scheduled_task_failure_email_notifications',
|
||||
'docker_cleanup_success_email_notifications',
|
||||
'docker_cleanup_failure_email_notifications',
|
||||
'server_disk_usage_email_notifications',
|
||||
'server_reachable_email_notifications',
|
||||
'server_unreachable_email_notifications',
|
||||
'server_patch_email_notifications',
|
||||
'traefik_outdated_email_notifications',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@ class Environment extends BaseModel
|
|||
use HasFactory;
|
||||
use HasSafeStringAttribute;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,25 @@
|
|||
|
||||
class GithubApp extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'organization',
|
||||
'api_url',
|
||||
'html_url',
|
||||
'custom_user',
|
||||
'custom_port',
|
||||
'app_id',
|
||||
'installation_id',
|
||||
'client_id',
|
||||
'client_secret',
|
||||
'webhook_secret',
|
||||
'is_system_wide',
|
||||
'is_public',
|
||||
'contents',
|
||||
'metadata',
|
||||
'pull_requests',
|
||||
'administration',
|
||||
];
|
||||
|
||||
protected $appends = ['type'];
|
||||
|
||||
|
|
@ -92,7 +110,7 @@ public function type(): Attribute
|
|||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
if ($this->getMorphClass() === \App\Models\GithubApp::class) {
|
||||
if ($this->getMorphClass() === GithubApp::class) {
|
||||
return 'github';
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,24 @@
|
|||
|
||||
class GitlabApp extends BaseModel
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'organization',
|
||||
'api_url',
|
||||
'html_url',
|
||||
'custom_port',
|
||||
'custom_user',
|
||||
'is_system_wide',
|
||||
'is_public',
|
||||
'app_id',
|
||||
'app_secret',
|
||||
'oauth_id',
|
||||
'group_name',
|
||||
'public_key',
|
||||
'webhook_token',
|
||||
'deploy_key_id',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'webhook_token',
|
||||
'app_secret',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,43 @@
|
|||
|
||||
class InstanceSettings extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'public_ipv4',
|
||||
'public_ipv6',
|
||||
'fqdn',
|
||||
'public_port_min',
|
||||
'public_port_max',
|
||||
'do_not_track',
|
||||
'is_auto_update_enabled',
|
||||
'is_registration_enabled',
|
||||
'next_channel',
|
||||
'smtp_enabled',
|
||||
'smtp_from_address',
|
||||
'smtp_from_name',
|
||||
'smtp_recipients',
|
||||
'smtp_host',
|
||||
'smtp_port',
|
||||
'smtp_encryption',
|
||||
'smtp_username',
|
||||
'smtp_password',
|
||||
'smtp_timeout',
|
||||
'resend_enabled',
|
||||
'resend_api_key',
|
||||
'is_dns_validation_enabled',
|
||||
'custom_dns_servers',
|
||||
'instance_name',
|
||||
'is_api_enabled',
|
||||
'allowed_ips',
|
||||
'auto_update_frequency',
|
||||
'update_check_frequency',
|
||||
'new_version_available',
|
||||
'instance_timezone',
|
||||
'helper_version',
|
||||
'disable_two_step_confirmation',
|
||||
'is_sponsorship_popup_enabled',
|
||||
'dev_helper_version',
|
||||
'is_wire_navigate_enabled',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'smtp_enabled' => 'boolean',
|
||||
|
|
|
|||
|
|
@ -20,7 +20,18 @@ class LocalFileVolume extends BaseModel
|
|||
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'fs_path',
|
||||
'mount_path',
|
||||
'content',
|
||||
'resource_type',
|
||||
'resource_id',
|
||||
'is_directory',
|
||||
'chown',
|
||||
'chmod',
|
||||
'is_based_on_git',
|
||||
'is_preview_suffix_enabled',
|
||||
];
|
||||
|
||||
public $appends = ['is_binary'];
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,15 @@
|
|||
|
||||
class LocalPersistentVolume extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'mount_path',
|
||||
'host_path',
|
||||
'container_id',
|
||||
'resource_type',
|
||||
'resource_id',
|
||||
'is_preview_suffix_enabled',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_preview_suffix_enabled' => 'boolean',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ class Project extends BaseModel
|
|||
use HasFactory;
|
||||
use HasSafeStringAttribute;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get query builder for projects owned by current team.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
class ProjectSetting extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [];
|
||||
|
||||
public function project()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class PushoverNotificationSettings extends Model
|
|||
'backup_failure_pushover_notifications',
|
||||
'scheduled_task_success_pushover_notifications',
|
||||
'scheduled_task_failure_pushover_notifications',
|
||||
'docker_cleanup_pushover_notifications',
|
||||
'docker_cleanup_success_pushover_notifications',
|
||||
'docker_cleanup_failure_pushover_notifications',
|
||||
'server_disk_usage_pushover_notifications',
|
||||
'server_reachable_pushover_notifications',
|
||||
'server_unreachable_pushover_notifications',
|
||||
|
|
|
|||
|
|
@ -12,7 +12,17 @@ class S3Storage extends BaseModel
|
|||
{
|
||||
use HasFactory, HasSafeStringAttribute;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'region',
|
||||
'key',
|
||||
'secret',
|
||||
'bucket',
|
||||
'endpoint',
|
||||
'is_usable',
|
||||
'unusable_email_sent',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_usable' => 'boolean',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,25 @@
|
|||
|
||||
class ScheduledDatabaseBackup extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'description',
|
||||
'enabled',
|
||||
'save_s3',
|
||||
'frequency',
|
||||
'database_backup_retention_amount_locally',
|
||||
'database_type',
|
||||
'database_id',
|
||||
's3_storage_id',
|
||||
'databases_to_backup',
|
||||
'dump_all',
|
||||
'database_backup_retention_days_locally',
|
||||
'database_backup_retention_max_storage_locally',
|
||||
'database_backup_retention_amount_s3',
|
||||
'database_backup_retention_days_s3',
|
||||
'database_backup_retention_max_storage_s3',
|
||||
'timeout',
|
||||
'disable_local_backup',
|
||||
];
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,17 @@
|
|||
|
||||
class ScheduledDatabaseBackupExecution extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'status',
|
||||
'message',
|
||||
'size',
|
||||
'filename',
|
||||
'database_name',
|
||||
'finished_at',
|
||||
'local_storage_deleted',
|
||||
's3_storage_deleted',
|
||||
's3_uploaded',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,7 +29,14 @@ class ScheduledTask extends BaseModel
|
|||
use HasFactory;
|
||||
use HasSafeStringAttribute;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'enabled',
|
||||
'name',
|
||||
'command',
|
||||
'frequency',
|
||||
'container',
|
||||
'timeout',
|
||||
];
|
||||
|
||||
public static function ownedByCurrentTeamAPI(int $teamId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,15 @@
|
|||
)]
|
||||
class ScheduledTaskExecution extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'status',
|
||||
'message',
|
||||
'finished_at',
|
||||
'started_at',
|
||||
'retry_count',
|
||||
'duration',
|
||||
'error_details',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||
use Spatie\Url\Url;
|
||||
use Stevebauman\Purify\Facades\Purify;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
|
|
@ -265,14 +266,12 @@ public static function flushIdentityMap(): void
|
|||
'server_metadata',
|
||||
];
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
use HasSafeStringAttribute;
|
||||
|
||||
public function setValidationLogsAttribute($value): void
|
||||
{
|
||||
$this->attributes['validation_logs'] = $value !== null
|
||||
? \Stevebauman\Purify\Facades\Purify::config('validation_logs')->clean($value)
|
||||
? Purify::config('validation_logs')->clean($value)
|
||||
: null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,50 @@
|
|||
)]
|
||||
class ServerSetting extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'is_swarm_manager',
|
||||
'is_jump_server',
|
||||
'is_build_server',
|
||||
'is_reachable',
|
||||
'is_usable',
|
||||
'wildcard_domain',
|
||||
'is_cloudflare_tunnel',
|
||||
'is_logdrain_newrelic_enabled',
|
||||
'logdrain_newrelic_license_key',
|
||||
'logdrain_newrelic_base_uri',
|
||||
'is_logdrain_highlight_enabled',
|
||||
'logdrain_highlight_project_id',
|
||||
'is_logdrain_axiom_enabled',
|
||||
'logdrain_axiom_dataset_name',
|
||||
'logdrain_axiom_api_key',
|
||||
'is_swarm_worker',
|
||||
'is_logdrain_custom_enabled',
|
||||
'logdrain_custom_config',
|
||||
'logdrain_custom_config_parser',
|
||||
'concurrent_builds',
|
||||
'dynamic_timeout',
|
||||
'force_disabled',
|
||||
'is_metrics_enabled',
|
||||
'generate_exact_labels',
|
||||
'force_docker_cleanup',
|
||||
'docker_cleanup_frequency',
|
||||
'docker_cleanup_threshold',
|
||||
'server_timezone',
|
||||
'delete_unused_volumes',
|
||||
'delete_unused_networks',
|
||||
'is_sentinel_enabled',
|
||||
'sentinel_token',
|
||||
'sentinel_metrics_refresh_rate_seconds',
|
||||
'sentinel_metrics_history_days',
|
||||
'sentinel_push_interval_seconds',
|
||||
'sentinel_custom_url',
|
||||
'server_disk_usage_notification_threshold',
|
||||
'is_sentinel_debug_enabled',
|
||||
'server_disk_usage_check_frequency',
|
||||
'is_terminal_enabled',
|
||||
'deployment_queue_limit',
|
||||
'disable_application_image_retention',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'force_docker_cleanup' => 'boolean',
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
use OpenApi\Attributes as OA;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
#[OA\Schema(
|
||||
|
|
@ -47,7 +48,21 @@ class Service extends BaseModel
|
|||
|
||||
private static $parserVersion = '5';
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'docker_compose_raw',
|
||||
'docker_compose',
|
||||
'connect_to_docker_network',
|
||||
'service_type',
|
||||
'config_hash',
|
||||
'compose_parsing_version',
|
||||
'environment_id',
|
||||
'server_id',
|
||||
'destination_id',
|
||||
'destination_type',
|
||||
'is_container_label_escape_enabled',
|
||||
];
|
||||
|
||||
protected $appends = ['server_status', 'status'];
|
||||
|
||||
|
|
@ -1552,7 +1567,7 @@ public function saveComposeConfigs()
|
|||
// Generate SERVICE_NAME_* environment variables from docker-compose services
|
||||
if ($this->docker_compose) {
|
||||
try {
|
||||
$dockerCompose = \Symfony\Component\Yaml\Yaml::parse($this->docker_compose);
|
||||
$dockerCompose = Yaml::parse($this->docker_compose);
|
||||
$services = data_get($dockerCompose, 'services', []);
|
||||
foreach ($services as $serviceName => $_) {
|
||||
$envs->push('SERVICE_NAME_'.str($serviceName)->replace('-', '_')->replace('.', '_')->upper().'='.$serviceName);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,30 @@
|
|||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ServiceApplication extends BaseModel
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'human_name',
|
||||
'description',
|
||||
'fqdn',
|
||||
'ports',
|
||||
'exposes',
|
||||
'status',
|
||||
'exclude_from_status',
|
||||
'required_fqdn',
|
||||
'image',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'is_gzip_enabled',
|
||||
'is_stripprefix_enabled',
|
||||
'last_online_at',
|
||||
'is_migrated',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
|
|
@ -211,7 +229,7 @@ public function getRequiredPort(): ?int
|
|||
return $this->service->getRequiredPort();
|
||||
}
|
||||
|
||||
$dockerCompose = \Symfony\Component\Yaml\Yaml::parse($dockerComposeRaw);
|
||||
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
||||
$serviceConfig = data_get($dockerCompose, "services.{$this->name}");
|
||||
if (! $serviceConfig) {
|
||||
return $this->service->getRequiredPort();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,27 @@ class ServiceDatabase extends BaseModel
|
|||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'human_name',
|
||||
'description',
|
||||
'fqdn',
|
||||
'ports',
|
||||
'exposes',
|
||||
'status',
|
||||
'exclude_from_status',
|
||||
'image',
|
||||
'public_port',
|
||||
'is_public',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'is_gzip_enabled',
|
||||
'is_stripprefix_enabled',
|
||||
'last_online_at',
|
||||
'is_migrated',
|
||||
'custom_type',
|
||||
'public_port_timeout',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'public_port_timeout' => 'integer',
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ class SharedEnvironmentVariable extends Model
|
|||
'is_multiline',
|
||||
'is_literal',
|
||||
'is_shown_once',
|
||||
|
||||
// Metadata
|
||||
'version',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ class SlackNotificationSettings extends Model
|
|||
'backup_failure_slack_notifications',
|
||||
'scheduled_task_success_slack_notifications',
|
||||
'scheduled_task_failure_slack_notifications',
|
||||
'docker_cleanup_slack_notifications',
|
||||
'docker_cleanup_success_slack_notifications',
|
||||
'docker_cleanup_failure_slack_notifications',
|
||||
'server_disk_usage_slack_notifications',
|
||||
'server_reachable_slack_notifications',
|
||||
'server_unreachable_slack_notifications',
|
||||
|
|
|
|||
|
|
@ -13,7 +13,34 @@ class StandaloneClickhouse extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'clickhouse_admin_user',
|
||||
'clickhouse_admin_password',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'custom_docker_run_options',
|
||||
'clickhouse_db',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ class StandaloneDocker extends BaseModel
|
|||
use HasFactory;
|
||||
use HasSafeStringAttribute;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'network',
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,7 +13,33 @@ class StandaloneDragonfly extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'dragonfly_password',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,34 @@ class StandaloneKeydb extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'keydb_password',
|
||||
'keydb_conf',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,36 @@ class StandaloneMariadb extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'mariadb_root_password',
|
||||
'mariadb_user',
|
||||
'mariadb_password',
|
||||
'mariadb_database',
|
||||
'mariadb_conf',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'is_log_drain_enabled',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,37 @@ class StandaloneMongodb extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'mongo_conf',
|
||||
'mongo_initdb_root_username',
|
||||
'mongo_initdb_root_password',
|
||||
'mongo_initdb_database',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'ssl_mode',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,38 @@ class StandaloneMysql extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'mysql_root_password',
|
||||
'mysql_user',
|
||||
'mysql_password',
|
||||
'mysql_database',
|
||||
'mysql_conf',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'ssl_mode',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,40 @@ class StandalonePostgresql extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'postgres_user',
|
||||
'postgres_password',
|
||||
'postgres_db',
|
||||
'postgres_initdb_args',
|
||||
'postgres_host_auth_method',
|
||||
'postgres_conf',
|
||||
'init_scripts',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'ssl_mode',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,33 @@ class StandaloneRedis extends BaseModel
|
|||
{
|
||||
use ClearsGlobalSearchCache, HasFactory, HasMetrics, HasSafeStringAttribute, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'redis_conf',
|
||||
'status',
|
||||
'image',
|
||||
'is_public',
|
||||
'public_port',
|
||||
'ports_mappings',
|
||||
'limits_memory',
|
||||
'limits_memory_swap',
|
||||
'limits_memory_swappiness',
|
||||
'limits_memory_reservation',
|
||||
'limits_cpus',
|
||||
'limits_cpuset',
|
||||
'limits_cpu_shares',
|
||||
'started_at',
|
||||
'restart_count',
|
||||
'last_restart_at',
|
||||
'last_restart_type',
|
||||
'last_online_at',
|
||||
'public_port_timeout',
|
||||
'enable_ssl',
|
||||
'is_log_drain_enabled',
|
||||
'is_include_timestamps',
|
||||
'custom_docker_run_options',
|
||||
];
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,18 @@
|
|||
|
||||
class Subscription extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'stripe_invoice_paid',
|
||||
'stripe_subscription_id',
|
||||
'stripe_customer_id',
|
||||
'stripe_cancel_at_period_end',
|
||||
'stripe_plan_id',
|
||||
'stripe_feedback',
|
||||
'stripe_comment',
|
||||
'stripe_trial_already_ended',
|
||||
'stripe_past_due',
|
||||
'stripe_refunded_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
class SwarmDocker extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'network',
|
||||
];
|
||||
|
||||
public function setNetworkAttribute(string $value): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ class Tag extends BaseModel
|
|||
{
|
||||
use HasSafeStringAttribute;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
];
|
||||
|
||||
protected function customizeName($value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,7 +40,12 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
|||
{
|
||||
use HasFactory, HasNotificationSettings, HasSafeStringAttribute, Notifiable;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'description',
|
||||
'show_boarding',
|
||||
'custom_server_limit',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'personal_team' => 'boolean',
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class TelegramNotificationSettings extends Model
|
|||
'backup_failure_telegram_notifications',
|
||||
'scheduled_task_success_telegram_notifications',
|
||||
'scheduled_task_failure_telegram_notifications',
|
||||
'docker_cleanup_telegram_notifications',
|
||||
'docker_cleanup_success_telegram_notifications',
|
||||
'docker_cleanup_failure_telegram_notifications',
|
||||
'server_disk_usage_telegram_notifications',
|
||||
'server_reachable_telegram_notifications',
|
||||
'server_unreachable_telegram_notifications',
|
||||
|
|
@ -39,7 +40,8 @@ class TelegramNotificationSettings extends Model
|
|||
'telegram_notifications_backup_failure_thread_id',
|
||||
'telegram_notifications_scheduled_task_success_thread_id',
|
||||
'telegram_notifications_scheduled_task_failure_thread_id',
|
||||
'telegram_notifications_docker_cleanup_thread_id',
|
||||
'telegram_notifications_docker_cleanup_success_thread_id',
|
||||
'telegram_notifications_docker_cleanup_failure_thread_id',
|
||||
'telegram_notifications_server_disk_usage_thread_id',
|
||||
'telegram_notifications_server_reachable_thread_id',
|
||||
'telegram_notifications_server_unreachable_thread_id',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
use App\Jobs\UpdateStripeCustomerEmailJob;
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use App\Notifications\TransactionalEmails\EmailChangeVerification;
|
||||
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
|
||||
use App\Services\ChangelogService;
|
||||
use App\Traits\DeletesUserSessions;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
@ -41,7 +43,16 @@ class User extends Authenticatable implements SendsEmail
|
|||
{
|
||||
use DeletesUserSessions, HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'force_password_reset',
|
||||
'marketing_emails',
|
||||
'pending_email',
|
||||
'email_change_code',
|
||||
'email_change_code_expires_at',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password',
|
||||
|
|
@ -228,7 +239,7 @@ public function changelogReads()
|
|||
|
||||
public function getUnreadChangelogCount(): int
|
||||
{
|
||||
return app(\App\Services\ChangelogService::class)->getUnreadCountForUser($this);
|
||||
return app(ChangelogService::class)->getUnreadCountForUser($this);
|
||||
}
|
||||
|
||||
public function getRecipients(): array
|
||||
|
|
@ -239,7 +250,7 @@ public function getRecipients(): array
|
|||
public function sendVerificationEmail()
|
||||
{
|
||||
$mail = new MailMessage;
|
||||
$url = Url::temporarySignedRoute(
|
||||
$url = URL::temporarySignedRoute(
|
||||
'verify.verify',
|
||||
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||
[
|
||||
|
|
@ -408,7 +419,7 @@ public function requestEmailChange(string $newEmail): void
|
|||
]);
|
||||
|
||||
// Send verification email to new address
|
||||
$this->notify(new \App\Notifications\TransactionalEmails\EmailChangeVerification($this, $code, $newEmail, $expiresAt));
|
||||
$this->notify(new EmailChangeVerification($this, $code, $newEmail, $expiresAt));
|
||||
}
|
||||
|
||||
public function isEmailChangeCodeValid(string $code): bool
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
use App\Jobs\VolumeCloneJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use Spatie\Url\Url;
|
||||
|
|
@ -192,7 +193,7 @@ function clone_application(Application $source, $destination, array $overrides =
|
|||
$server = $destination->server;
|
||||
|
||||
if ($server->team_id !== currentTeam()->id) {
|
||||
throw new \RuntimeException('Destination does not belong to the current team.');
|
||||
throw new RuntimeException('Destination does not belong to the current team.');
|
||||
}
|
||||
|
||||
// Prepare name and URL
|
||||
|
|
@ -211,7 +212,7 @@ function clone_application(Application $source, $destination, array $overrides =
|
|||
'updated_at',
|
||||
'additional_servers_count',
|
||||
'additional_networks_count',
|
||||
])->fill(array_merge([
|
||||
])->forceFill(array_merge([
|
||||
'uuid' => $uuid,
|
||||
'name' => $name,
|
||||
'fqdn' => $url,
|
||||
|
|
@ -322,8 +323,8 @@ function clone_application(Application $source, $destination, array $overrides =
|
|||
destination: $source->destination,
|
||||
no_questions_asked: true
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Failed to copy volume data for '.$volume->name.': '.$e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Log::error('Failed to copy volume data for '.$volume->name.': '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -344,7 +345,7 @@ function clone_application(Application $source, $destination, array $overrides =
|
|||
// Clone production environment variables without triggering the created hook
|
||||
$environmentVariables = $source->environment_variables()->get();
|
||||
foreach ($environmentVariables as $environmentVariable) {
|
||||
\App\Models\EnvironmentVariable::withoutEvents(function () use ($environmentVariable, $newApplication) {
|
||||
EnvironmentVariable::withoutEvents(function () use ($environmentVariable, $newApplication) {
|
||||
$newEnvironmentVariable = $environmentVariable->replicate([
|
||||
'id',
|
||||
'created_at',
|
||||
|
|
@ -361,7 +362,7 @@ function clone_application(Application $source, $destination, array $overrides =
|
|||
// Clone preview environment variables
|
||||
$previewEnvironmentVariables = $source->environment_variables_preview()->get();
|
||||
foreach ($previewEnvironmentVariables as $previewEnvironmentVariable) {
|
||||
\App\Models\EnvironmentVariable::withoutEvents(function () use ($previewEnvironmentVariable, $newApplication) {
|
||||
EnvironmentVariable::withoutEvents(function () use ($previewEnvironmentVariable, $newApplication) {
|
||||
$newPreviewEnvironmentVariable = $previewEnvironmentVariable->replicate([
|
||||
'id',
|
||||
'created_at',
|
||||
|
|
|
|||
249
tests/Feature/MassAssignmentProtectionTest.php
Normal file
249
tests/Feature/MassAssignmentProtectionTest.php
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
|
||||
describe('mass assignment protection', function () {
|
||||
|
||||
test('no API-exposed model uses unguarded $guarded = []', function () {
|
||||
$models = [
|
||||
Application::class,
|
||||
Service::class,
|
||||
User::class,
|
||||
Team::class,
|
||||
Server::class,
|
||||
StandalonePostgresql::class,
|
||||
StandaloneRedis::class,
|
||||
StandaloneMysql::class,
|
||||
StandaloneMariadb::class,
|
||||
StandaloneMongodb::class,
|
||||
StandaloneKeydb::class,
|
||||
StandaloneDragonfly::class,
|
||||
StandaloneClickhouse::class,
|
||||
];
|
||||
|
||||
foreach ($models as $modelClass) {
|
||||
$model = new $modelClass;
|
||||
$guarded = $model->getGuarded();
|
||||
$fillable = $model->getFillable();
|
||||
|
||||
// Model must NOT have $guarded = [] (empty guard = no protection)
|
||||
// It should either have non-empty $guarded OR non-empty $fillable
|
||||
$hasProtection = $guarded !== ['*'] ? count($guarded) > 0 : true;
|
||||
$hasProtection = $hasProtection || count($fillable) > 0;
|
||||
|
||||
expect($hasProtection)
|
||||
->toBeTrue("Model {$modelClass} has no mass assignment protection (empty \$guarded and empty \$fillable)");
|
||||
}
|
||||
});
|
||||
|
||||
test('Application model blocks mass assignment of identity fields', function () {
|
||||
$application = new Application;
|
||||
|
||||
expect($application->isFillable('id'))->toBeFalse('id should not be fillable');
|
||||
expect($application->isFillable('uuid'))->toBeFalse('uuid should not be fillable');
|
||||
expect($application->isFillable('created_at'))->toBeFalse('created_at should not be fillable');
|
||||
expect($application->isFillable('updated_at'))->toBeFalse('updated_at should not be fillable');
|
||||
expect($application->isFillable('deleted_at'))->toBeFalse('deleted_at should not be fillable');
|
||||
});
|
||||
|
||||
test('Application model allows mass assignment of user-facing fields', function () {
|
||||
$application = new Application;
|
||||
$userFields = ['name', 'description', 'git_repository', 'git_branch', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'health_check_path', 'health_check_enabled', 'limits_memory', 'status'];
|
||||
|
||||
foreach ($userFields as $field) {
|
||||
expect($application->isFillable($field))
|
||||
->toBeTrue("Application model should allow mass assignment of '{$field}'");
|
||||
}
|
||||
});
|
||||
|
||||
test('Application model allows mass assignment of relationship fields needed for create()', function () {
|
||||
$application = new Application;
|
||||
$relationFields = ['environment_id', 'destination_id', 'destination_type', 'source_id', 'source_type', 'private_key_id', 'repository_project_id'];
|
||||
|
||||
foreach ($relationFields as $field) {
|
||||
expect($application->isFillable($field))
|
||||
->toBeTrue("Application model should allow mass assignment of '{$field}' for internal create() calls");
|
||||
}
|
||||
});
|
||||
|
||||
test('Application fill ignores non-fillable fields', function () {
|
||||
$application = new Application;
|
||||
$application->fill([
|
||||
'name' => 'test-app',
|
||||
'team_id' => 999,
|
||||
]);
|
||||
|
||||
expect($application->name)->toBe('test-app');
|
||||
expect($application->team_id)->toBeNull();
|
||||
});
|
||||
|
||||
test('Server model has $fillable and no conflicting $guarded', function () {
|
||||
$server = new Server;
|
||||
$fillable = $server->getFillable();
|
||||
$guarded = $server->getGuarded();
|
||||
|
||||
expect($fillable)->not->toBeEmpty('Server model should have explicit $fillable');
|
||||
expect($guarded)->not->toBe([], 'Server model should not have $guarded = [] overriding $fillable');
|
||||
});
|
||||
|
||||
test('Server model blocks mass assignment of dangerous fields', function () {
|
||||
$server = new Server;
|
||||
|
||||
expect($server->isFillable('id'))->toBeFalse();
|
||||
expect($server->isFillable('uuid'))->toBeFalse();
|
||||
expect($server->isFillable('created_at'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('User model blocks mass assignment of auth-sensitive fields', function () {
|
||||
$user = new User;
|
||||
|
||||
expect($user->isFillable('id'))->toBeFalse('User id should not be fillable');
|
||||
expect($user->isFillable('email_verified_at'))->toBeFalse('email_verified_at should not be fillable');
|
||||
expect($user->isFillable('remember_token'))->toBeFalse('remember_token should not be fillable');
|
||||
expect($user->isFillable('two_factor_secret'))->toBeFalse('two_factor_secret should not be fillable');
|
||||
expect($user->isFillable('two_factor_recovery_codes'))->toBeFalse('two_factor_recovery_codes should not be fillable');
|
||||
});
|
||||
|
||||
test('User model allows mass assignment of profile fields', function () {
|
||||
$user = new User;
|
||||
|
||||
expect($user->isFillable('name'))->toBeTrue();
|
||||
expect($user->isFillable('email'))->toBeTrue();
|
||||
expect($user->isFillable('password'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('Team model blocks mass assignment of internal fields', function () {
|
||||
$team = new Team;
|
||||
|
||||
expect($team->isFillable('id'))->toBeFalse();
|
||||
expect($team->isFillable('personal_team'))->toBeFalse('personal_team should not be fillable');
|
||||
});
|
||||
|
||||
test('Service model blocks mass assignment of identity fields', function () {
|
||||
$service = new Service;
|
||||
|
||||
expect($service->isFillable('id'))->toBeFalse();
|
||||
expect($service->isFillable('uuid'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('Service model allows mass assignment of relationship fields needed for create()', function () {
|
||||
$service = new Service;
|
||||
|
||||
expect($service->isFillable('environment_id'))->toBeTrue();
|
||||
expect($service->isFillable('destination_id'))->toBeTrue();
|
||||
expect($service->isFillable('destination_type'))->toBeTrue();
|
||||
expect($service->isFillable('server_id'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('standalone database models block mass assignment of identity and relationship fields', function () {
|
||||
$models = [
|
||||
StandalonePostgresql::class,
|
||||
StandaloneRedis::class,
|
||||
StandaloneMysql::class,
|
||||
StandaloneMariadb::class,
|
||||
StandaloneMongodb::class,
|
||||
StandaloneKeydb::class,
|
||||
StandaloneDragonfly::class,
|
||||
StandaloneClickhouse::class,
|
||||
];
|
||||
|
||||
foreach ($models as $modelClass) {
|
||||
$model = new $modelClass;
|
||||
|
||||
expect($model->isFillable('id'))
|
||||
->toBeFalse("{$modelClass} should not allow mass assignment of 'id'");
|
||||
expect($model->isFillable('uuid'))
|
||||
->toBeFalse("{$modelClass} should not allow mass assignment of 'uuid'");
|
||||
expect($model->isFillable('environment_id'))
|
||||
->toBeFalse("{$modelClass} should not allow mass assignment of 'environment_id'");
|
||||
expect($model->isFillable('destination_id'))
|
||||
->toBeFalse("{$modelClass} should not allow mass assignment of 'destination_id'");
|
||||
expect($model->isFillable('destination_type'))
|
||||
->toBeFalse("{$modelClass} should not allow mass assignment of 'destination_type'");
|
||||
}
|
||||
});
|
||||
|
||||
test('standalone database models allow mass assignment of config fields', function () {
|
||||
$model = new StandalonePostgresql;
|
||||
expect($model->isFillable('name'))->toBeTrue();
|
||||
expect($model->isFillable('postgres_user'))->toBeTrue();
|
||||
expect($model->isFillable('postgres_password'))->toBeTrue();
|
||||
expect($model->isFillable('image'))->toBeTrue();
|
||||
expect($model->isFillable('limits_memory'))->toBeTrue();
|
||||
|
||||
$model = new StandaloneRedis;
|
||||
expect($model->isFillable('redis_conf'))->toBeTrue();
|
||||
|
||||
$model = new StandaloneMysql;
|
||||
expect($model->isFillable('mysql_root_password'))->toBeTrue();
|
||||
|
||||
$model = new StandaloneMongodb;
|
||||
expect($model->isFillable('mongo_initdb_root_username'))->toBeTrue();
|
||||
});
|
||||
|
||||
test('standalone database models allow mass assignment of public_port_timeout', function () {
|
||||
$models = [
|
||||
StandalonePostgresql::class,
|
||||
StandaloneRedis::class,
|
||||
StandaloneMysql::class,
|
||||
StandaloneMariadb::class,
|
||||
StandaloneMongodb::class,
|
||||
StandaloneKeydb::class,
|
||||
StandaloneDragonfly::class,
|
||||
StandaloneClickhouse::class,
|
||||
];
|
||||
|
||||
foreach ($models as $modelClass) {
|
||||
$model = new $modelClass;
|
||||
expect($model->isFillable('public_port_timeout'))
|
||||
->toBeTrue("{$modelClass} should allow mass assignment of 'public_port_timeout'");
|
||||
}
|
||||
});
|
||||
|
||||
test('standalone database models allow mass assignment of SSL fields where applicable', function () {
|
||||
// Models with enable_ssl
|
||||
$sslModels = [
|
||||
StandalonePostgresql::class,
|
||||
StandaloneMysql::class,
|
||||
StandaloneMariadb::class,
|
||||
StandaloneMongodb::class,
|
||||
StandaloneRedis::class,
|
||||
StandaloneKeydb::class,
|
||||
StandaloneDragonfly::class,
|
||||
];
|
||||
|
||||
foreach ($sslModels as $modelClass) {
|
||||
$model = new $modelClass;
|
||||
expect($model->isFillable('enable_ssl'))
|
||||
->toBeTrue("{$modelClass} should allow mass assignment of 'enable_ssl'");
|
||||
}
|
||||
|
||||
// Clickhouse has no SSL columns
|
||||
expect((new StandaloneClickhouse)->isFillable('enable_ssl'))->toBeFalse();
|
||||
|
||||
// Models with ssl_mode
|
||||
$sslModeModels = [
|
||||
StandalonePostgresql::class,
|
||||
StandaloneMysql::class,
|
||||
StandaloneMongodb::class,
|
||||
];
|
||||
|
||||
foreach ($sslModeModels as $modelClass) {
|
||||
$model = new $modelClass;
|
||||
expect($model->isFillable('ssl_mode'))
|
||||
->toBeTrue("{$modelClass} should allow mass assignment of 'ssl_mode'");
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue