From 91acd4cb6ab6493be27488ccbc8ad97e87f8d13c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 09:34:35 +0200 Subject: [PATCH 01/29] fix: backups should be done with internal db url fix: create default database on mongodb start with a collection --- app/Actions/Database/StartMongodb.php | 15 +++++++++++++++ app/Jobs/DatabaseBackupJob.php | 2 +- app/Models/StandaloneMongodb.php | 16 ++++++++++++---- app/Models/StandalonePostgresql.php | 4 ++-- config/sentry.php | 4 ++-- config/version.php | 2 +- versions.json | 2 +- 7 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index fbdf0bba4..645ed6ee9 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -94,6 +94,14 @@ class StartMongodb ]; $docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf'; } + $this->add_default_database(); + $docker_compose['services'][$container_name]['volumes'][] = [ + 'type' => 'bind', + 'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d', + 'target' => '/docker-entrypoint-initdb.d', + 'read_only' => true, + ]; + $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml"; @@ -160,4 +168,11 @@ class StartMongodb $content_base64 = base64_encode($content); $this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}"; } + private function add_default_database() + { + $content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});"; + $content_base64 = base64_encode($content); + $this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d"; + $this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js"; + } } diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 9104434ea..84aad1b6c 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -170,7 +170,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted private function backup_standalone_mongodb(string $databaseWithCollections): void { try { - $url = $this->database->getDbUrl(); + $url = $this->database->getDbUrl(useInternal: true); if ($databaseWithCollections === 'all') { $commands[] = "mkdir -p " . $this->backup_dir; $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location"; diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index 56c644481..6d9158a64 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -15,8 +15,16 @@ class StandaloneMongodb extends BaseModel { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'mongodb-data-' . $database->uuid, - 'mount_path' => '/data', + 'name' => 'mongodb-configdb-' . $database->uuid, + 'mount_path' => '/data/configdb', + 'host_path' => null, + 'resource_id' => $database->id, + 'resource_type' => $database->getMorphClass(), + 'is_readonly' => true + ]); + LocalPersistentVolume::create([ + 'name' => 'mongodb-db-' . $database->uuid, + 'mount_path' => '/data/db', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), @@ -55,8 +63,8 @@ class StandaloneMongodb extends BaseModel { return 'standalone-mongodb'; } - public function getDbUrl() { - if ($this->is_public) { + public function getDbUrl(bool $useInternal = false) { + if ($this->is_public && !$useInternal) { return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; } else { return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true"; diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 669b43f58..3a0432180 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -62,9 +62,9 @@ class StandalonePostgresql extends BaseModel { return 'standalone-postgresql'; } - public function getDbUrl(): string + public function getDbUrl(bool $useInternal = false): string { - if ($this->is_public) { + if ($this->is_public && !$useInternal) { return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}"; } else { return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}"; diff --git a/config/sentry.php b/config/sentry.php index a7d0a2a84..b8cd87fa3 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -3,11 +3,11 @@ return [ // @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - 'dsn' => 'https://72f02655749d5d687297b6b9f078b8b9@o1082494.ingest.sentry.io/4505347448045568', + 'dsn' => 'https://c35fe90ee56e18b220bb55e8217d4839@o1082494.ingest.sentry.io/4505347448045568', // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.98', + 'release' => '4.0.0-beta.99', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 36134c947..63857a361 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 24 Oct 2023 10:10:45 +0200 Subject: [PATCH 02/29] fix: clone to with the same environment name --- app/Http/Livewire/Project/CloneProject.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Http/Livewire/Project/CloneProject.php b/app/Http/Livewire/Project/CloneProject.php index b271a7970..e12138d10 100644 --- a/app/Http/Livewire/Project/CloneProject.php +++ b/app/Http/Livewire/Project/CloneProject.php @@ -55,18 +55,21 @@ class CloneProject extends Component 'selectedServer' => 'required', 'newProjectName' => 'required', ]); + $foundProject = Project::where('name', $this->newProjectName)->first(); + if ($foundProject) { + throw new \Exception('Project with the same name already exists.'); + } $newProject = Project::create([ 'name' => $this->newProjectName, 'team_id' => currentTeam()->id, 'description' => $this->project->description . ' (clone)', ]); - if ($this->environment->id !== 1) { + if ($this->environment->name !== 'production') { $newProject->environments()->create([ 'name' => $this->environment->name, ]); - $newProject->environments()->find(1)->delete(); } - $newEnvironment = $newProject->environments->first(); + $newEnvironment = $newProject->environments->where('name', $this->environment->name)->first(); // Clone Applications $applications = $this->environment->applications; $databases = $this->environment->databases(); @@ -80,7 +83,6 @@ class CloneProject extends Component 'environment_id' => $newEnvironment->id, 'destination_id' => $this->selectedServer, ]); - $newApplication->environment_id = $newProject->environments->first()->id; $newApplication->save(); $environmentVaribles = $application->environment_variables()->get(); foreach ($environmentVaribles as $environmentVarible) { @@ -105,7 +107,6 @@ class CloneProject extends Component 'environment_id' => $newEnvironment->id, 'destination_id' => $this->selectedServer, ]); - $newDatabase->environment_id = $newProject->environments->first()->id; $newDatabase->save(); $environmentVaribles = $database->environment_variables()->get(); foreach ($environmentVaribles as $environmentVarible) { @@ -128,7 +129,6 @@ class CloneProject extends Component 'environment_id' => $newEnvironment->id, 'destination_id' => $this->selectedServer, ]); - $newService->environment_id = $newProject->environments->first()->id; $newService->save(); $newService->parse(); } From 554222abc71d139edf2cb10cc9934906e998b0b0 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 10:10:55 +0200 Subject: [PATCH 03/29] fix: cleanup stucked resources on start --- app/Console/Commands/Init.php | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index 446b0c693..6abac7029 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -3,7 +3,12 @@ namespace App\Console\Commands; use App\Enums\ApplicationDeploymentStatus; +use App\Models\Application; use App\Models\ApplicationDeploymentQueue; +use App\Models\Service; +use App\Models\StandaloneMongodb; +use App\Models\StandalonePostgresql; +use App\Models\StandaloneRedis; use Illuminate\Console\Command; class Init extends Command @@ -13,7 +18,9 @@ class Init extends Command public function handle() { + ray()->clearAll(); $this->cleanup_in_progress_application_deployments(); + $this->cleanup_stucked_resources(); } private function cleanup_in_progress_application_deployments() @@ -30,4 +37,70 @@ class Init extends Command echo "Error: {$e->getMessage()}\n"; } } + private function cleanup_stucked_resources() { + // Cleanup any resources that are not attached to any environment or destination or server + try { + $applications = Application::all(); + foreach($applications as $application) { + if (!$application->environment) { + ray('Application without environment', $application->name); + $application->delete(); + } + if (!$application->destination()) { + ray('Application without destination', $application->name); + $application->delete(); + } + } + $postgresqls = StandalonePostgresql::all(); + foreach($postgresqls as $postgresql) { + if (!$postgresql->environment) { + ray('Postgresql without environment', $postgresql->name); + $postgresql->delete(); + } + if (!$postgresql->destination()) { + ray('Postgresql without destination', $postgresql->name); + $postgresql->delete(); + } + } + $redis = StandaloneRedis::all(); + foreach($redis as $redis) { + if (!$redis->environment) { + ray('Redis without environment', $redis->name); + $redis->delete(); + } + if (!$redis->destination()) { + ray('Redis without destination', $redis->name); + $redis->delete(); + } + } + $mongodbs = StandaloneMongodb::all(); + foreach($mongodbs as $mongodb) { + if (!$mongodb->environment) { + ray('Mongodb without environment', $mongodb->name); + $mongodb->delete(); + } + if (!$mongodb->destination()) { + ray('Mongodb without destination', $mongodb->name); + $mongodb->delete(); + } + } + $services = Service::all(); + foreach($services as $service) { + if (!$service->environment) { + ray('Service without environment', $service->name); + $service->delete(); + } + if (!$service->server) { + ray('Service without server', $service->name); + $service->delete(); + } + if (!$service->destination()) { + ray('Service without destination', $service->name); + $service->delete(); + } + } + } catch (\Throwable $e) { + echo "Error: {$e->getMessage()}\n"; + } + } } From 8bfc1a7c06d6b9d848805905ef22ffe653ad08b6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 10:11:21 +0200 Subject: [PATCH 04/29] fix: do not allow to delete env if a resource is defined --- app/Http/Livewire/Project/DeleteEnvironment.php | 8 ++++---- app/Models/Environment.php | 2 +- app/Models/Project.php | 2 +- resources/views/project/resources.blade.php | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/Http/Livewire/Project/DeleteEnvironment.php b/app/Http/Livewire/Project/DeleteEnvironment.php index f341d7cb5..0b6254b3b 100644 --- a/app/Http/Livewire/Project/DeleteEnvironment.php +++ b/app/Http/Livewire/Project/DeleteEnvironment.php @@ -21,10 +21,10 @@ class DeleteEnvironment extends Component 'environment_id' => 'required|int', ]); $environment = Environment::findOrFail($this->environment_id); - if ($environment->applications->count() > 0) { - return $this->emit('error', 'Environment has resources defined, please delete them first.'); + if ($environment->isEmpty()) { + $environment->delete(); + return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]); } - $environment->delete(); - return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]); + return $this->emit('error', 'Environment has defined resources, please delete them first.'); } } diff --git a/app/Models/Environment.php b/app/Models/Environment.php index 8f67ed004..941586edd 100644 --- a/app/Models/Environment.php +++ b/app/Models/Environment.php @@ -12,7 +12,7 @@ class Environment extends Model 'project_id', ]; - public function can_delete_environment() + public function isEmpty() { return $this->applications()->count() == 0 && $this->redis()->count() == 0 && diff --git a/app/Models/Project.php b/app/Models/Project.php index f8f9622b8..a910348b4 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -18,7 +18,7 @@ class Project extends BaseModel 'project_id' => $project->id, ]); Environment::create([ - 'name' => 'Production', + 'name' => 'production', 'project_id' => $project->id, ]); }); diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index da726e658..ef2780719 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -2,17 +2,17 @@

Resources

- @if ($environment->can_delete_environment()) + + Clone + + @if ($environment->isEmpty()) @else + New @endif - - Clone -
- @if ($environment->can_delete_environment()) + @if ($environment->isEmpty()) + Add New Resource @endif From 69691b2ca7929d8f0b4a22e66f7f5eeb86c2049a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 10:19:12 +0200 Subject: [PATCH 05/29] fix: service template generator + appwrite --- app/Console/Commands/GenerateServiceTemplates.php | 4 +++- app/Http/Livewire/Project/Service/ComposeModal.php | 4 ++-- templates/service-templates.json | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Console/Commands/GenerateServiceTemplates.php b/app/Console/Commands/GenerateServiceTemplates.php index 6220a3774..bf271e8ad 100644 --- a/app/Console/Commands/GenerateServiceTemplates.php +++ b/app/Console/Commands/GenerateServiceTemplates.php @@ -89,7 +89,9 @@ class GenerateServiceTemplates extends Command 'compose' => $yaml, ]; if ($env_file) { - $payload['envs'] = $env_file; + $env_file_content = file_get_contents(base_path("templates/compose/$env_file")); + $env_file_base64 = base64_encode($env_file_content); + $payload['envs'] = $env_file_base64; } return $payload; } diff --git a/app/Http/Livewire/Project/Service/ComposeModal.php b/app/Http/Livewire/Project/Service/ComposeModal.php index 0c9f5e98f..4203f4507 100644 --- a/app/Http/Livewire/Project/Service/ComposeModal.php +++ b/app/Http/Livewire/Project/Service/ComposeModal.php @@ -6,8 +6,8 @@ use Livewire\Component; class ComposeModal extends Component { - public string $raw; - public string $actual; + public ?string $raw = null; + public ?string $actual = null; public function render() { return view('livewire.project.service.compose-modal'); diff --git a/templates/service-templates.json b/templates/service-templates.json index 7d3136784..33576e92b 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -8,7 +8,7 @@ "documentation": "https:\/\/appwrite.io\/docs", "slogan": "Appwrite is a self-hosted backend-as-a-service platform that simplifies the development of web and mobile applications by providing a range of features and APIs.", "compose": "eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnZlcnNpb246ICczJwpzZXJ2aWNlczoKICBhcHB3cml0ZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40JwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBsYWJlbHM6CiAgICAgIC0gdHJhZWZpay5jb25zdHJhaW50LWxhYmVsLXN0YWNrPWFwcHdyaXRlCiAgICAgIC0gdHJhZWZpay5kb2NrZXIubmV0d29yaz1hcHB3cml0ZQogICAgICAtIHRyYWVmaWsuaHR0cC5zZXJ2aWNlcy5hcHB3cml0ZV9hcGkubG9hZGJhbGFuY2VyLnNlcnZlci5wb3J0PTgwCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfYXBpX2h0dHAuZW50cnlwb2ludHM9d2ViCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfYXBpX2h0dHAucnVsZT1QYXRoUHJlZml4KGAvYCkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cC5zZXJ2aWNlPWFwcHdyaXRlX2FwaQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX2FwaV9odHRwcy5lbnRyeXBvaW50cz13ZWJzZWN1cmUKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMucnVsZT1QYXRoUHJlZml4KGAvYCkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMuc2VydmljZT1hcHB3cml0ZV9hcGkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMudGxzPXRydWUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICAgICAgLSByZWRpcwogICAgICAtIGluZmx1eGRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQVBQV1JJVEU9LwogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0xPQ0FMRQogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfUk9PVAogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfRU1BSUxTCiAgICAgIC0gX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFMKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1NZU1RFTV9SRVNQT05TRV9GT1JNQVQKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFMKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlMKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfSU5GTFVYREJfSE9TVAogICAgICAtIF9BUFBfSU5GTFVYREJfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU1JVAogICAgICAtIF9BUFBfU1RPUkFHRV9QUkVWSUVXX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVUwogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVAogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0NQVVMKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19NRU1PUlkKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfU1RBVFNEX0hPU1QKICAgICAgLSBfQVBQX1NUQVRTRF9QT1JUCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0VYRUNVVElPTgogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0NBQ0hFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQUJVU0UKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUwogICAgICAtIF9BUFBfU01TX1BST1ZJREVSCiAgICAgIC0gX0FQUF9TTVNfRlJPTQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfQkFUQ0hfU0laRQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfQ09NUExFWElUWQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfREVQVEgKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1dFQkhPT0tfU0VDUkVUCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0NMSUVOVF9TRUNSRVQKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfU0VDUkVUCiAgICAgIC0gX0FQUF9BU1NJU1RBTlRfT1BFTkFJX0FQSV9LRVkKICBhcHB3cml0ZS1yZWFsdGltZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiByZWFsdGltZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlYWx0aW1lCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBsYWJlbHM6CiAgICAgIC0gdHJhZWZpay5jb25zdHJhaW50LWxhYmVsLXN0YWNrPWFwcHdyaXRlCiAgICAgIC0gdHJhZWZpay5kb2NrZXIubmV0d29yaz1hcHB3cml0ZQogICAgICAtIHRyYWVmaWsuaHR0cC5zZXJ2aWNlcy5hcHB3cml0ZV9yZWFsdGltZS5sb2FkYmFsYW5jZXIuc2VydmVyLnBvcnQ9ODAKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93cy5lbnRyeXBvaW50cz13ZWIKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93cy5ydWxlPVBhdGhQcmVmaXgoYC92MS9yZWFsdGltZWApCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3Muc2VydmljZT1hcHB3cml0ZV9yZWFsdGltZQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX3JlYWx0aW1lX3dzcy5lbnRyeXBvaW50cz13ZWJzZWN1cmUKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93c3MucnVsZT1QYXRoUHJlZml4KGAvdjEvcmVhbHRpbWVgKQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX3JlYWx0aW1lX3dzcy5zZXJ2aWNlPWFwcHdyaXRlX3JlYWx0aW1lCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3NzLnRscz10cnVlCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3NzLnRscy5jZXJ0cmVzb2x2ZXI9ZG5zCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSBtYXJpYWRiCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BUFBXUklURT0vdjEvcmVhbHRpbWUKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUFRJT05TX0FCVVNFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItYXVkaXRzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1hdWRpdHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYXVkaXRzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci13ZWJob29rczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItd2ViaG9va3MKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItd2ViaG9va3MKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgICAtIG1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZGVsZXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItZGVsZXRlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kZWxldGVzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS11cGxvYWRzOi9zdG9yYWdlL3VwbG9hZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNhY2hlOi9zdG9yYWdlL2NhY2hlOnJ3JwogICAgICAtICdhcHB3cml0ZS1mdW5jdGlvbnM6L3N0b3JhZ2UvZnVuY3Rpb25zOnJ3JwogICAgICAtICdhcHB3cml0ZS1idWlsZHM6L3N0b3JhZ2UvYnVpbGRzOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIF9BUFBfRVhFQ1VUT1JfSE9TVAogIGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogd29ya2VyLWRhdGFiYXNlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXMKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgICAtIG1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtd29ya2VyLWJ1aWxkczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItYnVpbGRzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWJ1aWxkcwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBkZXBlbmRzX29uOgogICAgICAtIHJlZGlzCiAgICAgIC0gbWFyaWFkYgogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9PUFRJT05TX0ZPUkNFX0hUVFBTCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1jZXJ0aWZpY2F0ZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS1jb25maWc6L3N0b3JhZ2UvY29uZmlnOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICAgIC0gb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9ET0NLRVJfSFVCX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogIGFwcHdyaXRlLXdvcmtlci1tYWlsczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWFpbHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWFpbHMKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmc6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogd29ya2VyLW1lc3NhZ2luZwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01TX1BST1ZJREVSCiAgICAgIC0gX0FQUF9TTVNfRlJPTQogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItbWlncmF0aW9uczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWlncmF0aW9ucwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQKICBhcHB3cml0ZS1tYWludGVuYW5jZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiBtYWludGVuYW5jZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1haW50ZW5hbmNlCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICBhcHB3cml0ZS11c2FnZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB1c2FnZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXVzYWdlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gaW5mbHV4ZGIKICAgICAgLSBtYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9JTkZMVVhEQl9IT1NUCiAgICAgIC0gX0FQUF9JTkZMVVhEQl9QT1JUCiAgICAgIC0gX0FQUF9VU0FHRV9BR0dSRUdBVElPTl9JTlRFUlZBTAogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtc2NoZWR1bGU6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogc2NoZWR1bGUKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLWFzc2lzdGFudDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXNzaXN0YW50OjAuMi4xJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWFzc2lzdGFudAogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0FTU0lTVEFOVF9PUEVOQUlfQVBJX0tFWQogIG9wZW5ydW50aW1lcy1leGVjdXRvcjoKICAgIGNvbnRhaW5lcl9uYW1lOiBvcGVucnVudGltZXMtZXhlY3V0b3IKICAgIGhvc3RuYW1lOiBhcHB3cml0ZS1leGVjdXRvcgogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgc3RvcF9zaWduYWw6IFNJR0lOVAogICAgaW1hZ2U6ICdvcGVucnVudGltZXMvZXhlY3V0b3I6MC40LjEnCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgICAtIHJ1bnRpbWVzCiAgICB2b2x1bWVzOgogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnL3RtcDovdG1wOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gT1BSX0VYRUNVVE9SX0lOQUNUSVZFX1RSRVNIT0xEPSRfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQKICAgICAgLSBPUFJfRVhFQ1VUT1JfTUFJTlRFTkFOQ0VfSU5URVJWQUw9JF9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMCiAgICAgIC0gT1BSX0VYRUNVVE9SX05FVFdPUks9JF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUksKICAgICAgLSBPUFJfRVhFQ1VUT1JfRE9DS0VSX0hVQl9VU0VSTkFNRT0kX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FCiAgICAgIC0gT1BSX0VYRUNVVE9SX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9JF9BUFBfRE9DS0VSX0hVQl9QQVNTV09SRAogICAgICAtIE9QUl9FWEVDVVRPUl9FTlY9JF9BUFBfRU5WCiAgICAgIC0gT1BSX0VYRUNVVE9SX1JVTlRJTUVTPSRfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIE9QUl9FWEVDVVRPUl9TRUNSRVQ9JF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX0xPR0dJTkdfUFJPVklERVI9JF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIE9QUl9FWEVDVVRPUl9MT0dHSU5HX0NPTkZJRz0kX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RFVklDRT0kX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfU0VDUkVUPSRfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfUkVHSU9OPSRfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfQlVDS0VUPSRfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQ9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT049JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfUkVHSU9OPSRfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX1JFR0lPTj0kX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfQlVDS0VUPSRfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogIG1hcmlhZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTAuNycKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1tYXJpYWRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLW1hcmlhZGI6L3Zhci9saWIvbXlzcWw6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke19BUFBfREJfUk9PVF9QQVNTfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtfQVBQX0RCX1NDSEVNQX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtfQVBQX0RCX1VTRVJ9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke19BUFBfREJfUEFTU30nCiAgICBjb21tYW5kOiAnbXlzcWxkIC0taW5ub2RiLWZsdXNoLW1ldGhvZD1mc3luYycKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny4wLjQtYWxwaW5lJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlZGlzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogInJlZGlzLXNlcnZlciAtLW1heG1lbW9yeSAgICAgICAgICAgIDUxMm1iIC0tbWF4bWVtb3J5LXBvbGljeSAgICAgYWxsa2V5cy1scnUgLS1tYXhtZW1vcnktc2FtcGxlcyAgICA1XG4iCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtcmVkaXM6L2RhdGE6cncnCiAgaW5mbHV4ZGI6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2luZmx1eGRiOjEuNS4wJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWluZmx1eGRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWluZmx1eGRiOi92YXIvbGliL2luZmx1eGRiOnJ3JwogIHRlbGVncmFmOgogICAgaW1hZ2U6ICdhcHB3cml0ZS90ZWxlZ3JhZjoxLjQuMCcKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS10ZWxlZ3JhZgogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0lORkxVWERCX0hPU1QKICAgICAgLSBfQVBQX0lORkxVWERCX1BPUlQKbmV0d29ya3M6CiAgZ2F0ZXdheToKICAgIG5hbWU6IGdhdGV3YXkKICBhcHB3cml0ZToKICAgIG5hbWU6IGFwcHdyaXRlCiAgcnVudGltZXM6CiAgICBuYW1lOiBydW50aW1lcwp2b2x1bWVzOgogIGFwcHdyaXRlLW1hcmlhZGI6IG51bGwKICBhcHB3cml0ZS1yZWRpczogbnVsbAogIGFwcHdyaXRlLWNhY2hlOiBudWxsCiAgYXBwd3JpdGUtdXBsb2FkczogbnVsbAogIGFwcHdyaXRlLWNlcnRpZmljYXRlczogbnVsbAogIGFwcHdyaXRlLWZ1bmN0aW9uczogbnVsbAogIGFwcHdyaXRlLWJ1aWxkczogbnVsbAogIGFwcHdyaXRlLWluZmx1eGRiOiBudWxsCiAgYXBwd3JpdGUtY29uZmlnOiBudWxsCg==", - "envs": "appwrite.env" + "envs": "X0FQUF9FTlY9cHJvZHVjdGlvbgpfQVBQX0xPQ0FMRT1lbgpfQVBQX09QVElPTlNfQUJVU0U9ZW5hYmxlZApfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFM9ZGlzYWJsZWQKX0FQUF9PUEVOU1NMX0tFWV9WMT0KX0FQUF9ET01BSU5fRlVOQ1RJT05TPQpfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1Q9ZW5hYmxlZApfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUz0KX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFM9Cl9BUFBfU1lTVEVNX0VNQUlMX05BTUU9QXBwd3JpdGUKX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUz10ZWFtQGFwcHdyaXRlLmlvCl9BUFBfU1lTVEVNX1JFU1BPTlNFX0ZPUk1BVD0KX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUz1jZXJ0c0BhcHB3cml0ZS5pbwpfQVBQX1VTQUdFX1NUQVRTPWVuYWJsZWQKX0FQUF9MT0dHSU5HX1BST1ZJREVSPQpfQVBQX0xPR0dJTkdfQ09ORklHPQpfQVBQX1VTQUdFX0FHR1JFR0FUSU9OX0lOVEVSVkFMPTMwCl9BUFBfVVNBR0VfVElNRVNFUklFU19JTlRFUlZBTD0zMApfQVBQX1VTQUdFX0RBVEFCQVNFX0lOVEVSVkFMPTkwMApfQVBQX1dPUktFUl9QRVJfQ09SRT02Cl9BUFBfUkVESVNfSE9TVD1yZWRpcwpfQVBQX1JFRElTX1BPUlQ9NjM3OQpfQVBQX1JFRElTX1VTRVI9Cl9BUFBfUkVESVNfUEFTUz0KX0FQUF9EQl9IT1NUPW1hcmlhZGIKX0FQUF9EQl9QT1JUPTMzMDYKX0FQUF9EQl9TQ0hFTUE9YXBwd3JpdGUKX0FQUF9EQl9VU0VSPSRTRVJWSUNFX1VTRVJfTVlTUUwKX0FQUF9EQl9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCl9BUFBfREJfUk9PVF9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1RNWVNRTApfQVBQX0lORkxVWERCX0hPU1Q9aW5mbHV4ZGIKX0FQUF9JTkZMVVhEQl9QT1JUPTgwODYKX0FQUF9TVEFUU0RfSE9TVD10ZWxlZ3JhZgpfQVBQX1NUQVRTRF9QT1JUPTgxMjUKX0FQUF9TTVRQX0hPU1Q9Cl9BUFBfU01UUF9QT1JUPQpfQVBQX1NNVFBfU0VDVVJFPQpfQVBQX1NNVFBfVVNFUk5BTUU9Cl9BUFBfU01UUF9QQVNTV09SRD0KX0FQUF9TTVNfUFJPVklERVI9Cl9BUFBfU01TX0ZST009Cl9BUFBfU1RPUkFHRV9MSU1JVD0zMDAwMDAwMApfQVBQX1NUT1JBR0VfUFJFVklFV19MSU1JVD0yMDAwMDAwMApfQVBQX1NUT1JBR0VfQU5USVZJUlVTPWRpc2FibGVkCl9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVD1jbGFtYXYKX0FQUF9TVE9SQUdFX0FOVElWSVJVU19QT1JUPTMzMTAKX0FQUF9TVE9SQUdFX0RFVklDRT1sb2NhbApfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX1MzX1JFR0lPTj11cy1lYXN0LTEKX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT049dXMtZWFzdC0xCl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTj11cy13ZXN0LTAwNApfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT049ZXUtY2VudHJhbC0xCl9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUPQpfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUPQpfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTj1ldS1jZW50cmFsLTEKX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQ9Cl9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQ9MzAwMDAwMDAKX0FQUF9GVU5DVElPTlNfVElNRU9VVD05MDAKX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVD05MDAKX0FQUF9GVU5DVElPTlNfQ09OVEFJTkVSUz0xMApfQVBQX0ZVTkNUSU9OU19DUFVTPTAKX0FQUF9GVU5DVElPTlNfTUVNT1JZPTAKX0FQUF9GVU5DVElPTlNfTUVNT1JZX1NXQVA9MApfQVBQX0ZVTkNUSU9OU19SVU5USU1FUz1ub2RlLTE2LjAscGhwLTguMCxweXRob24tMy45LHJ1YnktMy4wCl9BUFBfRVhFQ1VUT1JfU0VDUkVUPXlvdXItc2VjcmV0LWtleQpfQVBQX0VYRUNVVE9SX0hPU1Q9aHR0cDovL2FwcHdyaXRlLWV4ZWN1dG9yL3YxCl9BUFBfRVhFQ1VUT1JfUlVOVElNRV9ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX0VOVlM9bm9kZS0xNi4wLHBocC03LjQscHl0aG9uLTMuOSxydWJ5LTMuMApfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQ9NjAKRE9DS0VSSFVCX1BVTExfVVNFUk5BTUU9CkRPQ0tFUkhVQl9QVUxMX1BBU1NXT1JEPQpET0NLRVJIVUJfUFVMTF9FTUFJTD0KT1BFTl9SVU5USU1FU19ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUks9cnVudGltZXMKX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FPQpfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9Cl9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMPTM2MDAKX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FPQpfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVk9Cl9BUFBfVkNTX0dJVEhVQl9BUFBfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUPQpfQVBQX1ZDU19HSVRIVUJfV0VCSE9PS19TRUNSRVQ9Cl9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUw9ODY0MDAKX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEU9MjU5MjAwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT049MTIwOTYwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVD0xMjA5NjAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFPTg2NDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWT04NjQwMDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUz04NjQwMApfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkU9MTAKX0FQUF9HUkFQSFFMX01BWF9DT01QTEVYSVRZPTI1MApfQVBQX0dSQVBIUUxfTUFYX0RFUFRIPTMKX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRD0KX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQ9Cl9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZPQo=" }, "babybuddy": { "documentation": "https:\/\/docs.baby-buddy.net", From 551032177672881439cc8f2059ece4cb4fda8c63 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 10:22:36 +0200 Subject: [PATCH 06/29] syncbunny update --- app/Console/Commands/SyncBunny.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/SyncBunny.php b/app/Console/Commands/SyncBunny.php index 6a85ffd91..509614f4c 100644 --- a/app/Console/Commands/SyncBunny.php +++ b/app/Console/Commands/SyncBunny.php @@ -16,7 +16,7 @@ class SyncBunny extends Command * * @var string */ - protected $signature = 'sync:bunny {--only-template} {--only-version}'; + protected $signature = 'sync:bunny {templates?} {release?}'; /** * The console command description. @@ -31,8 +31,8 @@ class SyncBunny extends Command public function handle() { $that = $this; - $only_template = $this->option('only-template'); - $only_version = $this->option('only-version'); + $only_template = $this->argument('templates'); + $only_version = $this->argument('release'); $bunny_cdn = "https://cdn.coollabs.io"; $bunny_cdn_path = "coolify"; $bunny_cdn_storage_name = "coolcdn"; From f187040b7e2eeffa05de813c984a0b807254e71c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 10:42:28 +0200 Subject: [PATCH 07/29] fix: mongodb backup --- app/Jobs/DatabaseBackupJob.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 84aad1b6c..d73474018 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -175,13 +175,20 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted $commands[] = "mkdir -p " . $this->backup_dir; $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location"; } else { - $collectionsToExclude = str($databaseWithCollections)->after(':')->explode(','); - $databaseName = str($databaseWithCollections)->before(':'); + if (str($databaseWithCollections)->contains(':')) { + $databaseName = str($databaseWithCollections)->before(':'); + $collectionsToExclude = str($databaseWithCollections)->after(':')->explode(','); + } else { + $databaseName = $databaseWithCollections; + $collectionsToExclude = collect(); + } $commands[] = "mkdir -p " . $this->backup_dir; - $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location"; + if ($collectionsToExclude->count() === 0) { + $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location"; + } else { + $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location"; + } } - - ray($commands); $this->backup_output = instant_remote_process($commands, $this->server); $this->backup_output = trim($this->backup_output); if ($this->backup_output === '') { From a6f9e5f0afe94c1f921eebf348f347f9fb5dffa4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 10:42:33 +0200 Subject: [PATCH 08/29] fixes --- resources/views/layouts/base.blade.php | 3 +-- resources/views/project/resources.blade.php | 12 ++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 1a83dc1fb..a5a6ad3af 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -96,8 +96,7 @@ } function copyToClipboard(text) { - navigator.clipboard.writeText(text); - Livewire.emit('success', 'Copied to clipboard.'); + navigator?.clipboard?.writeText(text) && Livewire.emit('success', 'Copied to clipboard.'); } Livewire.on('reloadWindow', (timeout) => { diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index ef2780719..a81982ec7 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -2,16 +2,20 @@

Resources

- - Clone - @if ($environment->isEmpty()) + + Clone + @else + New + + Clone + @endif
@@ -120,7 +120,7 @@ New MySQL
- MySQL + MySQL is an open-source relational database management system known for its speed, reliability, and flexibility in managing and accessing data.
@@ -130,7 +130,7 @@ New Mariadb
- MySQL + MariaDB is an open-source relational database management system that serves as a drop-in replacement for MySQL, offering more robust, scalable, and reliable SQL server capabilities.
From 6e73f7f2e49992a8abb5fc93af9f2d3df7bbf353 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 15:40:29 +0200 Subject: [PATCH 17/29] fix: encrypt mongodb password --- app/Models/StandaloneMongodb.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index 6d9158a64..06a6cb537 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -42,6 +42,20 @@ class StandaloneMongodb extends BaseModel }); } + public function mongoInitdbRootPassword(): Attribute + { + return Attribute::make( + get: function ($value) { + try { + return decrypt($value); + } catch (\Throwable $th) { + $this->mongo_initdb_root_password = encrypt($value); + $this->save(); + return $value; + } + } + ); + } public function portsMappings(): Attribute { return Attribute::make( @@ -63,7 +77,8 @@ class StandaloneMongodb extends BaseModel { return 'standalone-mongodb'; } - public function getDbUrl(bool $useInternal = false) { + public function getDbUrl(bool $useInternal = false) + { if ($this->is_public && !$useInternal) { return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; } else { From 0232cf5b4c4b80e1d90fc0cbb95f04a941e810b2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 15:41:21 +0200 Subject: [PATCH 18/29] feat: lock environment variables --- .../Shared/EnvironmentVariable/All.php | 24 ++++++-- .../Shared/EnvironmentVariable/Show.php | 33 +++++++--- app/Models/EnvironmentVariable.php | 16 +++-- ...wn_once_to_environment_variables_table.php | 28 +++++++++ .../shared/environment-variable/all.blade.php | 3 +- .../environment-variable/show.blade.php | 60 ++++++++++++------- 6 files changed, 121 insertions(+), 43 deletions(-) create mode 100644 database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php index 9b714a590..b1fa237e0 100644 --- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -31,11 +31,17 @@ class All extends Component public function getDevView() { $this->variables = $this->resource->environment_variables->map(function ($item) { + if ($item->is_shown_once) { + return "$item->key=(locked secret)"; + } return "$item->key=$item->value"; })->sort()->join(' '); if ($this->showPreview) { $this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) { + if ($item->is_shown_once) { + return "$item->key=(locked secret)"; + } return "$item->key=$item->value"; })->sort()->join(' '); @@ -49,19 +55,27 @@ class All extends Component { if ($isPreview) { $variables = parseEnvFormatToArray($this->variablesPreview); - $existingVariables = $this->resource->environment_variables_preview(); - $this->resource->environment_variables_preview()->delete(); } else { $variables = parseEnvFormatToArray($this->variables); - $existingVariables = $this->resource->environment_variables(); - $this->resource->environment_variables()->delete(); } foreach ($variables as $key => $variable) { - $found = $existingVariables->where('key', $key)->first(); + $found = $this->resource->environment_variables()->where('key', $key)->first(); + $foundPreview = $this->resource->environment_variables_preview()->where('key', $key)->first(); if ($found) { + if ($found->is_shown_once) { + continue; + } $found->value = $variable; $found->save(); continue; + } + if ($foundPreview) { + if ($foundPreview->is_shown_once) { + continue; + } + $foundPreview->value = $variable; + $foundPreview->save(); + continue; } else { $environment = new EnvironmentVariable(); $environment->key = $key; diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php index 0ad197f1a..eed0f7052 100644 --- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -5,7 +5,6 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable; use App\Models\EnvironmentVariable as ModelsEnvironmentVariable; use Livewire\Component; use Visus\Cuid2\Cuid2; -use Illuminate\Support\Str; class Show extends Component { @@ -13,29 +12,45 @@ class Show extends Component public ModelsEnvironmentVariable $env; public ?string $modalId = null; public bool $isDisabled = false; + public bool $isLocked = false; public string $type; protected $rules = [ 'env.key' => 'required|string', 'env.value' => 'nullable', 'env.is_build_time' => 'required|boolean', + 'env.is_shown_once' => 'required|boolean', ]; protected $validationAttributes = [ - 'key' => 'key', - 'value' => 'value', - 'is_build_time' => 'build', + 'key' => 'Key', + 'value' => 'Value', + 'is_build_time' => 'Build Time', + 'is_shown_once' => 'Shown Once', ]; public function mount() { - $this->isDisabled = false; - if (Str::of($this->env->key)->startsWith('SERVICE_FQDN') || Str::of($this->env->key)->startsWith('SERVICE_URL')) { - $this->isDisabled = true; - } $this->modalId = new Cuid2(7); $this->parameters = get_route_parameters(); + $this->checkEnvs(); + } + public function checkEnvs() + { + $this->isDisabled = false; + if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) { + $this->isDisabled = true; + } + if ($this->env->is_shown_once) { + $this->isLocked = true; + } + } + public function lock() + { + $this->env->is_shown_once = true; + $this->env->save(); + $this->checkEnvs(); + $this->emit('refreshEnvs'); } - public function instantSave() { $this->submit(); diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 37619d190..5450f0127 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -11,7 +11,7 @@ class EnvironmentVariable extends Model { protected $guarded = []; protected $casts = [ - "key" => 'string', + 'key' => 'string', 'value' => 'encrypted', 'is_build_time' => 'boolean', ]; @@ -21,6 +21,10 @@ class EnvironmentVariable extends Model static::created(function ($environment_variable) { if ($environment_variable->application_id && !$environment_variable->is_preview) { $found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first(); + $application = Application::find($environment_variable->application_id); + if ($application->build_pack === 'dockerfile') { + return; + } if (!$found) { ModelsEnvironmentVariable::create([ 'key' => $environment_variable->key, @@ -33,7 +37,8 @@ class EnvironmentVariable extends Model } }); } - public function service() { + public function service() + { return $this->belongsTo(Service::class); } protected function value(): Attribute @@ -55,9 +60,9 @@ class EnvironmentVariable extends Model $variable = Str::after($environment_variable, 'global.'); $variable = Str::before($variable, '}}'); $variable = Str::of($variable)->trim()->value; - // $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value; - ray('global env variable'); - return $environment_variable; + // $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value; + ray('global env variable'); + return $environment_variable; } return $environment_variable; } @@ -77,5 +82,4 @@ class EnvironmentVariable extends Model set: fn (string $value) => Str::of($value)->trim(), ); } - } diff --git a/database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php b/database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php new file mode 100644 index 000000000..e0df21186 --- /dev/null +++ b/database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php @@ -0,0 +1,28 @@ +boolean('is_shown_once')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('environment_variables', function (Blueprint $table) { + $table->dropColumn('is_shown_once'); + }); + } +}; diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index 6297f3822..ec1480e05 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -28,8 +28,7 @@ @endif @else
- + Save
@if ($showPreview) diff --git a/resources/views/livewire/project/shared/environment-variable/show.blade.php b/resources/views/livewire/project/shared/environment-variable/show.blade.php index 6663dc12a..f41cf8bef 100644 --- a/resources/views/livewire/project/shared/environment-variable/show.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php @@ -6,36 +6,54 @@
- @if ($isDisabled) + @if ($isLocked) + + + + + + - - @if ($type !== 'service') - - @endif @else - - - @if ($type !== 'service') - + @if ($isDisabled) + + + @if ($type !== 'service') + + @endif + @else + + + @if ($type !== 'service') + + @endif @endif @endif
- @if ($isDisabled) - - Update - - - Delete - - @else - - Update - + @if ($isLocked) Delete + @else + @if ($isDisabled) + + Update + + + Delete + + @else + + Update + + + Lock + + + Delete + + @endif @endif -
From dc86170ef5e3d3b2ff8d7b67a13048bc5401d8eb Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 24 Oct 2023 15:41:44 +0200 Subject: [PATCH 19/29] version++ --- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/sentry.php b/config/sentry.php index 25d04008a..32e27e081 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.100', + 'release' => '4.0.0-beta.101', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 042813982..d54064579 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 24 Oct 2023 15:47:29 +0200 Subject: [PATCH 20/29] fix: mongodb healtcheck command --- app/Actions/Database/StartMongodb.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 645ed6ee9..8bfb9a982 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -52,7 +52,7 @@ class StartMongodb 'healthcheck' => [ 'test' => [ 'CMD-SHELL', - 'mongo --eval "printjson(db.serverStatus())" | grep uptime | grep -v grep' + 'mongosh --eval "printjson(db.runCommand(\"ping\"))"' ], 'interval' => '5s', 'timeout' => '5s', From d5cc2a2eedc644746049a04626ed886dbb4fcb5f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 09:28:26 +0200 Subject: [PATCH 21/29] feat: download local backups --- .../Project/Database/BackupExecutions.php | 24 +++++++ composer.json | 1 + composer.lock | 62 ++++++++++++++++++- config/sentry.php | 2 +- config/version.php | 2 +- .../database/backup-executions.blade.php | 8 +-- versions.json | 2 +- 7 files changed, 93 insertions(+), 8 deletions(-) diff --git a/app/Http/Livewire/Project/Database/BackupExecutions.php b/app/Http/Livewire/Project/Database/BackupExecutions.php index 2f808d992..f8ec4efbe 100644 --- a/app/Http/Livewire/Project/Database/BackupExecutions.php +++ b/app/Http/Livewire/Project/Database/BackupExecutions.php @@ -2,6 +2,7 @@ namespace App\Http\Livewire\Project\Database; +use Illuminate\Support\Facades\Storage; use Livewire\Component; class BackupExecutions extends Component @@ -23,6 +24,29 @@ class BackupExecutions extends Component $this->emit('success', 'Backup deleted successfully.'); $this->emit('refreshBackupExecutions'); } + public function download($exeuctionId) + { + try { + $execution = $this->backup->executions()->where('id', $exeuctionId)->first(); + if (is_null($execution)) { + $this->emit('error', 'Backup execution not found.'); + return; + } + $filename = data_get($execution, 'filename'); + $server = $execution->scheduledDatabaseBackup->database->destination->server; + $privateKeyLocation = savePrivateKeyToFs($server); + $disk = Storage::build([ + 'driver' => 'sftp', + 'host' => $server->ip, + 'port' => $server->port, + 'username' => $server->user, + 'privateKey' => $privateKeyLocation, + ]); + return $disk->download($filename); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } public function refreshBackupExecutions(): void { $this->executions = $this->backup->executions; diff --git a/composer.json b/composer.json index 217560b57..9937ee5b9 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "laravel/ui": "^4.2", "lcobucci/jwt": "^5.0.0", "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-sftp-v3": "^3.0", "livewire/livewire": "^v2.12.3", "lorisleiva/laravel-actions": "^2.7", "masmerise/livewire-toaster": "^1.2", diff --git a/composer.lock b/composer.lock index 9f9e8d658..62d4f27ff 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "de2c45be3f03d43430549d963778dc4a", + "content-hash": "21ed976753483557403be75318585442", "packages": [ { "name": "aws/aws-crt-php", @@ -2938,6 +2938,66 @@ ], "time": "2023-08-30T10:23:59+00:00" }, + { + "name": "league/flysystem-sftp-v3", + "version": "3.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-sftp-v3.git", + "reference": "1ba682def8e87fd7fa00883629553c0200d2e974" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/1ba682def8e87fd7fa00883629553c0200d2e974", + "reference": "1ba682def8e87fd7fa00883629553c0200d2e974", + "shasum": "" + }, + "require": { + "league/flysystem": "^3.0.14", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2", + "phpseclib/phpseclib": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\PhpseclibV3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "SFTP filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "sftp" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-sftp-v3/issues", + "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.16.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2023-08-30T10:25:05+00:00" + }, { "name": "league/mime-type-detection", "version": "1.13.0", diff --git a/config/sentry.php b/config/sentry.php index 32e27e081..16f184229 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.101', + 'release' => '4.0.0-beta.102', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index d54064579..004c77c3b 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Location: {{ data_get($execution, 'filename', 'N/A') }}
- - {{-- @if (data_get($execution, 'status') !== 'failed') --}} - {{-- Download --}} - {{-- @endif --}} + @if (data_get($execution, 'status') === 'success') + Download + @endif Delete
diff --git a/versions.json b/versions.json index 4c8e06b9d..cd5da1110 100644 --- a/versions.json +++ b/versions.json @@ -4,7 +4,7 @@ "version": "3.12.36" }, "v4": { - "version": "4.0.0-beta.101" + "version": "4.0.0-beta.102" } } } From 70ecb92e82c69aecbe912ef48d1aa7e3dea83884 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 09:41:41 +0200 Subject: [PATCH 22/29] cleanup ssh dir on start --- app/Console/Commands/Init.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index 6abac7029..31d7639be 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -10,6 +10,7 @@ use App\Models\StandaloneMongodb; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Storage; class Init extends Command { @@ -21,8 +22,23 @@ class Init extends Command ray()->clearAll(); $this->cleanup_in_progress_application_deployments(); $this->cleanup_stucked_resources(); + $this->cleanup_ssh(); } + private function cleanup_ssh() { + try { + $files = Storage::allFiles('ssh/keys'); + foreach ($files as $file) { + Storage::delete($file); + } + $files = Storage::allFiles('ssh/mux'); + foreach ($files as $file) { + Storage::delete($file); + } + } catch (\Throwable $e) { + echo "Error: {$e->getMessage()}\n"; + } + } private function cleanup_in_progress_application_deployments() { // Cleanup any failed deployments From aa02b8d4332a671bd2c430f11b85fa53254b84e7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 09:56:58 +0200 Subject: [PATCH 23/29] fix: rate limit for api + add mariadb + mysql --- app/Providers/RouteServiceProvider.php | 2 +- routes/api.php | 34 +++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index f60994c61..79b214502 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -48,7 +48,7 @@ class RouteServiceProvider extends ServiceProvider if ($request->path() === 'api/health') { return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip()); } - return Limit::perMinute(30)->by($request->user()?->id ?: $request->ip()); + return Limit::perMinute(200)->by($request->user()?->id ?: $request->ip()); }); RateLimiter::for('5', function (Request $request) { return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip()); diff --git a/routes/api.php b/routes/api.php index 77c000576..53ae1bd09 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,8 @@ query->get('uuid'); $force = $request->query->get('force') ?? false; - if (is_null($teamId)) { return response()->json(['error' => 'Invalid token.'], 400); } @@ -50,29 +51,56 @@ Route::group([ ); return response()->json(['message' => 'Deployment queued.'], 200); } else if ($type === 'App\Models\StandalonePostgresql') { + if (str($resource->status)->startsWith('running')) { + return response()->json(['message' => 'Database already running.'], 200); + } StartPostgresql::run($resource); $resource->update([ 'started_at' => now(), ]); return response()->json(['message' => 'Database started.'], 200); } else if ($type === 'App\Models\StandaloneRedis') { + if (str($resource->status)->startsWith('running')) { + return response()->json(['message' => 'Database already running.'], 200); + } StartRedis::run($resource); $resource->update([ 'started_at' => now(), ]); return response()->json(['message' => 'Database started.'], 200); } else if ($type === 'App\Models\StandaloneMongodb') { + if (str($resource->status)->startsWith('running')) { + return response()->json(['message' => 'Database already running.'], 200); + } StartMongodb::run($resource); $resource->update([ 'started_at' => now(), ]); return response()->json(['message' => 'Database started.'], 200); - }else if ($type === 'App\Models\Service') { + } else if ($type === 'App\Models\StandaloneMysql') { + if (str($resource->status)->startsWith('running')) { + return response()->json(['message' => 'Database already running.'], 200); + } + StartMysql::run($resource); + $resource->update([ + 'started_at' => now(), + ]); + return response()->json(['message' => 'Database started.'], 200); + } else if ($type === 'App\Models\StandaloneMariadb') { + if (str($resource->status)->startsWith('running')) { + return response()->json(['message' => 'Database already running.'], 200); + } + StartMariadb::run($resource); + $resource->update([ + 'started_at' => now(), + ]); + return response()->json(['message' => 'Database started.'], 200); + } else if ($type === 'App\Models\Service') { StartService::run($resource); return response()->json(['message' => 'Service started.'], 200); } } - return response()->json(['error' => 'No resource found.'], 404); + return response()->json(['error' => "No resource found with {$uuid}."], 404); }); }); From 379f4b9dff1310e165844fa356f989b293286d24 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 10:43:07 +0200 Subject: [PATCH 24/29] feat: show webhook on ui feat: n8n service --- app/Http/Livewire/Project/Shared/Webhooks.php | 19 ++++++++++ bootstrap/helpers/shared.php | 15 ++++++++ .../livewire/project/service/index.blade.php | 25 ++++++++---- .../project/shared/webhooks.blade.php | 10 +++++ .../application/configuration.blade.php | 6 +++ .../project/database/configuration.blade.php | 6 +++ routes/api.php | 2 +- templates/compose/appsmith.yaml | 2 + templates/compose/n8n-with-postgresql.yaml | 38 +++++++++++++++++++ templates/compose/n8n.yaml | 15 ++++++++ templates/service-templates.json | 30 ++++++++++++++- 11 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 app/Http/Livewire/Project/Shared/Webhooks.php create mode 100644 resources/views/livewire/project/shared/webhooks.blade.php create mode 100644 templates/compose/n8n-with-postgresql.yaml create mode 100644 templates/compose/n8n.yaml diff --git a/app/Http/Livewire/Project/Shared/Webhooks.php b/app/Http/Livewire/Project/Shared/Webhooks.php new file mode 100644 index 000000000..a943347b1 --- /dev/null +++ b/app/Http/Livewire/Project/Shared/Webhooks.php @@ -0,0 +1,19 @@ +deploywebhook = generateDeployWebhook($this->resource); + } + public function render() + { + return view('livewire.project.shared.webhooks'); + } +} diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 3348ce1ae..8e8b2ec3e 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -4,7 +4,9 @@ use App\Models\Application; use App\Models\InstanceSettings; use App\Models\Server; use App\Models\Service; +use App\Models\StandaloneMariadb; use App\Models\StandaloneMongodb; +use App\Models\StandaloneMysql; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; use App\Models\Team; @@ -484,5 +486,18 @@ function queryResourcesByUuid(string $uuid) if ($redis) return $redis; $mongodb = StandaloneMongodb::whereUuid($uuid)->first(); if ($mongodb) return $mongodb; + $mysql = StandaloneMysql::whereUuid($uuid)->first(); + if ($mysql) return $mysql; + $mariadb = StandaloneMariadb::whereUuid($uuid)->first(); + if ($mariadb) return $mariadb; return $resource; } + +function generateDeployWebhook($resource) { + $baseUrl = base_url(); + $api = Url::fromString($baseUrl) . '/api/v1'; + $endpoint = '/deploy'; + $uuid = data_get($resource, 'uuid'); + $url = $api . $endpoint . "?uuid=$uuid&force=false"; + return $url; +} diff --git a/resources/views/livewire/project/service/index.blade.php b/resources/views/livewire/project/service/index.blade.php index a966c56d4..933c89ecd 100644 --- a/resources/views/livewire/project/service/index.blade.php +++ b/resources/views/livewire/project/service/index.blade.php @@ -4,16 +4,25 @@
@@ -100,7 +109,9 @@ @foreach ($databases as $database) @endforeach - +
+
+
diff --git a/resources/views/livewire/project/shared/webhooks.blade.php b/resources/views/livewire/project/shared/webhooks.blade.php new file mode 100644 index 000000000..ba0461bdb --- /dev/null +++ b/resources/views/livewire/project/shared/webhooks.blade.php @@ -0,0 +1,10 @@ +
+
+

Webhooks

+ +
+
+ +
+
diff --git a/resources/views/project/application/configuration.blade.php b/resources/views/project/application/configuration.blade.php index 78f0a3061..d4766362f 100644 --- a/resources/views/project/application/configuration.blade.php +++ b/resources/views/project/application/configuration.blade.php @@ -19,6 +19,9 @@ Storages + Webhooks + @if ($application->git_based()) Preview @@ -57,6 +60,9 @@
+
+ +
diff --git a/resources/views/project/database/configuration.blade.php b/resources/views/project/database/configuration.blade.php index 3ad6e6df5..c65ef4301 100644 --- a/resources/views/project/database/configuration.blade.php +++ b/resources/views/project/database/configuration.blade.php @@ -31,6 +31,9 @@ window.location.hash = 'storages'" href="#">Storages
+ Webhooks +
+
+ +
diff --git a/routes/api.php b/routes/api.php index 53ae1bd09..ef233e37e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -97,7 +97,7 @@ Route::group([ return response()->json(['message' => 'Database started.'], 200); } else if ($type === 'App\Models\Service') { StartService::run($resource); - return response()->json(['message' => 'Service started.'], 200); + return response()->json(['message' => 'Service started. It could take a while, be patient.'], 200); } } return response()->json(['error' => "No resource found with {$uuid}."], 404); diff --git a/templates/compose/appsmith.yaml b/templates/compose/appsmith.yaml index a30f4c4e5..81ac5fe3d 100644 --- a/templates/compose/appsmith.yaml +++ b/templates/compose/appsmith.yaml @@ -14,3 +14,5 @@ services: - APPSMITH_SMART_LOOK_ID= volumes: - stacks-data:/appsmith-stacks + healthcheck: + test: ["NONE"] diff --git a/templates/compose/n8n-with-postgresql.yaml b/templates/compose/n8n-with-postgresql.yaml new file mode 100644 index 000000000..25955583d --- /dev/null +++ b/templates/compose/n8n-with-postgresql.yaml @@ -0,0 +1,38 @@ +# documentation: https://docs.n8n.io/hosting/ +# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model. +# tags: n8n,workflow,automation,open,source,low,code + +services: + n8n: + image: docker.n8n.io/n8nio/n8n + environment: + - SERVICE_FQDN_N8N + - N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N} + - N8N_HOST=${SERVICE_FQDN_N8N} + - GENERIC_TIMEZONE="Europe/Berlin" + - TZ="Europe/Berlin" + - DB_TYPE=postgresdb + - DB_POSTGRESDB_DATABASE=${POSTGRES_DB:-umami} + - DB_POSTGRESDB_HOST=postgresql + - DB_POSTGRESDB_PORT=5432 + - DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES + - DB_POSTGRESDB_SCHEMA=public + - DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES + volumes: + - n8n-data:/home/node/.n8n + depends_on: + - postgresql + postgresql: + image: postgres:15-alpine + volumes: + - postgresql-data:/var/lib/postgresql/data + environment: + - POSTGRES_USER=$SERVICE_USER_POSTGRES + - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES + - POSTGRES_DB=${POSTGRES_DB:-umami} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 10 + diff --git a/templates/compose/n8n.yaml b/templates/compose/n8n.yaml new file mode 100644 index 000000000..c8613cf03 --- /dev/null +++ b/templates/compose/n8n.yaml @@ -0,0 +1,15 @@ +# documentation: https://docs.n8n.io/hosting/ +# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model. +# tags: n8n,workflow,automation,open,source,low,code + +services: + n8n: + image: docker.n8n.io/n8nio/n8n + environment: + - SERVICE_FQDN_N8N + - N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N} + - N8N_HOST=${SERVICE_FQDN_N8N} + - GENERIC_TIMEZONE="Europe/Berlin" + - TZ="Europe/Berlin" + volumes: + - n8n-data:/home/node/.n8n diff --git a/templates/service-templates.json b/templates/service-templates.json index d5af2b37c..4c06f76c0 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -2,7 +2,7 @@ "appsmith": { "documentation": "https:\/\/docs.appsmith.com", "slogan": "Appsmith is an open-source, self-hosted application development platform that enables you to build powerful web applications with ease.", - "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycK", + "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gTk9ORQo=", "tags": [ "lowcode", "nocode", @@ -110,6 +110,34 @@ "api" ] }, + "n8n-with-postgresql": { + "documentation": "https:\/\/docs.n8n.io\/hosting\/", + "slogan": "n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.", + "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnR0VORVJJQ19USU1FWk9ORT0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gJ1RaPSJFdXJvcGUvQmVybGluIicKICAgICAgLSBEQl9UWVBFPXBvc3RncmVzZGIKICAgICAgLSAnREJfUE9TVEdSRVNEQl9EQVRBQkFTRT0ke1BPU1RHUkVTX0RCOi11bWFtaX0nCiAgICAgIC0gREJfUE9TVEdSRVNEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BPUlQ9NTQzMgogICAgICAtIERCX1BPU1RHUkVTREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gREJfUE9TVEdSRVNEQl9TQ0hFTUE9cHVibGljCiAgICAgIC0gREJfUE9TVEdSRVNEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXVtYW1pfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAo=", + "tags": [ + "n8n", + "workflow", + "automation", + "open", + "source", + "low", + "code" + ] + }, + "n8n": { + "documentation": "https:\/\/docs.n8n.io\/hosting\/", + "slogan": "n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.", + "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnR0VORVJJQ19USU1FWk9ORT0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gJ1RaPSJFdXJvcGUvQmVybGluIicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ244bi1kYXRhOi9ob21lL25vZGUvLm44bicK", + "tags": [ + "n8n", + "workflow", + "automation", + "open", + "source", + "low", + "code" + ] + }, "pairdrop": { "documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop\/blob\/master\/docs\/faq.md", "slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.", From 50fc05ab5243766f6f03ea939dc9f80ebeab1656 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 11:43:18 +0200 Subject: [PATCH 25/29] update init script --- app/Console/Commands/Init.php | 41 ++++++++++++++++++++++++++++------- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index 31d7639be..0efd9b025 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -7,6 +7,7 @@ use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\Service; use App\Models\StandaloneMongodb; +use App\Models\StandaloneMysql; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; use Illuminate\Console\Command; @@ -22,10 +23,11 @@ class Init extends Command ray()->clearAll(); $this->cleanup_in_progress_application_deployments(); $this->cleanup_stucked_resources(); - $this->cleanup_ssh(); + // $this->cleanup_ssh(); } - private function cleanup_ssh() { + private function cleanup_ssh() + { try { $files = Storage::allFiles('ssh/keys'); foreach ($files as $file) { @@ -53,11 +55,12 @@ class Init extends Command echo "Error: {$e->getMessage()}\n"; } } - private function cleanup_stucked_resources() { + private function cleanup_stucked_resources() + { // Cleanup any resources that are not attached to any environment or destination or server try { $applications = Application::all(); - foreach($applications as $application) { + foreach ($applications as $application) { if (!$application->environment) { ray('Application without environment', $application->name); $application->delete(); @@ -68,7 +71,7 @@ class Init extends Command } } $postgresqls = StandalonePostgresql::all(); - foreach($postgresqls as $postgresql) { + foreach ($postgresqls as $postgresql) { if (!$postgresql->environment) { ray('Postgresql without environment', $postgresql->name); $postgresql->delete(); @@ -79,7 +82,7 @@ class Init extends Command } } $redis = StandaloneRedis::all(); - foreach($redis as $redis) { + foreach ($redis as $redis) { if (!$redis->environment) { ray('Redis without environment', $redis->name); $redis->delete(); @@ -90,7 +93,7 @@ class Init extends Command } } $mongodbs = StandaloneMongodb::all(); - foreach($mongodbs as $mongodb) { + foreach ($mongodbs as $mongodb) { if (!$mongodb->environment) { ray('Mongodb without environment', $mongodb->name); $mongodb->delete(); @@ -100,8 +103,30 @@ class Init extends Command $mongodb->delete(); } } + $mysqls = StandaloneMysql::all(); + foreach ($mysqls as $mysql) { + if (!$mysql->environment) { + ray('Mysql without environment', $mysql->name); + $mysql->delete(); + } + if (!$mysql->destination()) { + ray('Mysql without destination', $mysql->name); + $mysql->delete(); + } + } + $mariadbs = StandaloneMysql::all(); + foreach ($mariadbs as $mariadb) { + if (!$mariadb->environment) { + ray('Mariadb without environment', $mariadb->name); + $mariadb->delete(); + } + if (!$mariadb->destination()) { + ray('Mariadb without destination', $mariadb->name); + $mariadb->delete(); + } + } $services = Service::all(); - foreach($services as $service) { + foreach ($services as $service) { if (!$service->environment) { ray('Service without environment', $service->name); $service->delete(); diff --git a/config/sentry.php b/config/sentry.php index 16f184229..b4f412e41 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.102', + 'release' => '4.0.0-beta.103', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 004c77c3b..14a39b0fa 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Wed, 25 Oct 2023 11:50:22 +0200 Subject: [PATCH 26/29] fix: server settings guarded --- app/Models/ServerSetting.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index 198600735..9235848ee 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -6,10 +6,7 @@ use Illuminate\Database\Eloquent\Model; class ServerSetting extends Model { - protected $fillable = [ - 'server_id', - 'is_usable', - ]; + protected $guarded = []; public function server() { From ead1edc2b98639b489c0eb79c8e7274273dbf870 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 15:44:34 +0200 Subject: [PATCH 27/29] Services --- templates/compose/dashboard.yaml | 1 + templates/compose/emby.yaml | 1 + templates/compose/embystat.yaml | 1 + templates/compose/grocy.yaml | 1 + templates/compose/pairdrop.yaml | 2 +- templates/compose/snapdrop.yaml | 2 +- templates/compose/wordpress-with-mariadb.yaml | 2 +- templates/compose/wordpress-with-mysql.yaml | 2 +- .../compose/wordpress-without-database.yaml | 2 +- templates/service-templates.json | 57 +++++++++++++++++-- 10 files changed, 61 insertions(+), 10 deletions(-) diff --git a/templates/compose/dashboard.yaml b/templates/compose/dashboard.yaml index eeca63a5b..ea2ae3489 100644 --- a/templates/compose/dashboard.yaml +++ b/templates/compose/dashboard.yaml @@ -1,5 +1,6 @@ # documentation: https://github.com/phntxx/dashboard/wiki/Installation#installation-using-docker # slogan: A dashboard. Inspired by SUI, it offers simple customization through JSON-files and a handy search bar to help you browse the internet more efficiently. +# tags: dashboard, web, search, bookmarks services: dashboard: diff --git a/templates/compose/emby.yaml b/templates/compose/emby.yaml index 2bb7b1538..b5e35b4d7 100644 --- a/templates/compose/emby.yaml +++ b/templates/compose/emby.yaml @@ -1,5 +1,6 @@ # documentation: https://emby.media/support/articles/Home.html # slogan: A media server software that allows you to organize, stream, and access your multimedia content effortlessly, making it easy to enjoy your favorite movies, TV shows, music, and more. +# tags: media, server, movies, tv, music services: emby: diff --git a/templates/compose/embystat.yaml b/templates/compose/embystat.yaml index ae0b20eea..c7dac0029 100644 --- a/templates/compose/embystat.yaml +++ b/templates/compose/embystat.yaml @@ -1,5 +1,6 @@ # documentation: https://github.com/mregni/EmbyStat/wiki/docker # slogan: EmyStat is an open-source, self-hosted web analytics tool, designed to provide insight into website traffic and user behavior, of your local Emby deployement, all within your control. +# tags: media, server, movies, tv, music services: embystat: diff --git a/templates/compose/grocy.yaml b/templates/compose/grocy.yaml index 3c980efb1..46edd984e 100644 --- a/templates/compose/grocy.yaml +++ b/templates/compose/grocy.yaml @@ -1,5 +1,6 @@ # documentation: https://github.com/grocy/grocy # slogan: Grocy is a self-hosted, web-based household management and grocery list application, designed to simplify your household chores and grocery shopping. +# tags: groceries, household, management, grocery, shopping services: grocy: diff --git a/templates/compose/pairdrop.yaml b/templates/compose/pairdrop.yaml index 924dae0d8..57e32afc0 100644 --- a/templates/compose/pairdrop.yaml +++ b/templates/compose/pairdrop.yaml @@ -1,4 +1,4 @@ -# documentation: https://github.com/schlagmichdoch/PairDrop/blob/master/docs/faq.md +# documentation: https://github.com/schlagmichdoch/PairDrop # slogan: Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork. # tags: file, sharing, collaboration, teamwork diff --git a/templates/compose/snapdrop.yaml b/templates/compose/snapdrop.yaml index 066fb2013..652eb1bbb 100644 --- a/templates/compose/snapdrop.yaml +++ b/templates/compose/snapdrop.yaml @@ -1,4 +1,4 @@ -# documentation: https://github.com/RobinLinus/snapdrop/blob/master/docs/faq.md +# documentation: https://github.com/RobinLinus/snapdrop # slogan: A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet. # tags: file, sharing, transfer, local, network, internet diff --git a/templates/compose/wordpress-with-mariadb.yaml b/templates/compose/wordpress-with-mariadb.yaml index 4d59daf32..b0205f952 100644 --- a/templates/compose/wordpress-with-mariadb.yaml +++ b/templates/compose/wordpress-with-mariadb.yaml @@ -1,5 +1,5 @@ # documentation: https://wordpress.org/documentation/ -# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app." +# slogan: WordPress with MariaDB. Wordpress is open source software you can use to create a beautiful website, blog, or app. # tags: cms, blog, content, management, mariadb services: diff --git a/templates/compose/wordpress-with-mysql.yaml b/templates/compose/wordpress-with-mysql.yaml index c264ecbf3..a64952150 100644 --- a/templates/compose/wordpress-with-mysql.yaml +++ b/templates/compose/wordpress-with-mysql.yaml @@ -1,5 +1,5 @@ # documentation: https://wordpress.org/documentation/ -# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app." +# slogan: WordPress with MySQL. Wordpress is open source software you can use to create a beautiful website, blog, or app. # tags: cms, blog, content, management, mysql services: diff --git a/templates/compose/wordpress-without-database.yaml b/templates/compose/wordpress-without-database.yaml index ddcefe90d..0891e6c11 100644 --- a/templates/compose/wordpress-without-database.yaml +++ b/templates/compose/wordpress-without-database.yaml @@ -1,5 +1,5 @@ # documentation: https://wordpress.org/documentation/ -# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app." +# slogan: WordPress with external database. Wordpress is open source software you can use to create a beautiful website, blog, or app. # tags: cms, blog, content, management services: diff --git a/templates/service-templates.json b/templates/service-templates.json index 4c06f76c0..8fd76c18e 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -44,6 +44,17 @@ "collaboration" ] }, + "dashboard": { + "documentation": "https:\/\/github.com\/phntxx\/dashboard\/wiki\/Installation#installation-using-docker", + "slogan": "A dashboard. Inspired by SUI, it offers simple customization through JSON-files and a handy search bar to help you browse the internet more efficiently.", + "compose": "c2VydmljZXM6CiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdwaG50eHgvZGFzaGJvYXJkOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9EQVNIQk9BUkQKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Rhc2hib2FyZC1kYXRhOi9hcHAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MDgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==", + "tags": [ + "dashboard", + "web", + "search", + "bookmarks" + ] + }, "dokuwiki": { "documentation": "https:\/\/www.dokuwiki.org\/faq", "slogan": "A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases with simplicity and flexibility.", @@ -55,6 +66,30 @@ "base" ] }, + "emby": { + "documentation": "https:\/\/emby.media\/support\/articles\/Home.html", + "slogan": "A media server software that allows you to organize, stream, and access your multimedia content effortlessly, making it easy to enjoy your favorite movies, TV shows, music, and more.", + "compose": "c2VydmljZXM6CiAgZW1ieToKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9lbWJ5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FTUJZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZW1ieS1jb25maWc6L2NvbmZpZycKICAgICAgLSAnZW1ieS10dnNob3dzOi90dnNob3dzJwogICAgICAtICdlbWJ5LW1vdmllczovbW92aWVzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwOTYnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK", + "tags": [ + "media", + "server", + "movies", + "tv", + "music" + ] + }, + "embystat": { + "documentation": "https:\/\/github.com\/mregni\/EmbyStat\/wiki\/docker", + "slogan": "EmyStat is an open-source, self-hosted web analytics tool, designed to provide insight into website traffic and user behavior, of your local Emby deployement, all within your control.", + "compose": "c2VydmljZXM6CiAgZW1ieXN0YXQ6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZW1ieXN0YXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0VNQllTVEFUCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZW1ieXN0YXQtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NjU1NScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=", + "tags": [ + "media", + "server", + "movies", + "tv", + "music" + ] + }, "fider": { "documentation": "https:\/\/fider.io\/doc", "slogan": "Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.", @@ -76,6 +111,18 @@ "system" ] }, + "grocy": { + "documentation": "https:\/\/github.com\/grocy\/grocy", + "slogan": "Grocy is a self-hosted, web-based household management and grocery list application, designed to simplify your household chores and grocery shopping.", + "compose": "c2VydmljZXM6CiAgZ3JvY3k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZ3JvY3k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dST0NZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZ3JvY3ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK", + "tags": [ + "groceries", + "household", + "management", + "grocery", + "shopping" + ] + }, "heimdall": { "documentation": "https:\/\/github.com\/linuxserver\/Heimdall", "slogan": "Heimdall is a self-hosted dashboard for managing and organizing your server applications, providing a centralized and efficient interface.", @@ -139,7 +186,7 @@ ] }, "pairdrop": { - "documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop\/blob\/master\/docs\/faq.md", + "documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop", "slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.", "compose": "c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIERFQlVHX01PREU9ZmFsc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==", "tags": [ @@ -150,7 +197,7 @@ ] }, "snapdrop": { - "documentation": "https:\/\/github.com\/RobinLinus\/snapdrop\/blob\/master\/docs\/faq.md", + "documentation": "https:\/\/github.com\/RobinLinus\/snapdrop", "slogan": "A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.", "compose": "c2VydmljZXM6CiAgc25hcGRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvc25hcGRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NOQVBEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnc25hcGRyb3AtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK", "tags": [ @@ -188,7 +235,7 @@ }, "wordpress-with-mariadb": { "documentation": "https:\/\/wordpress.org\/documentation\/", - "slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"", + "slogan": "WordPress with MariaDB. Wordpress is open source software you can use to create a beautiful website, blog, or app.", "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBtYXJpYWRiCiAgICAgIFdPUkRQUkVTU19EQl9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfTkFNRTogd29yZHByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==", "tags": [ "cms", @@ -200,7 +247,7 @@ }, "wordpress-with-mysql": { "documentation": "https:\/\/wordpress.org\/documentation\/", - "slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"", + "slogan": "WordPress with MySQL. Wordpress is open source software you can use to create a beautiful website, blog, or app.", "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBteXNxbAogICAgICBXT1JEUFJFU1NfREJfVVNFUjogJFNFUlZJQ0VfVVNFUl9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX1BBU1NXT1JEOiAkU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX05BTUU6IHdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBteXNxbAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==", "tags": [ "cms", @@ -212,7 +259,7 @@ }, "wordpress-without-database": { "documentation": "https:\/\/wordpress.org\/documentation\/", - "slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"", + "slogan": "WordPress with external database. Wordpress is open source software you can use to create a beautiful website, blog, or app.", "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCg==", "tags": [ "cms", From 6e98fd94039696e78ff8564b19fe59ffeecbe305 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 20:13:45 +0200 Subject: [PATCH 28/29] grafana + openblocks --- .../compose/grafana-with-postgresql.yaml | 40 +++++++++++++++++++ templates/compose/grafana.yaml | 19 +++++++++ templates/compose/openblocks.yaml | 27 +++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 templates/compose/grafana-with-postgresql.yaml create mode 100644 templates/compose/grafana.yaml create mode 100644 templates/compose/openblocks.yaml diff --git a/templates/compose/grafana-with-postgresql.yaml b/templates/compose/grafana-with-postgresql.yaml new file mode 100644 index 000000000..78c44db32 --- /dev/null +++ b/templates/compose/grafana-with-postgresql.yaml @@ -0,0 +1,40 @@ +# documentation: https://grafana.com/docs/grafana/latest/installation/docker/ +# slogan: Grafana is the open source analytics & monitoring solution for every database. +# tags: grafana,analytics,monitoring,dashboard + +services: + grafana: + image: grafana/grafana-oss + environment: + - SERVICE_FQDN_GRAFANA + - GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA} + - GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA} + - GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA} + - GF_DATABASE_TYPE=postgres + - GF_DATABASE_HOST=postgresql + - GF_DATABASE_USER=$SERVICE_USER_POSTGRES + - GF_DATABASE_PASSWORD=$SERVICE_PASSWORD_POSTGRES + - GF_DATABASE_NAME=${POSTGRES_DB:-grafana} + volumes: + - grafana-data:/var/lib/grafana + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + interval: 5s + timeout: 5s + retries: 10 + depends_on: + - postgresql + postgresql: + image: postgres:15-alpine + volumes: + - postgresql-data:/var/lib/postgresql/data + environment: + - POSTGRES_USER=$SERVICE_USER_POSTGRES + - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES + - POSTGRES_DB=${POSTGRES_DB:-grafana} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 10 + diff --git a/templates/compose/grafana.yaml b/templates/compose/grafana.yaml new file mode 100644 index 000000000..eb39909be --- /dev/null +++ b/templates/compose/grafana.yaml @@ -0,0 +1,19 @@ +# documentation: https://grafana.com/docs/grafana/latest/installation/docker/ +# slogan: Grafana is the open source analytics & monitoring solution for every database. +# tags: grafana,analytics,monitoring,dashboard + +services: + grafana: + image: grafana/grafana-oss + environment: + - SERVICE_FQDN_GRAFANA + - GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA} + - GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA} + - GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA} + volumes: + - grafana-data:/var/lib/grafana + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + interval: 5s + timeout: 5s + retries: 10 diff --git a/templates/compose/openblocks.yaml b/templates/compose/openblocks.yaml new file mode 100644 index 000000000..dda798628 --- /dev/null +++ b/templates/compose/openblocks.yaml @@ -0,0 +1,27 @@ +# documentation: https://docs.openblocks.dev/self-hosting +# slogan: OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools. +# tags: openblocks,low,code,platform,open,source,low,code + +services: + openblocks: + image: openblocksdev/openblocks-ce + environment: + - SERVICE_FQDN_OPENBLOCKS + - REDIS_ENABLED=true + - MONGODB_ENABLED=true + - API_SERVICE_ENABLED=true + - NODE_SERVICE_ENABLED=true + - PUID=1000 + - PGID=1000 + - MONGODB_URI=mongodb://localhost:27017/openblocks?authSource=admin + - REDIS_URL=redis://localhost:6379 + - JS_EXECUTOR_URI=http://localhost:6060 + - ENABLE_USER_SIGN_UP=${ENABLE_USER_SIGN_UP:-true} + - ENCRYPTION_PASSWORD=$SERVICE_ + volumes: + - openblocks-data:/openblocks-stacks + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + interval: 5s + timeout: 5s + retries: 10 From 21795cf788341ee8e993ff12b5d852efbdaf7fcd Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 25 Oct 2023 20:19:38 +0200 Subject: [PATCH 29/29] fix: space in build args --- app/Jobs/ApplicationDeploymentJob.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 3091bc339..8b590dd2b 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -885,14 +885,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); private function generate_build_env_variables() { - $this->build_args = collect(["--build-arg SOURCE_COMMIT={$this->commit}"]); + $this->build_args = collect(["--build-arg SOURCE_COMMIT=\"{$this->commit}\""]); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - $this->build_args->push("--build-arg {$env->key}={$env->value}"); + $this->build_args->push("--build-arg {$env->key}=\"{$env->value}\""); } } else { foreach ($this->application->build_environment_variables_preview as $env) { - $this->build_args->push("--build-arg {$env->key}={$env->value}"); + $this->build_args->push("--build-arg {$env->key}=\"{$env->value}\""); } }