From 77a01798226a2636ebe3264b2c42a71d69e8ca58 Mon Sep 17 00:00:00 2001
From: Stuart Rowlands
Date: Thu, 8 Feb 2024 19:27:43 +1000
Subject: [PATCH 01/97] Added basic support for post-deployment commands.
---
app/Jobs/ApplicationDeploymentJob.php | 25 ++++++++++++++++
app/Livewire/Project/Application/General.php | 2 ++
...23_add_post_deployment_to_applications.php | 30 +++++++++++++++++++
.../project/application/general.blade.php | 8 +++++
4 files changed, 65 insertions(+)
create mode 100644 database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 9d0e069ce..ca19a3b23 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -257,6 +257,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
}
}
+ $this->run_post_deployment_command();
$this->application->isConfigurationChanged(true);
} catch (Exception $e) {
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
@@ -1515,6 +1516,30 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
]);
}
+ private function run_post_deployment_command()
+ {
+ if (empty($this->application->post_deployment_command)) {
+ return;
+ }
+ $this->application_deployment_queue->addLogEntry("Executing post deployment command: {$this->application->post_deployment_command}");
+
+ $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
+ foreach ($containers as $container) {
+ $containerName = data_get($container, 'Names');
+ if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container. '-' . $this->application->uuid)) {
+ $cmd = 'sh -c "' . str_replace('"', '\"', $this->application->post_deployment_command) . '"';
+ $exec = "docker exec {$containerName} {$cmd}";
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, $exec), 'hidden' => true
+ ],
+ );
+ return;
+ }
+ }
+ throw new RuntimeException('Post deployment command: Could not find a valid container. Is the container name correct?');
+ }
+
private function next(string $status)
{
queue_next_deployment($this->application);
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 0f6d61957..277ec23d3 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -66,6 +66,8 @@ class General extends Component
'application.docker_compose_custom_build_command' => 'nullable',
'application.custom_labels' => 'nullable',
'application.custom_docker_run_options' => 'nullable',
+ 'application.post_deployment_command' => 'nullable',
+ 'application.post_deployment_command_container' => 'nullable',
'application.settings.is_static' => 'boolean|required',
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
'application.settings.is_build_server_enabled' => 'boolean|required',
diff --git a/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php
new file mode 100644
index 000000000..ccc86684a
--- /dev/null
+++ b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php
@@ -0,0 +1,30 @@
+string('post_deployment_command')->nullable();
+ $table->string('post_deployment_command_container')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('applications', function (Blueprint $table) {
+ $table->dropColumn('post_deployment_command');
+ $table->dropColumn('post_deployment_command_container');
+ });
+ }
+};
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php
index 3e5e4f137..189265834 100644
--- a/resources/views/livewire/project/application/general.blade.php
+++ b/resources/views/livewire/project/application/general.blade.php
@@ -236,6 +236,14 @@
Reset to Coolify Generated Labels
@endif
+
+ Deployment scripts
+
+
+
+
From 0538c2f4789b4615919c12ae1e705f750e978604 Mon Sep 17 00:00:00 2001
From: Stuart Rowlands
Date: Thu, 8 Feb 2024 20:02:30 +1000
Subject: [PATCH 02/97] Added pre-deployment support.
---
app/Jobs/ApplicationDeploymentJob.php | 29 ++++++++++++++++++-
app/Livewire/Project/Application/General.php | 2 ++
...23_add_post_deployment_to_applications.php | 4 +++
.../project/application/general.blade.php | 8 ++++-
4 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index ca19a3b23..3532f5c84 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -783,8 +783,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
[
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}")
],
-
);
+ $this->run_pre_deployment_command();
}
private function deploy_to_additional_destinations()
{
@@ -1516,6 +1516,33 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
]);
}
+ private function run_pre_deployment_command()
+ {
+ if (empty($this->application->pre_deployment_command)) {
+ return;
+ }
+ $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
+ if ($containers->count() == 0) {
+ return;
+ }
+ $this->application_deployment_queue->addLogEntry("Executing pre deployment command: {$this->application->post_deployment_command}");
+
+ foreach ($containers as $container) {
+ $containerName = data_get($container, 'Names');
+ if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container. '-' . $this->application->uuid)) {
+ $cmd = 'sh -c "' . str_replace('"', '\"', $this->application->pre_deployment_command) . '"';
+ $exec = "docker exec {$containerName} {$cmd}";
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, $exec), 'hidden' => true
+ ],
+ );
+ return;
+ }
+ }
+ throw new RuntimeException('Pre deployment command: Could not find a valid container. Is the container name correct?');
+ }
+
private function run_post_deployment_command()
{
if (empty($this->application->post_deployment_command)) {
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 277ec23d3..f018e3b9d 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -66,6 +66,8 @@ class General extends Component
'application.docker_compose_custom_build_command' => 'nullable',
'application.custom_labels' => 'nullable',
'application.custom_docker_run_options' => 'nullable',
+ 'application.pre_deployment_command' => 'nullable',
+ 'application.pre_deployment_command_container' => 'nullable',
'application.post_deployment_command' => 'nullable',
'application.post_deployment_command_container' => 'nullable',
'application.settings.is_static' => 'boolean|required',
diff --git a/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php
index ccc86684a..6d3a74896 100644
--- a/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php
+++ b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php
@@ -14,6 +14,8 @@ return new class extends Migration
Schema::table('applications', function (Blueprint $table) {
$table->string('post_deployment_command')->nullable();
$table->string('post_deployment_command_container')->nullable();
+ $table->string('pre_deployment_command')->nullable();
+ $table->string('pre_deployment_command_container')->nullable();
});
}
@@ -25,6 +27,8 @@ return new class extends Migration
Schema::table('applications', function (Blueprint $table) {
$table->dropColumn('post_deployment_command');
$table->dropColumn('post_deployment_command_container');
+ $table->dropColumn('pre_deployment_command');
+ $table->dropColumn('pre_deployment_command_container');
});
}
};
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php
index 189265834..f17f80af7 100644
--- a/resources/views/livewire/project/application/general.blade.php
+++ b/resources/views/livewire/project/application/general.blade.php
@@ -238,9 +238,15 @@
@endif
Deployment scripts
+
+
+
+
+ helper="An optional script or command to execute in the newly built container after the deployment completes." />
From 365850d922d2ffa3d61123e585ebf95f0c35ce28 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 6 Mar 2024 10:57:39 +0100
Subject: [PATCH 03/97] Update version numbers + add next-image-transformation
service
---
config/sentry.php | 2 +-
config/version.php | 2 +-
.../compose/next-image-transformation.yaml | 28 +++++++++++++++++++
templates/service-templates.json | 13 +++++++++
versions.json | 2 +-
5 files changed, 44 insertions(+), 3 deletions(-)
create mode 100644 templates/compose/next-image-transformation.yaml
diff --git a/config/sentry.php b/config/sentry.php
index 9414c5afe..74eb9ba6a 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.236',
+ 'release' => '4.0.0-beta.237',
// 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 e47e67853..73a72d6ad 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Wed, 6 Mar 2024 10:58:08 +0100
Subject: [PATCH 04/97] Update slogans and healthcheck command in
next-image-transformation.yaml and service-templates.json
---
templates/compose/next-image-transformation.yaml | 4 ++--
templates/service-templates.json | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/templates/compose/next-image-transformation.yaml b/templates/compose/next-image-transformation.yaml
index 3e99245ee..5cefb7b45 100644
--- a/templates/compose/next-image-transformation.yaml
+++ b/templates/compose/next-image-transformation.yaml
@@ -1,5 +1,5 @@
# documentation: https://github.com/coollabsio/next-image-transformation
-# slogan: Self-hosted Next.js Image Transformation Service
+# slogan: Drop-in replacement for Vercel's Nextjs image optimization service.
# tags: nextjs,image,transformation,service
services:
@@ -22,7 +22,7 @@ services:
- IMGPROXY_JPEG_PROGRESSIVE=true
- IMGPROXY_USE_ETAG=true
healthcheck:
- test: [ "CMD", "imgproxy", "health" ]
+ test: ["CMD", "imgproxy", "health"]
interval: 2s
timeout: 10s
retries: 5
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 47efa21e5..2c0ee7754 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -514,7 +514,7 @@
},
"next-image-transformation": {
"documentation": "https:\/\/github.com\/coollabsio\/next-image-transformation",
- "slogan": "Self-hosted Next.js Image Transformation Service",
+ "slogan": "Drop-in replacement for Vercel's Nextjs image optimization service.",
"compose": "c2VydmljZXM6CiAgbmV4dC1pbWFnZS10cmFuc2Zvcm1hdGlvbjoKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL25leHQtaW1hZ2UtdHJhbnNmb3JtYXRpb246bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1RSQU5TRk9STUFUSU9OCiAgICAgIC0gTk9ERV9FTlY9cHJvZHVjdGlvbgogICAgICAtICdBTExPV0VEX1JFTU9URV9ET01BSU5TPSR7QUxMT1dFRF9SRU1PVEVfRE9NQUlOUzotKn0nCiAgICAgIC0gJ0lNR1BST1hZX1VSTD0ke0lNR1BST1hZX1VSTDotaHR0cDovL2ltZ3Byb3h5OjgwODB9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC1xTy0gaHR0cDovL2xvY2FsaG9zdDozMDAwL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogIGltZ3Byb3h5OgogICAgaW1hZ2U6IGRhcnRoc2ltL2ltZ3Byb3h5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBJTUdQUk9YWV9FTkFCTEVfV0VCUF9ERVRFQ1RJT049dHJ1ZQogICAgICAtIElNR1BST1hZX0pQRUdfUFJPR1JFU1NJVkU9dHJ1ZQogICAgICAtIElNR1BST1hZX1VTRV9FVEFHPXRydWUKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBpbWdwcm94eQogICAgICAgIC0gaGVhbHRoCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQo=",
"tags": [
"nextjs",
From e699103d3e5fdaad5f9f3d4a8814aa807949b734 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 6 Mar 2024 15:34:21 +0100
Subject: [PATCH 05/97] Update application names and base directories
---
database/seeders/ApplicationSeeder.php | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php
index 276c5d53c..34a54c8eb 100644
--- a/database/seeders/ApplicationSeeder.php
+++ b/database/seeders/ApplicationSeeder.php
@@ -15,12 +15,12 @@ class ApplicationSeeder extends Seeder
public function run(): void
{
Application::create([
- 'name' => 'coollabsio/coolify-examples:nodejs-fastify',
- 'description' => 'NodeJS Fastify Example',
+ 'name' => 'NodeJS Fastify Example',
'fqdn' => 'http://nodejs.127.0.0.1.sslip.io',
'repository_project_id' => 603035348,
'git_repository' => 'coollabsio/coolify-examples',
- 'git_branch' => 'nodejs-fastify',
+ 'git_branch' => 'main',
+ 'base_directory' => '/nodejs',
'build_pack' => 'nixpacks',
'ports_exposes' => '3000',
'environment_id' => 1,
@@ -30,12 +30,12 @@ class ApplicationSeeder extends Seeder
'source_type' => GithubApp::class
]);
Application::create([
- 'name' => 'coollabsio/coolify-examples:dockerfile',
- 'description' => 'Dockerfile Example',
+ 'name' => 'Dockerfile Example',
'fqdn' => 'http://dockerfile.127.0.0.1.sslip.io',
'repository_project_id' => 603035348,
'git_repository' => 'coollabsio/coolify-examples',
- 'git_branch' => 'dockerfile',
+ 'git_branch' => 'main',
+ 'base_directory' => '/dockerfile',
'build_pack' => 'dockerfile',
'ports_exposes' => '80',
'environment_id' => 1,
@@ -45,8 +45,7 @@ class ApplicationSeeder extends Seeder
'source_type' => GithubApp::class
]);
Application::create([
- 'name' => 'pure-dockerfile',
- 'description' => 'Pure Dockerfile Example',
+ 'name' => 'Pure Dockerfile Example',
'fqdn' => 'http://pure-dockerfile.127.0.0.1.sslip.io',
'git_repository' => 'coollabsio/coolify',
'git_branch' => 'main',
From f1b00436aae35bd511f87f1202e611055a8f47de Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 09:56:09 +0100
Subject: [PATCH 06/97] Update link target in stack-form.blade.php
---
resources/views/livewire/project/service/stack-form.blade.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/resources/views/livewire/project/service/stack-form.blade.php b/resources/views/livewire/project/service/stack-form.blade.php
index b9593087b..8e4535d90 100644
--- a/resources/views/livewire/project/service/stack-form.blade.php
+++ b/resources/views/livewire/project/service/stack-form.blade.php
@@ -16,7 +16,7 @@
+ helper="By default, you do not reach the Coolify defined networks.
Starting a docker compose based resource will have an internal network.
If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.
For more information, check
this ." />
@if ($fields)
From a6669ed87634bc326994f684f3a3e91960441f4f Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 09:59:19 +0100
Subject: [PATCH 07/97] Update version and add check for Docker installed via
snap
---
scripts/install.sh | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/scripts/install.sh b/scripts/install.sh
index 79e71596a..ad2a8de22 100644
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -6,7 +6,7 @@ set -e # Exit immediately if a command exits with a non-zero status
#set -u # Treat unset variables as an error and exit
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
-VERSION="1.2.2"
+VERSION="1.2.3"
DOCKER_VERSION="24.0"
CDN="https://cdn.coollabs.io/coolify"
@@ -122,6 +122,16 @@ if [ "$SSH_PERMIT_ROOT_LOGIN" != "true" ]; then
echo "###############################################################################"
fi
+# Detect if docker is installed via snap
+if [ -x "$(command -v snap)" ]; then
+ if snap list | grep -q docker; then
+ echo "Docker is installed via snap."
+ echo "Please note that Coolify does not support Docker installed via snap."
+ echo "Please remove Docker with snap (snap remove docker) and reexecute this script."
+ exit 1
+ fi
+fi
+
if ! [ -x "$(command -v docker)" ]; then
if [ "$OS_TYPE" == 'almalinux' ]; then
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
From 051a1405e7150dcb5ef9523b42bafb226f5434b6 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 10:27:21 +0100
Subject: [PATCH 08/97] Refactor backup execution and cleanup functionality
---
app/Livewire/Project/Database/Backup/Execution.php | 8 +++++++-
app/Livewire/Project/Database/BackupExecutions.php | 4 ++--
.../livewire/project/database/backup/execution.blade.php | 5 ++++-
3 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/app/Livewire/Project/Database/Backup/Execution.php b/app/Livewire/Project/Database/Backup/Execution.php
index 07f7db03c..1f790d643 100644
--- a/app/Livewire/Project/Database/Backup/Execution.php
+++ b/app/Livewire/Project/Database/Backup/Execution.php
@@ -10,7 +10,8 @@ class Execution extends Component
public $backup;
public $executions;
public $s3s;
- public function mount() {
+ public function mount()
+ {
$backup_uuid = request()->route('backup_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
@@ -34,6 +35,11 @@ class Execution extends Component
$this->executions = $executions;
$this->s3s = currentTeam()->s3s;
}
+ public function cleanupFailed()
+ {
+ $this->backup->executions()->where('status', 'failed')->delete();
+ $this->dispatch('refreshBackupExecutions');
+ }
public function render()
{
return view('livewire.project.database.backup.execution');
diff --git a/app/Livewire/Project/Database/BackupExecutions.php b/app/Livewire/Project/Database/BackupExecutions.php
index c78a8cbee..5484dfdc8 100644
--- a/app/Livewire/Project/Database/BackupExecutions.php
+++ b/app/Livewire/Project/Database/BackupExecutions.php
@@ -34,7 +34,7 @@ class BackupExecutions extends Component
}
$execution->delete();
$this->dispatch('success', 'Backup deleted.');
- $this->dispatch('refreshBackupExecutions');
+ $this->refreshBackupExecutions();
}
public function download($exeuctionId)
{
@@ -65,6 +65,6 @@ class BackupExecutions extends Component
}
public function refreshBackupExecutions(): void
{
- $this->executions = data_get($this->backup, 'executions', []);
+ $this->executions = $this->backup->executions()->get()->sortByDesc('created_at');
}
}
diff --git a/resources/views/livewire/project/database/backup/execution.blade.php b/resources/views/livewire/project/database/backup/execution.blade.php
index 437186b60..f63ed2be7 100644
--- a/resources/views/livewire/project/database/backup/execution.blade.php
+++ b/resources/views/livewire/project/database/backup/execution.blade.php
@@ -13,7 +13,10 @@
-
Executions
+
+
Executions
+ Cleanup Failed Backups
+
From 7a21312daf04522b6a1c2d0d1b7b6461b77b5e17 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 11:14:03 +0100
Subject: [PATCH 09/97] feat: domains api endpoint
---
app/Http/Controllers/Api/Domains.php | 55 ++++++++++++++++++++++++++++
app/Models/Project.php | 4 ++
routes/api.php | 2 +
3 files changed, 61 insertions(+)
create mode 100644 app/Http/Controllers/Api/Domains.php
diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php
new file mode 100644
index 000000000..080f5aa4f
--- /dev/null
+++ b/app/Http/Controllers/Api/Domains.php
@@ -0,0 +1,55 @@
+json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $projects = ModelsProject::where('team_id', $teamId)->get();
+ $domains = collect();
+ $applications = $projects->pluck('applications')->flatten();
+ if ($applications->count() > 0) {
+ foreach ($applications as $application) {
+ $ip = $application->destination->server->ip;
+ $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
+ return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
+ });
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
+ }
+ }
+ $services = $projects->pluck('services')->flatten();
+ if ($services->count() > 0) {
+ foreach ($services as $service) {
+ $service_applications = $service->applications;
+ if ($service_applications->count() > 0) {
+ foreach ($service_applications as $application) {
+ $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
+ return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
+ });
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
+ }
+ }
+ }
+ }
+ $domains = $domains->groupBy('ip')->map(function ($domain) {
+ return $domain->pluck('domain')->flatten();
+ });
+
+ return response()->json($domains);
+ }
+}
diff --git a/app/Models/Project.php b/app/Models/Project.php
index b9afc7426..6416971f6 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -45,6 +45,10 @@ class Project extends BaseModel
return $this->belongsTo(Team::class);
}
+ public function services()
+ {
+ return $this->hasManyThrough(Service::class, Environment::class);
+ }
public function applications()
{
return $this->hasManyThrough(Application::class, Environment::class);
diff --git a/routes/api.php b/routes/api.php
index 4ef5500f8..a1ddd666e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1,6 +1,7 @@
Date: Thu, 7 Mar 2024 11:25:15 +0100
Subject: [PATCH 10/97] Refactor domain grouping in domains API controller
---
app/Http/Controllers/Api/Domains.php | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php
index 080f5aa4f..8151a6c87 100644
--- a/app/Http/Controllers/Api/Domains.php
+++ b/app/Http/Controllers/Api/Domains.php
@@ -48,7 +48,12 @@ class Domains extends Controller
}
$domains = $domains->groupBy('ip')->map(function ($domain) {
return $domain->pluck('domain')->flatten();
- });
+ })->map(function ($domain, $ip) {
+ return [
+ 'ip' => $ip,
+ 'domains' => $domain,
+ ];
+ })->values();
return response()->json($domains);
}
From c7693d0ec34de1e942cacfa5b7ab5d87a430af38 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 11:35:00 +0100
Subject: [PATCH 11/97] feat: resources api endpoint
---
app/Http/Controllers/Api/Resources.php | 30 ++++++++++++++++++++++++++
app/Models/Project.php | 6 ++++--
routes/api.php | 8 ++++---
3 files changed, 39 insertions(+), 5 deletions(-)
create mode 100644 app/Http/Controllers/Api/Resources.php
diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php
new file mode 100644
index 000000000..a6e25758c
--- /dev/null
+++ b/app/Http/Controllers/Api/Resources.php
@@ -0,0 +1,30 @@
+json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $projects = Project::where('team_id', $teamId)->get();
+ $resources = collect();
+ $resources->push($projects->pluck('applications')->flatten());
+ $resources->push($projects->pluck('services')->flatten());
+ $resources->push($projects->pluck('postgresqls')->flatten());
+ $resources->push($projects->pluck('redis')->flatten());
+ $resources->push($projects->pluck('mongodbs')->flatten());
+ $resources->push($projects->pluck('mysqls')->flatten());
+ $resources->push($projects->pluck('mariadbs')->flatten());
+ $resources = $resources->flatten();
+ return response()->json($resources);
+ }
+
+}
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 6416971f6..27ae10778 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -27,7 +27,8 @@ class Project extends BaseModel
$project->settings()->delete();
});
}
- public function environment_variables() {
+ public function environment_variables()
+ {
return $this->hasMany(SharedEnvironmentVariable::class);
}
public function environments()
@@ -74,7 +75,8 @@ class Project extends BaseModel
{
return $this->hasManyThrough(StandaloneMariadb::class, Environment::class);
}
- public function resource_count() {
+ public function resource_count()
+ {
return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count();
}
}
diff --git a/routes/api.php b/routes/api.php
index a1ddd666e..ae60e5b7b 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -3,6 +3,7 @@
use App\Http\Controllers\Api\Deploy;
use App\Http\Controllers\Api\Domains;
use App\Http\Controllers\Api\Project;
+use App\Http\Controllers\Api\Resources;
use App\Http\Controllers\Api\Server;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
@@ -32,10 +33,11 @@ Route::group([
Route::get('/deploy', [Deploy::class, 'deploy']);
Route::get('/servers', [Server::class, 'servers']);
Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']);
- Route::get('/projects', [Project::class, 'projects']);
- Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']);
- Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']);
+ Route::get('/resources', [Resources::class, 'resources']);
Route::get('/domains', [Domains::class, 'domains']);
+ //Route::get('/projects', [Project::class, 'projects']);
+ //Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']);
+ //Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']);
});
Route::get('/{any}', function () {
From db24828a5a1dae83a94c671974251b3b13aecbbe Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 11:37:56 +0100
Subject: [PATCH 12/97] Refactor resource retrieval in API controller
---
app/Http/Controllers/Api/Resources.php | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php
index a6e25758c..78ea854c7 100644
--- a/app/Http/Controllers/Api/Resources.php
+++ b/app/Http/Controllers/Api/Resources.php
@@ -18,11 +18,9 @@ class Resources extends Controller
$resources = collect();
$resources->push($projects->pluck('applications')->flatten());
$resources->push($projects->pluck('services')->flatten());
- $resources->push($projects->pluck('postgresqls')->flatten());
- $resources->push($projects->pluck('redis')->flatten());
- $resources->push($projects->pluck('mongodbs')->flatten());
- $resources->push($projects->pluck('mysqls')->flatten());
- $resources->push($projects->pluck('mariadbs')->flatten());
+ foreach (collect(DATABASE_TYPES) as $db) {
+ $resources->push($projects->pluck(str($db)->plural(2))->flatten());
+ }
$resources = $resources->flatten();
return response()->json($resources);
}
From 038f65aae6980c893c0d642b59fee2b276925ba6 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 11:42:16 +0100
Subject: [PATCH 13/97] Add InstanceSettings model and update IP handling in
domains controller
---
app/Http/Controllers/Api/Domains.php | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php
index 8151a6c87..f96765397 100644
--- a/app/Http/Controllers/Api/Domains.php
+++ b/app/Http/Controllers/Api/Domains.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
+use App\Models\InstanceSettings;
use App\Models\Project as ModelsProject;
use Illuminate\Http\Request;
@@ -17,9 +18,13 @@ class Domains extends Controller
$projects = ModelsProject::where('team_id', $teamId)->get();
$domains = collect();
$applications = $projects->pluck('applications')->flatten();
+ $settings = InstanceSettings::get();
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;
+ if ($ip === 'host.docker.internal') {
+ $ip = $settings->ipv4 || $settings->ipv6 || 'host.docker.internal';
+ }
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
From 7aa8c765f6563b781db0baa22bdbf39fcee1931c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 11:49:15 +0100
Subject: [PATCH 14/97] Refactor domain IP handling in Domains controller
---
app/Http/Controllers/Api/Domains.php | 65 +++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 11 deletions(-)
diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php
index f96765397..4b8a026fb 100644
--- a/app/Http/Controllers/Api/Domains.php
+++ b/app/Http/Controllers/Api/Domains.php
@@ -22,16 +22,36 @@ class Domains extends Controller
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;
- if ($ip === 'host.docker.internal') {
- $ip = $settings->ipv4 || $settings->ipv6 || 'host.docker.internal';
- }
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
- $domains->push([
- 'domain' => $fqdn,
- 'ip' => $ip,
- ]);
+ if ($ip === 'host.docker.internal') {
+ if ($settings->public_ipv4) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $settings->public_ipv4,
+ ]);
+ }
+ if ($settings->public_ipv6) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $settings->public_ipv6,
+ ]);
+ }
+ if (!$settings->public_ipv4 && !$settings->public_ipv6) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
+ }
+ } else {
+ if (!$settings->public_ipv4 && !$settings->public_ipv6) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
+ }
+ }
}
}
$services = $projects->pluck('services')->flatten();
@@ -43,10 +63,33 @@ class Domains extends Controller
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
- $domains->push([
- 'domain' => $fqdn,
- 'ip' => $ip,
- ]);
+ if ($ip === 'host.docker.internal') {
+ if ($settings->public_ipv4) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $settings->public_ipv4,
+ ]);
+ }
+ if ($settings->public_ipv6) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $settings->public_ipv6,
+ ]);
+ }
+ if (!$settings->public_ipv4 && !$settings->public_ipv6) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
+ }
+ } else {
+ if (!$settings->public_ipv4 && !$settings->public_ipv6) {
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
+ }
+ }
}
}
}
From 2a03b452d3d0fc90f5708470ff1548dc92928e02 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 12:01:21 +0100
Subject: [PATCH 15/97] feat: team api endpoint
---
app/Http/Controllers/Api/Team.php | 64 +++++++++++++++++++++++++++++++
routes/api.php | 8 ++++
2 files changed, 72 insertions(+)
create mode 100644 app/Http/Controllers/Api/Team.php
diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php
new file mode 100644
index 000000000..d99ea8ea7
--- /dev/null
+++ b/app/Http/Controllers/Api/Team.php
@@ -0,0 +1,64 @@
+json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $teams = auth()->user()->teams;
+ return response()->json($teams);
+ }
+ public function team_by_id(Request $request)
+ {
+ $id = $request->id;
+ $teamId = get_team_id_from_token();
+ if (is_null($teamId)) {
+ return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $teams = auth()->user()->teams;
+ $team = $teams->where('id', $id)->first();
+ if (is_null($team)) {
+ return response()->json(['error' => 'Team not found.'], 404);
+ }
+ return response()->json($team);
+ }
+ public function members_by_id(Request $request)
+ {
+ $id = $request->id;
+ $teamId = get_team_id_from_token();
+ if (is_null($teamId)) {
+ return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $teams = auth()->user()->teams;
+ $team = $teams->where('id', $id)->first();
+ if (is_null($team)) {
+ return response()->json(['error' => 'Team not found.'], 404);
+ }
+ return response()->json($team->members);
+ }
+ public function current_team(Request $request)
+ {
+ $teamId = get_team_id_from_token();
+ if (is_null($teamId)) {
+ return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $team = auth()->user()->currentTeam();
+ return response()->json($team);
+ }
+ public function current_team_members(Request $request) {
+ $teamId = get_team_id_from_token();
+ if (is_null($teamId)) {
+ return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ }
+ $team = auth()->user()->currentTeam();
+ return response()->json($team->members);
+ }
+}
diff --git a/routes/api.php b/routes/api.php
index ae60e5b7b..b27d1fb28 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -5,6 +5,7 @@ use App\Http\Controllers\Api\Domains;
use App\Http\Controllers\Api\Project;
use App\Http\Controllers\Api\Resources;
use App\Http\Controllers\Api\Server;
+use App\Http\Controllers\Api\Team;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
@@ -35,6 +36,13 @@ Route::group([
Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']);
Route::get('/resources', [Resources::class, 'resources']);
Route::get('/domains', [Domains::class, 'domains']);
+ Route::get('/teams', [Team::class, 'teams']);
+ Route::get('/team/current', [Team::class, 'current_team']);
+ Route::get('/team/current/members', [Team::class, 'current_team_members']);
+ Route::get('/team/{id}', [Team::class, 'team_by_id']);
+ Route::get('/team/{id}/members', [Team::class, 'members_by_id']);
+
+
//Route::get('/projects', [Project::class, 'projects']);
//Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']);
//Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']);
From 515d40174685901d986d027a673fcaf80bb24bc1 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 12:22:18 +0100
Subject: [PATCH 16/97] feat: add deployment details to deploy endpoint
---
app/Http/Controllers/Api/Deploy.php | 35 ++++++++++++++++++++++-------
routes/api.php | 3 +++
2 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php
index 21da51d66..1173bd5d2 100644
--- a/app/Http/Controllers/Api/Deploy.php
+++ b/app/Http/Controllers/Api/Deploy.php
@@ -45,15 +45,24 @@ class Deploy extends Controller
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
}
$message = collect([]);
+ $deployments = collect();
+ $payload = collect();
foreach ($uuids as $uuid) {
$resource = getResourceByUuid($uuid, $teamId);
if ($resource) {
- $return_message = $this->deploy_resource($resource, $force);
+ ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
+ if ($deployment_uuid) {
+ $deployments->push(['resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
+ }
$message = $message->merge($return_message);
}
}
if ($message->count() > 0) {
- return response()->json(['message' => $message->toArray()], 200);
+ $payload->put('message', $message->toArray());
+ if ($deployments->count() > 0) {
+ $payload->put('details', $deployments->toArray());
+ }
+ return response()->json($payload->toArray(), 200);
}
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
}
@@ -66,6 +75,8 @@ class Deploy extends Controller
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
}
$message = collect([]);
+ $deployments = collect();
+ $payload = collect();
foreach ($tags as $tag) {
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
if (!$found_tag) {
@@ -79,21 +90,28 @@ class Deploy extends Controller
continue;
}
foreach ($applications as $resource) {
- $return_message = $this->deploy_resource($resource, $force);
+ ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
+ if ($deployment_uuid) {
+ $deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
+ }
$message = $message->merge($return_message);
}
foreach ($services as $resource) {
- $return_message = $this->deploy_resource($resource, $force);
+ ['message' => $return_message] = $this->deploy_resource($resource, $force);
$message = $message->merge($return_message);
}
}
if ($message->count() > 0) {
- return response()->json(['message' => $message->toArray()], 200);
+ $payload->put('message', $message->toArray());
+ if ($deployments->count() > 0) {
+ $payload->put('details', $deployments->toArray());
+ }
+ return response()->json($payload->toArray(), 200);
}
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
}
- public function deploy_resource($resource, bool $force = false): Collection
+ public function deploy_resource($resource, bool $force = false): array
{
$message = collect([]);
if (gettype($resource) !== 'object') {
@@ -101,9 +119,10 @@ class Deploy extends Controller
}
$type = $resource?->getMorphClass();
if ($type === 'App\Models\Application') {
+ $deployment_uuid = new Cuid2(7);
queue_application_deployment(
application: $resource,
- deployment_uuid: new Cuid2(7),
+ deployment_uuid: $deployment_uuid,
force_rebuild: $force,
);
$message->push("Application {$resource->name} deployment queued.");
@@ -156,6 +175,6 @@ class Deploy extends Controller
StartService::run($resource);
$message->push("Service {$resource->name} started. It could take a while, be patient.");
}
- return $message;
+ return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
}
}
diff --git a/routes/api.php b/routes/api.php
index b27d1fb28..25f4f948f 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -32,10 +32,13 @@ Route::group([
return response(config('version'));
});
Route::get('/deploy', [Deploy::class, 'deploy']);
+
Route::get('/servers', [Server::class, 'servers']);
Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']);
+
Route::get('/resources', [Resources::class, 'resources']);
Route::get('/domains', [Domains::class, 'domains']);
+
Route::get('/teams', [Team::class, 'teams']);
Route::get('/team/current', [Team::class, 'current_team']);
Route::get('/team/current/members', [Team::class, 'current_team_members']);
From c7f15c42fa33320e333003c67b13b283e06e3f98 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 12:27:23 +0100
Subject: [PATCH 17/97] feat: add deployments api
---
app/Http/Controllers/Api/Deploy.php | 23 +++++++++++++++++++++--
app/Http/Controllers/Api/Domains.php | 2 +-
app/Http/Controllers/Api/Project.php | 6 +++---
app/Http/Controllers/Api/Resources.php | 2 +-
app/Http/Controllers/Api/Server.php | 4 ++--
app/Http/Controllers/Api/Team.php | 10 +++++-----
bootstrap/helpers/api.php | 4 ++++
routes/api.php | 1 +
8 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php
index 1173bd5d2..70753f328 100644
--- a/app/Http/Controllers/Api/Deploy.php
+++ b/app/Http/Controllers/Api/Deploy.php
@@ -9,13 +9,32 @@ use App\Actions\Database\StartPostgresql;
use App\Actions\Database\StartRedis;
use App\Actions\Service\StartService;
use App\Http\Controllers\Controller;
+use App\Models\ApplicationDeploymentQueue;
+use App\Models\Server;
use App\Models\Tag;
use Illuminate\Http\Request;
-use Illuminate\Support\Collection;
use Visus\Cuid2\Cuid2;
class Deploy extends Controller
{
+ public function deployments(Request $request) {
+ $teamId = get_team_id_from_token();
+ if (is_null($teamId)) {
+ return invalid_token();
+ }
+ $servers = Server::whereTeamId($teamId)->get();
+ $deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([
+ "id",
+ "application_id",
+ "application_name",
+ "deployment_url",
+ "pull_request_id",
+ "server_name",
+ "server_id",
+ "status"
+ ])->sortBy('id')->toArray();
+ return response()->json($deployments_per_server, 200);
+ }
public function deploy(Request $request)
{
$teamId = get_team_id_from_token();
@@ -27,7 +46,7 @@ class Deploy extends Controller
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
}
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
if ($tags) {
return $this->by_tags($tags, $teamId, $force);
diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php
index 4b8a026fb..15db5b6ef 100644
--- a/app/Http/Controllers/Api/Domains.php
+++ b/app/Http/Controllers/Api/Domains.php
@@ -13,7 +13,7 @@ class Domains extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$projects = ModelsProject::where('team_id', $teamId)->get();
$domains = collect();
diff --git a/app/Http/Controllers/Api/Project.php b/app/Http/Controllers/Api/Project.php
index fa2ba34bb..45d6b4059 100644
--- a/app/Http/Controllers/Api/Project.php
+++ b/app/Http/Controllers/Api/Project.php
@@ -12,7 +12,7 @@ class Project extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get();
return response()->json($projects);
@@ -21,7 +21,7 @@ class Project extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
return response()->json($project);
@@ -30,7 +30,7 @@ class Project extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
$environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php
index 78ea854c7..d3d313f3a 100644
--- a/app/Http/Controllers/Api/Resources.php
+++ b/app/Http/Controllers/Api/Resources.php
@@ -12,7 +12,7 @@ class Resources extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$projects = Project::where('team_id', $teamId)->get();
$resources = collect();
diff --git a/app/Http/Controllers/Api/Server.php b/app/Http/Controllers/Api/Server.php
index 2cfec183e..bb5ef255b 100644
--- a/app/Http/Controllers/Api/Server.php
+++ b/app/Http/Controllers/Api/Server.php
@@ -12,7 +12,7 @@ class Server extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
$server['is_reachable'] = $server->settings->is_reachable;
@@ -26,7 +26,7 @@ class Server extends Controller
$with_resources = $request->query('resources');
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
if (is_null($server)) {
diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php
index d99ea8ea7..862d2e185 100644
--- a/app/Http/Controllers/Api/Team.php
+++ b/app/Http/Controllers/Api/Team.php
@@ -11,7 +11,7 @@ class Team extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$teams = auth()->user()->teams;
return response()->json($teams);
@@ -21,7 +21,7 @@ class Team extends Controller
$id = $request->id;
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
@@ -35,7 +35,7 @@ class Team extends Controller
$id = $request->id;
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
@@ -48,7 +48,7 @@ class Team extends Controller
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$team = auth()->user()->currentTeam();
return response()->json($team);
@@ -56,7 +56,7 @@ class Team extends Controller
public function current_team_members(Request $request) {
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+ return invalid_token();
}
$team = auth()->user()->currentTeam();
return response()->json($team->members);
diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php
index 94e9242cb..4fcdbac4f 100644
--- a/bootstrap/helpers/api.php
+++ b/bootstrap/helpers/api.php
@@ -5,3 +5,7 @@ function get_team_id_from_token()
$token = auth()->user()->currentAccessToken();
return data_get($token, 'team_id');
}
+function invalid_token()
+{
+ return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
+}
diff --git a/routes/api.php b/routes/api.php
index 25f4f948f..c0ea836a6 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -32,6 +32,7 @@ Route::group([
return response(config('version'));
});
Route::get('/deploy', [Deploy::class, 'deploy']);
+ Route::get('/deployments', [Deploy::class, 'deployments']);
Route::get('/servers', [Server::class, 'servers']);
Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']);
From 9d31d990fc8b5f140d344fc6dde86db1d385b0df Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 12:35:38 +0100
Subject: [PATCH 18/97] Update error message for missing resources
---
app/Http/Controllers/Api/Deploy.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php
index 70753f328..bdea30029 100644
--- a/app/Http/Controllers/Api/Deploy.php
+++ b/app/Http/Controllers/Api/Deploy.php
@@ -99,7 +99,7 @@ class Deploy extends Controller
foreach ($tags as $tag) {
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
if (!$found_tag) {
- $message->push("Tag {$tag} not found.");
+ // $message->push("Tag {$tag} not found.");
continue;
}
$applications = $found_tag->applications()->get();
@@ -120,6 +120,7 @@ class Deploy extends Controller
$message = $message->merge($return_message);
}
}
+ ray($message);
if ($message->count() > 0) {
$payload->put('message', $message->toArray());
if ($deployments->count() > 0) {
@@ -128,7 +129,7 @@ class Deploy extends Controller
return response()->json($payload->toArray(), 200);
}
- return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
+ return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
}
public function deploy_resource($resource, bool $force = false): array
{
From bbfbd4a1055b928ce0626118ddcc989acf81fbe5 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 12:36:49 +0100
Subject: [PATCH 19/97] Refactor domain handling in API controller
---
app/Http/Controllers/Api/Domains.php | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php
index 15db5b6ef..f6468cdf0 100644
--- a/app/Http/Controllers/Api/Domains.php
+++ b/app/Http/Controllers/Api/Domains.php
@@ -45,12 +45,10 @@ class Domains extends Controller
]);
}
} else {
- if (!$settings->public_ipv4 && !$settings->public_ipv6) {
- $domains->push([
- 'domain' => $fqdn,
- 'ip' => $ip,
- ]);
- }
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
}
}
}
@@ -83,12 +81,10 @@ class Domains extends Controller
]);
}
} else {
- if (!$settings->public_ipv4 && !$settings->public_ipv6) {
- $domains->push([
- 'domain' => $fqdn,
- 'ip' => $ip,
- ]);
- }
+ $domains->push([
+ 'domain' => $fqdn,
+ 'ip' => $ip,
+ ]);
}
}
}
From 129a644781fe56682a8072e8fbee9647c7249dd9 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 12:58:04 +0100
Subject: [PATCH 20/97] ui: make notifications separate view fix: popup if no
notifications are set
---
app/Livewire/LayoutPopups.php | 6 +++-
app/Models/Team.php | 19 +++++++++++--
...add_notifications_notification_disable.php | 28 +++++++++++++++++++
resources/views/components/navbar.blade.php | 12 +++++++-
.../components/notifications/navbar.blade.php | 25 +++++++++++++++++
.../views/components/team/navbar.blade.php | 11 +++-----
.../views/livewire/layout-popups.blade.php | 15 +++++++++-
.../team/notification/index.blade.php | 2 +-
routes/web.php | 4 ++-
9 files changed, 108 insertions(+), 14 deletions(-)
create mode 100644 database/migrations/2024_03_07_115054_add_notifications_notification_disable.php
create mode 100644 resources/views/components/notifications/navbar.blade.php
diff --git a/app/Livewire/LayoutPopups.php b/app/Livewire/LayoutPopups.php
index dd7f14678..b6f06f808 100644
--- a/app/Livewire/LayoutPopups.php
+++ b/app/Livewire/LayoutPopups.php
@@ -17,10 +17,14 @@ class LayoutPopups extends Component
{
$this->dispatch('success', 'Realtime events configured!');
}
- public function disable()
+ public function disableSponsorship()
{
auth()->user()->update(['is_notification_sponsorship_enabled' => false]);
}
+ public function disableNotifications()
+ {
+ auth()->user()->update(['is_notification_notifications_enabled' => false]);
+ }
public function render()
{
return view('livewire.layout-popups');
diff --git a/app/Models/Team.php b/app/Models/Team.php
index 656c4009b..7cb1601de 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -48,13 +48,15 @@ class Team extends Model implements SendsDiscord, SendsEmail
}
return explode(',', $recipients);
}
- static public function serverLimitReached() {
+ static public function serverLimitReached()
+ {
$serverLimit = Team::serverLimit();
$team = currentTeam();
$servers = $team->servers->count();
return $servers >= $serverLimit;
}
- public function serverOverflow() {
+ public function serverOverflow()
+ {
if ($this->serverLimit() < $this->servers->count()) {
return true;
}
@@ -170,4 +172,17 @@ class Team extends Model implements SendsDiscord, SendsEmail
]);
}
}
+ public function isAnyNotificationEnabled()
+ {
+ if (isCloud()) {
+ return true;
+ }
+ if (!data_get(auth()->user(), 'is_notification_notifications_enabled')) {
+ return true;
+ }
+ if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->use_instance_email_settings) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/database/migrations/2024_03_07_115054_add_notifications_notification_disable.php b/database/migrations/2024_03_07_115054_add_notifications_notification_disable.php
new file mode 100644
index 000000000..8633b971e
--- /dev/null
+++ b/database/migrations/2024_03_07_115054_add_notifications_notification_disable.php
@@ -0,0 +1,28 @@
+boolean('is_notification_notifications_enabled')->default(true);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('is_notification_notifications_enabled');
+ });
+ }
+};
diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php
index 3bd1730be..65d2f0736 100644
--- a/resources/views/components/navbar.blade.php
+++ b/resources/views/components/navbar.blade.php
@@ -150,7 +150,17 @@
@endif
-
+
+
+
+
+
+ Notifications
+
+
@if (isInstanceAdmin())
diff --git a/resources/views/components/notifications/navbar.blade.php b/resources/views/components/notifications/navbar.blade.php
new file mode 100644
index 000000000..65b374249
--- /dev/null
+++ b/resources/views/components/notifications/navbar.blade.php
@@ -0,0 +1,25 @@
+
+
+
Team Notifications
+
+
+
+
+
+ Currently active team: {{ session('currentTeam.name') }}
+
+
+
+
+
+
+ General
+
+
+
+
+
+
+
diff --git a/resources/views/components/team/navbar.blade.php b/resources/views/components/team/navbar.blade.php
index 971b9de85..a3f13b4af 100644
--- a/resources/views/components/team/navbar.blade.php
+++ b/resources/views/components/team/navbar.blade.php
@@ -1,7 +1,7 @@
@@ -17,18 +17,15 @@
General
-
+
Members
S3 Storages
-
- Notifications
-
-
Shared Variables
diff --git a/resources/views/livewire/layout-popups.blade.php b/resources/views/livewire/layout-popups.blade.php
index 23b9bfe64..afacccc72 100644
--- a/resources/views/livewire/layout-popups.blade.php
+++ b/resources/views/livewire/layout-popups.blade.php
@@ -7,7 +7,8 @@
consider donating!💜
It enables us to keep creating features without paywalls, ensuring our work remains free and
open.
- Disable This Popup
+ Disable This
+ Popup
@endif
@@ -20,4 +21,16 @@
@endif
+ @if (!currentTeam()->isAnyNotificationEnabled())
+
+
+
WARNING: No notifications enabled. It is highly recommended to enable at least
+ one
+ notification channel to receive important alerts. Visit /notification to enable notifications.
+
Disable This
+ Popup
+
+
+ @endif
diff --git a/resources/views/livewire/team/notification/index.blade.php b/resources/views/livewire/team/notification/index.blade.php
index ed961deb0..e5eaffe17 100644
--- a/resources/views/livewire/team/notification/index.blade.php
+++ b/resources/views/livewire/team/notification/index.blade.php
@@ -1,5 +1,5 @@
-
+
Notifications
diff --git a/routes/web.php b/routes/web.php
index f49d1b278..c442f6c75 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -121,11 +121,13 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/', TagsIndex::class)->name('tags.index');
Route::get('/{tag_name}', TagsShow::class)->name('tags.show');
});
+ Route::prefix('notifications')->group(function () {
+ Route::get('/', TeamNotificationIndex::class)->name('notification.index');
+ });
Route::prefix('team')->group(function () {
Route::get('/', TeamIndex::class)->name('team.index');
Route::get('/new', TeamCreate::class)->name('team.create');
Route::get('/members', TeamMemberIndex::class)->name('team.member.index');
- Route::get('/notifications', TeamNotificationIndex::class)->name('team.notification.index');
Route::get('/shared-variables', TeamSharedVariablesIndex::class)->name('team.shared-variables.index');
Route::get('/storages', TeamStorageIndex::class)->name('team.storage.index');
Route::get('/storages/new', TeamStorageCreate::class)->name('team.storage.create');
From 8b74e50c50d9ea112cd8b497ae48eacfa318e906 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 13:05:04 +0100
Subject: [PATCH 21/97] Add two-factor authentication fields to hidden array in
User model
---
app/Models/User.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/Models/User.php b/app/Models/User.php
index d6389d678..e2ecae56a 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -26,6 +26,8 @@ class User extends Authenticatable implements SendsEmail
protected $hidden = [
'password',
'remember_token',
+ 'two_factor_recovery_codes',
+ 'two_factor_secret',
];
protected $casts = [
'email_verified_at' => 'datetime',
From 93e4e723fab8b3d5679a39bd89db6ce4b09afb6c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 15:45:49 +0100
Subject: [PATCH 22/97] Add documentation links to error responses in Team
controller
---
app/Http/Controllers/Api/Team.php | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php
index 862d2e185..540ac282a 100644
--- a/app/Http/Controllers/Api/Team.php
+++ b/app/Http/Controllers/Api/Team.php
@@ -26,7 +26,7 @@ class Team extends Controller
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
- return response()->json(['error' => 'Team not found.'], 404);
+ return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id"], 404);
}
return response()->json($team);
}
@@ -40,7 +40,7 @@ class Team extends Controller
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
- return response()->json(['error' => 'Team not found.'], 404);
+ return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-members"], 404);
}
return response()->json($team->members);
}
@@ -53,7 +53,8 @@ class Team extends Controller
$team = auth()->user()->currentTeam();
return response()->json($team);
}
- public function current_team_members(Request $request) {
+ public function current_team_members(Request $request)
+ {
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
From 6688120aeebb7a2316763623854c85335f9d7f46 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 7 Mar 2024 15:46:27 +0100
Subject: [PATCH 23/97] Update team-by-id-members API documentation link
---
app/Http/Controllers/Api/Team.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php
index 540ac282a..453c2590f 100644
--- a/app/Http/Controllers/Api/Team.php
+++ b/app/Http/Controllers/Api/Team.php
@@ -40,7 +40,7 @@ class Team extends Controller
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
- return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-members"], 404);
+ return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id-members"], 404);
}
return response()->json($team->members);
}
From 8b9548a463dbd9e0ac72a5bb307bb79c189d4d47 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Fri, 8 Mar 2024 15:16:58 +0100
Subject: [PATCH 24/97] Refactor resource mapping in resources() method
---
app/Http/Controllers/Api/Resources.php | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php
index d3d313f3a..4032d26e2 100644
--- a/app/Http/Controllers/Api/Resources.php
+++ b/app/Http/Controllers/Api/Resources.php
@@ -22,6 +22,16 @@ class Resources extends Controller
$resources->push($projects->pluck(str($db)->plural(2))->flatten());
}
$resources = $resources->flatten();
+ $resources = $resources->map(function ($resource) {
+ $payload = $resource->toArray();
+ if ($resource->getMorphClass() === 'App\Models\Service') {
+ $payload['status'] = $resource->status();
+ } else {
+ $payload['status'] = $resource->status;
+ }
+ $payload['type'] = $resource->type();
+ return $payload;
+ });
return response()->json($resources);
}
From 15ca68f7e16d06c38ae901e2895306f379783fce Mon Sep 17 00:00:00 2001
From: Fabian Gigler
Date: Fri, 8 Mar 2024 18:44:42 +0100
Subject: [PATCH 25/97] improve onboarding messages
---
.../views/livewire/boarding/index.blade.php | 30 +++++++++----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php
index 2777eb3ac..e32528dcc 100644
--- a/resources/views/livewire/boarding/index.blade.php
+++ b/resources/views/livewire/boarding/index.blade.php
@@ -3,7 +3,7 @@
@if ($currentState === 'welcome')
Welcome to Coolify
-
Let me help you to set the basics.
+
Let me help you set up the basics.
Get
Started
@@ -24,12 +24,12 @@
- You do not to manage your servers too much. Coolify do
+
You don't need to manage your servers anymore. Coolify does
it for you.
- All configurations are stored on your server, so
- everything works without Coolify (except integrations and automations).
- You will get notified on your favourite platform (Discord,
- Telegram, Email, etc.) when something goes wrong, or an action needed from your side.
+ All configurations are stored on your servers, so
+ everything works without a connection to Coolify (except integrations and automations).
+ You can get notified on your favourite platforms (Discord,
+ Telegram, Email, etc.) when something goes wrong, or an action is needed from your side.
Next
@@ -40,8 +40,8 @@
@if ($currentState === 'select-server-type')
- Do you want to deploy your resources on your
- or on a ?
+ Do you want to deploy your resources to your
+ or to a ?
- Let's create a new
- one!
+ Create new
+ project!
@if (count($projects) > 0)
- Projects are bound together several resources into one virtual group. There are no
- limitations on the number of projects you could have.
- Each project should have at least one environment. This helps you to create a production &
+
Projects contain several resources combined into one virtual group. There are no
+ limitations on the number of projects you can add.
+ Each project should have at least one environment, this allows you to create a production &
staging version of the same application, but grouped separately.
@@ -331,7 +331,7 @@
@if ($currentState === 'create-resource')
- I will redirect you to the new resource page, where you can create your first resource.
+ Let's go to the new resource page, where you can create your first resource.
Let's do
From 853a14c6b80f9b4d94f59ffb36d7259bf33c6fda Mon Sep 17 00:00:00 2001
From: Lee Conlin
Date: Fri, 8 Mar 2024 23:54:37 +0000
Subject: [PATCH 26/97] Added PenPot service template
---
templates/compose/penpot.yaml | 83 +++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 templates/compose/penpot.yaml
diff --git a/templates/compose/penpot.yaml b/templates/compose/penpot.yaml
new file mode 100644
index 000000000..646c1a706
--- /dev/null
+++ b/templates/compose/penpot.yaml
@@ -0,0 +1,83 @@
+# documentation: https://help.penpot.app/technical-guide/getting-started/#install-with-docker
+# slogan: Penpot is the first Open Source design and prototyping platform for product teams.
+# tags: penpot,design,prototyping,figma,open,source
+
+version: '3.5'
+networks:
+ penpot: null
+volumes:
+ penpot_postgres_v15: null
+ penpot_assets: null
+services:
+ penpot-frontend:
+ image: 'penpotapp/frontend:latest'
+ restart: always
+ ports:
+ - '9001:80'
+ volumes:
+ - 'penpot_assets:/opt/data/assets'
+ depends_on:
+ - penpot-backend
+ - penpot-exporter
+ networks:
+ - penpot
+ environment:
+ - SERVICE_FQDN_PENPOT-FRONTEND
+ - 'PENPOT_FLAGS=${PENPOT_FRONTEND_FLAGS}'
+ penpot-backend:
+ image: 'penpotapp/backend:latest'
+ restart: always
+ volumes:
+ - 'penpot_assets:/opt/data/assets'
+ depends_on:
+ - penpot-postgres
+ - penpot-redis
+ networks:
+ - penpot
+ environment:
+ - SERVICE_FQDN_PENPOT-BACKEND
+ - 'PENPOT_FLAGS=${PENPOT_BACKEND_FLAGS}'
+ - 'PENPOT_SECRET_KEY=${PENPOT_SECRET_KEY}'
+ - 'PENPOT_PUBLIC_URI=${SERVICE_FQDN_PENPOT-FRONTEND}'
+ - 'PENPOT_DATABASE_URI=postgresql://penpot-postgres/penpot'
+ - 'PENPOT_DATABASE_USERNAME=${SERVICE_USER_POSTGRES}'
+ - 'PENPOT_DATABASE_PASSWORD=${SERVICE_PASSWORD_POSTGRES}'
+ - 'PENPOT_REDIS_URI=redis://penpot-redis/0'
+ - PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
+ - PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets
+ - 'PENPOT_TELEMETRY_ENABLED=${PENPOT_TELEMETRY_ENABLED}'
+ - 'PENPOT_SMTP_DEFAULT_FROM=${PENPOT_SMTP_DEFAULT_FROM}'
+ - 'PENPOT_SMTP_DEFAULT_REPLY_TO=${PENPOT_SMTP_DEFAULT_REPLY_TO}'
+ - 'PENPOT_SMTP_HOST=${PENPOT_SMTP_HOST}'
+ - 'PENPOT_SMTP_PORT=${PENPOT_SMTP_PORT}'
+ - 'PENPOT_SMTP_USERNAME=${PENPOT_SMTP_USERNAME}'
+ - 'PENPOT_SMTP_PASSWORD=${PENPOT_SMTP_PASSWORD}'
+ - 'PENPOT_SMTP_TLS=${PENPOT_SMTP_TLS}'
+ - 'PENPOT_SMTP_SSL=${PENPOT_SMTP_SSL}'
+ penpot-exporter:
+ image: 'penpotapp/exporter:latest'
+ restart: always
+ networks:
+ - penpot
+ environment:
+ - SERVICE_FQDN_PENPOT-EXPORTER
+ - 'PENPOT_PUBLIC_URI=${SERVICE_FQDN_PENPOT-FRONTEND}'
+ - 'PENPOT_REDIS_URI=redis://penpot-redis/0'
+ penpot-postgres:
+ image: 'postgres:15'
+ restart: always
+ stop_signal: SIGINT
+ volumes:
+ - 'penpot_postgres_v15:/var/lib/postgresql/data'
+ networks:
+ - penpot
+ environment:
+ - POSTGRES_INITDB_ARGS=--data-checksums
+ - POSTGRES_DB=penpot
+ - 'POSTGRES_USER=${SERVICE_USER_POSTGRES}'
+ - 'POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}'
+ penpot-redis:
+ image: 'redis:7'
+ restart: always
+ networks:
+ - penpot
\ No newline at end of file
From 366d39a7a3647a4a7af51eb9c278eb2d0cac6f82 Mon Sep 17 00:00:00 2001
From: Lee Conlin
Date: Sat, 9 Mar 2024 00:21:47 +0000
Subject: [PATCH 27/97] Added some default env values for penpot
---
templates/compose/penpot.env | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 templates/compose/penpot.env
diff --git a/templates/compose/penpot.env b/templates/compose/penpot.env
new file mode 100644
index 000000000..a0b07199f
--- /dev/null
+++ b/templates/compose/penpot.env
@@ -0,0 +1,12 @@
+PENPOT_BACKEND_FLAGS=enable-login-with-password enable-smtp enable-prepl-server
+PENPOT_FRONTEND_FLAGS=enable-login-with-password
+PENPOT_SECRET_KEY=$SERVICE_PASSWORD_64_PENPOT
+PENPOT_SMTP_DEFAULT_FROM=
+PENPOT_SMTP_DEFAULT_REPLY_TO=
+PENPOT_SMTP_HOST=
+PENPOT_SMTP_PASSWORD=
+PENPOT_SMTP_PORT=
+PENPOT_SMTP_SSL=false
+PENPOT_SMTP_TLS=false
+PENPOT_SMTP_USERNAME=
+PENPOT_TELEMETRY_ENABLED=true
\ No newline at end of file
From 820099622e8f10448f88879247d85747c6a78592 Mon Sep 17 00:00:00 2001
From: "Mr. Mendez" <56850299+JustMrMendez@users.noreply.github.com>
Date: Sun, 10 Mar 2024 18:14:53 +0000
Subject: [PATCH 28/97] visual feedback when container is unhealthy
---
resources/views/components/status/running.blade.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/resources/views/components/status/running.blade.php b/resources/views/components/status/running.blade.php
index b046266d1..4751204ce 100644
--- a/resources/views/components/status/running.blade.php
+++ b/resources/views/components/status/running.blade.php
@@ -8,6 +8,6 @@
{{ str($status)->before(':')->headline() }}
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
- ({{ str($status)->after(':') }})
+ ({{ str($status)->after(':') }})
@endif
From 8b73f9da173b9e2a34c57ee60d206e66a2af77da Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 09:42:02 +0100
Subject: [PATCH 29/97] fix: deploy api messages
---
app/Http/Controllers/Api/Deploy.php | 50 ++++++++++-------------------
1 file changed, 17 insertions(+), 33 deletions(-)
diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php
index bdea30029..89a994afe 100644
--- a/app/Http/Controllers/Api/Deploy.php
+++ b/app/Http/Controllers/Api/Deploy.php
@@ -17,7 +17,8 @@ use Visus\Cuid2\Cuid2;
class Deploy extends Controller
{
- public function deployments(Request $request) {
+ public function deployments(Request $request)
+ {
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
@@ -63,7 +64,6 @@ class Deploy extends Controller
if (count($uuids) === 0) {
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
}
- $message = collect([]);
$deployments = collect();
$payload = collect();
foreach ($uuids as $uuid) {
@@ -71,16 +71,14 @@ class Deploy extends Controller
if ($resource) {
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
if ($deployment_uuid) {
- $deployments->push(['resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
+ $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
+ } else {
+ $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]);
}
- $message = $message->merge($return_message);
}
}
- if ($message->count() > 0) {
- $payload->put('message', $message->toArray());
- if ($deployments->count() > 0) {
- $payload->put('details', $deployments->toArray());
- }
+ if ($deployments->count() > 0) {
+ $payload->put('deployments', $deployments->toArray());
return response()->json($payload->toArray(), 200);
}
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
@@ -133,9 +131,10 @@ class Deploy extends Controller
}
public function deploy_resource($resource, bool $force = false): array
{
- $message = collect([]);
+ $message = null;
+ $deployment_uuid = null;
if (gettype($resource) !== 'object') {
- return $message->push("Resource ($resource) not found.");
+ return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
}
$type = $resource?->getMorphClass();
if ($type === 'App\Models\Application') {
@@ -145,55 +144,40 @@ class Deploy extends Controller
deployment_uuid: $deployment_uuid,
force_rebuild: $force,
);
- $message->push("Application {$resource->name} deployment queued.");
+ $message = "Application {$resource->name} deployment queued.";
} else if ($type === 'App\Models\StandalonePostgresql') {
- if (str($resource->status)->startsWith('running')) {
- $message->push("Database {$resource->name} already running.");
- }
StartPostgresql::run($resource);
$resource->update([
'started_at' => now(),
]);
- $message->push("Database {$resource->name} started.");
+ $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneRedis') {
- if (str($resource->status)->startsWith('running')) {
- $message->push("Database {$resource->name} already running.");
- }
StartRedis::run($resource);
$resource->update([
'started_at' => now(),
]);
- $message->push("Database {$resource->name} started.");
+ $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneMongodb') {
- if (str($resource->status)->startsWith('running')) {
- $message->push("Database {$resource->name} already running.");
- }
StartMongodb::run($resource);
$resource->update([
'started_at' => now(),
]);
- $message->push("Database {$resource->name} started.");
+ $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneMysql') {
- if (str($resource->status)->startsWith('running')) {
- $message->push("Database {$resource->name} already running.");
- }
StartMysql::run($resource);
$resource->update([
'started_at' => now(),
]);
- $message->push("Database {$resource->name} started.");
+ $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneMariadb') {
- if (str($resource->status)->startsWith('running')) {
- $message->push("Database {$resource->name} already running.");
- }
StartMariadb::run($resource);
$resource->update([
'started_at' => now(),
]);
- $message->push("Database {$resource->name} started.");
+ $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\Service') {
StartService::run($resource);
- $message->push("Service {$resource->name} started. It could take a while, be patient.");
+ $message = "Service {$resource->name} started. It could take a while, be patient.";
}
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
}
From 5d3de967f027e88aed971dafd0d9b04426163732 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 09:42:16 +0100
Subject: [PATCH 30/97] fix: fqdn null in case docker compose bp
---
app/Livewire/Project/Application/General.php | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 3279ff2d5..274e198a8 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -112,6 +112,10 @@ class General extends Component
} catch (\Throwable $e) {
$this->dispatch('error', $e->getMessage());
}
+ if ($this->application->build_pack === 'dockercompose') {
+ $this->application->fqdn = null;
+ $this->application->settings->save();
+ }
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
$this->ports_exposes = $this->application->ports_exposes;
@@ -163,7 +167,12 @@ class General extends Component
}
return $domain;
}
-
+ public function updatedApplicationBaseDirectory() {
+ raY('asdf');
+ if ($this->application->build_pack === 'dockercompose') {
+ $this->loadComposeFile();
+ }
+ }
public function updatedApplicationBuildPack()
{
if ($this->application->build_pack !== 'nixpacks') {
@@ -211,7 +220,6 @@ class General extends Component
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
$this->application->save();
$this->resetDefaultLabels(false);
- // $this->dispatch('success', 'Labels reset to default!');
}
public function submit($showToaster = true)
{
From 34d6a12d9575afe440df0800a5d57fb381ec38c4 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 15:08:05 +0100
Subject: [PATCH 31/97] feat: experimental caddy support
---
app/Actions/Proxy/CheckConfiguration.php | 7 +-
app/Actions/Proxy/CheckProxy.php | 3 +
app/Actions/Proxy/SaveConfiguration.php | 2 +-
app/Actions/Proxy/StartProxy.php | 4 +-
app/Livewire/Boarding/Index.php | 1 +
app/Livewire/Project/Application/General.php | 4 +-
.../Project/Shared/ResourceOperations.php | 2 +-
app/Livewire/Server/New/ByIp.php | 1 +
.../Proxy/DynamicConfigurationNavbar.php | 2 +-
.../Server/Proxy/DynamicConfigurations.php | 2 +-
.../Server/Proxy/NewDynamicConfiguration.php | 2 +-
app/Models/Server.php | 23 ++-
bootstrap/helpers/docker.php | 53 ++++-
bootstrap/helpers/proxy.php | 191 ++++++++++--------
bootstrap/helpers/shared.php | 22 +-
config/sentry.php | 2 +-
.../views/components/server/sidebar.blade.php | 12 +-
.../views/livewire/server/proxy.blade.php | 32 ++-
18 files changed, 252 insertions(+), 113 deletions(-)
diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php
index 7a1cb04ed..208a6863a 100644
--- a/app/Actions/Proxy/CheckConfiguration.php
+++ b/app/Actions/Proxy/CheckConfiguration.php
@@ -11,7 +11,12 @@ class CheckConfiguration
use AsAction;
public function handle(Server $server, bool $reset = false)
{
- $proxy_path = get_proxy_path();
+ $proxyType = $server->proxyType();
+ if ($proxyType === 'NONE') {
+ return 'OK';
+ }
+ $proxy_path = $server->proxyPath();
+
$proxy_configuration = instant_remote_process([
"mkdir -p $proxy_path",
"cat $proxy_path/docker-compose.yml",
diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php
index ccefa8681..41f961b59 100644
--- a/app/Actions/Proxy/CheckProxy.php
+++ b/app/Actions/Proxy/CheckProxy.php
@@ -10,6 +10,9 @@ class CheckProxy
use AsAction;
public function handle(Server $server, $fromUI = false)
{
+ if ($server->proxyType() === 'NONE') {
+ return false;
+ }
if (!$server->isProxyShouldRun()) {
if ($fromUI) {
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
diff --git a/app/Actions/Proxy/SaveConfiguration.php b/app/Actions/Proxy/SaveConfiguration.php
index edf4f3434..5fb983d1a 100644
--- a/app/Actions/Proxy/SaveConfiguration.php
+++ b/app/Actions/Proxy/SaveConfiguration.php
@@ -15,7 +15,7 @@ class SaveConfiguration
if (is_null($proxy_settings)) {
$proxy_settings = CheckConfiguration::run($server, true);
}
- $proxy_path = get_proxy_path();
+ $proxy_path = $server->proxyPath();
$docker_compose_yml_base64 = base64_encode($proxy_settings);
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php
index e106c1801..46ac816b4 100644
--- a/app/Actions/Proxy/StartProxy.php
+++ b/app/Actions/Proxy/StartProxy.php
@@ -15,11 +15,11 @@ class StartProxy
{
try {
$proxyType = $server->proxyType();
- if ($proxyType === 'NONE') {
+ if (is_null($proxyType) || $proxyType === 'NONE') {
return 'OK';
}
$commands = collect([]);
- $proxy_path = get_proxy_path();
+ $proxy_path = $server->proxyPath();
$configuration = CheckConfiguration::run($server);
if (!$configuration) {
throw new \Exception("Configuration is not synced");
diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php
index e80042573..52440dde7 100644
--- a/app/Livewire/Boarding/Index.php
+++ b/app/Livewire/Boarding/Index.php
@@ -126,6 +126,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
}
public function getProxyType()
{
+ // Set Default Proxy Type
$this->selectProxy(ProxyTypes::TRAEFIK_V2->value);
// $proxyTypeSet = $this->createdServer->proxy->type;
// if (!$proxyTypeSet) {
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 274e198a8..92b452a42 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -124,7 +124,7 @@ class General extends Component
}
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
$this->customLabels = $this->application->parseContainerLabels();
- if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
+ if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
@@ -224,7 +224,7 @@ class General extends Component
public function submit($showToaster = true)
{
try {
- if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
+ if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php
index e2037991a..62562179a 100644
--- a/app/Livewire/Project/Shared/ResourceOperations.php
+++ b/app/Livewire/Project/Shared/ResourceOperations.php
@@ -45,7 +45,7 @@ class ResourceOperations extends Component
'destination_id' => $new_destination->id,
]);
$new_resource->save();
- if ($new_resource->destination->server->proxyType() === 'TRAEFIK_V2') {
+ if ($new_resource->destination->server->proxyType() !== 'NONE') {
$customLabels = str(implode("|", generateLabelsApplication($new_resource)))->replace("|", "\n");
$new_resource->custom_labels = base64_encode($customLabels);
$new_resource->save();
diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php
index 9799443c7..df3fae20f 100644
--- a/app/Livewire/Server/New/ByIp.php
+++ b/app/Livewire/Server/New/ByIp.php
@@ -89,6 +89,7 @@ class ByIp extends Component
'team_id' => currentTeam()->id,
'private_key_id' => $this->private_key_id,
'proxy' => [
+ // set default proxy type to traefik v2
"type" => ProxyTypes::TRAEFIK_V2->value,
"status" => ProxyStatus::EXITED->value,
],
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
index ee46a3fff..aee61f7de 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
@@ -14,7 +14,7 @@ class DynamicConfigurationNavbar extends Component
public function delete(string $fileName)
{
$server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first();
- $proxy_path = get_proxy_path();
+ $proxy_path = $server->proxyPath();
$file = str_replace('|', '.', $fileName);
instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server);
$this->dispatch('success', 'File deleted.');
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurations.php b/app/Livewire/Server/Proxy/DynamicConfigurations.php
index 6e52f9d4a..b9e89321c 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurations.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurations.php
@@ -17,7 +17,7 @@ class DynamicConfigurations extends Component
];
public function loadDynamicConfigurations()
{
- $proxy_path = get_proxy_path();
+ $proxy_path = $this->server->proxyPath();
$files = instant_remote_process(["mkdir -p $proxy_path/dynamic && ls -1 {$proxy_path}/dynamic"], $this->server);
$files = collect(explode("\n", $files))->filter(fn ($file) => !empty($file));
$files = $files->map(fn ($file) => trim($file));
diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
index c9ceb41ee..302e603b3 100644
--- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
+++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
@@ -46,7 +46,7 @@ class NewDynamicConfiguration extends Component
$this->dispatch('error', 'File name is reserved.');
return;
}
- $proxy_path = get_proxy_path();
+ $proxy_path = $this->proxyPath();
$file = "{$proxy_path}/dynamic/{$this->fileName}";
if ($this->newFile) {
$exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server);
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 4028109e2..bc65cbdba 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -118,17 +118,30 @@ class Server extends BaseModel
}
}
}
+ public function proxyPath() {
+ $base_path = config('coolify.base_config_path');
+ $proxyType = $this->proxyType();
+ $proxy_path = "$base_path/proxy";
+ if ($proxyType === ProxyTypes::TRAEFIK_V2->value) {
+ $proxy_path = $proxy_path;
+ } else if ($proxyType === ProxyTypes::CADDY->value) {
+ $proxy_path = $proxy_path . '/caddy';
+ } else if ($proxyType === ProxyTypes::NGINX->value) {
+ $proxy_path = $proxy_path . '/nginx';
+ }
+ return $proxy_path;
+ }
public function proxyType()
{
$proxyType = $this->proxy->get('type');
if ($proxyType === ProxyTypes::NONE->value) {
return $proxyType;
}
- if (is_null($proxyType)) {
- $this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
- $this->proxy->status = ProxyStatus::EXITED->value;
- $this->save();
- }
+ // if (is_null($proxyType)) {
+ // $this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
+ // $this->proxy->status = ProxyStatus::EXITED->value;
+ // $this->save();
+ // }
return $this->proxy->get('type');
}
public function scopeWithProxy(): Builder
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 5fd43daa9..53b0bbe80 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -1,5 +1,6 @@
$domain) {
+ $loop = $loop;
+ $url = Url::fromString($domain);
+ $host = $url->getHost();
+ $path = $url->getPath();
+ // $stripped_path = str($path)->replaceEnd('/', '');
+
+ $schema = $url->getScheme();
+ $port = $url->getPort();
+ if (is_null($port) && !is_null($onlyPort)) {
+ $port = $onlyPort;
+ }
+ $labels->push("caddy_{$loop}={$schema}://{$host}");
+ $labels->push("caddy_{$loop}.header=-Server");
+
+ if ($serviceLabels) {
+ $labels->push("caddy_ingress_network={$uuid}");
+ $labels->push("caddy_{$loop}.reverse_proxy={{upstreams}}");
+ } else {
+ $labels->push("caddy_ingress_network={$network}");
+ if ($port) {
+ $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams $port}}");
+ } else {
+ $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams}}");
+ }
+ $labels->push("caddy_{$loop}.handle_path={$path}*");
+ }
+
+ if ($is_gzip_enabled) {
+ $labels->push("caddy_{$loop}.encode=zstd gzip");
+ }
+ if (isDev()) {
+ // $labels->push("caddy_{$loop}.tls=internal");
+ }
+ }
+ return $labels->sort();
+}
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null)
{
$labels = collect([]);
@@ -395,7 +436,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
} else {
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
}
- // Add Traefik labels no matter which proxy is selected
+ // Add Traefik labels
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
@@ -404,6 +445,16 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
+ // Add Caddy labels
+ $labels = $labels->merge(fqdnLabelsForCaddy(
+ network: $application->destination->network,
+ uuid: $appUuid,
+ domains: $domains,
+ onlyPort: $onlyPort,
+ is_force_https_enabled: $application->isForceHttpsEnabled(),
+ is_gzip_enabled: $application->isGzipEnabled(),
+ is_stripprefix_enabled: $application->isStripprefixEnabled()
+ ));
}
return $labels->all();
}
diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php
index 1bc1bdc28..1edfaed2e 100644
--- a/bootstrap/helpers/proxy.php
+++ b/bootstrap/helpers/proxy.php
@@ -7,12 +7,7 @@ use App\Models\Server;
use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
-function get_proxy_path()
-{
- $base_path = config('coolify.base_config_path');
- $proxy_path = "$base_path/proxy";
- return $proxy_path;
-}
+
function connectProxyToNetworks(Server $server)
{
if ($server->isSwarm()) {
@@ -75,7 +70,9 @@ function connectProxyToNetworks(Server $server)
}
function generate_default_proxy_configuration(Server $server)
{
- $proxy_path = get_proxy_path();
+ $proxy_path = $server->proxyPath();
+ $proxy_type = $server->proxyType();
+
if ($server->isSwarm()) {
$networks = collect($server->swarmDockers)->map(function ($docker) {
return $docker['network'];
@@ -98,93 +95,129 @@ function generate_default_proxy_configuration(Server $server)
"external" => true,
];
});
- $labels = [
- "traefik.enable=true",
- "traefik.http.routers.traefik.entrypoints=http",
- "traefik.http.routers.traefik.service=api@internal",
- "traefik.http.services.traefik.loadbalancer.server.port=8080",
- "coolify.managed=true",
- ];
- $config = [
- "version" => "3.8",
- "networks" => $array_of_networks->toArray(),
- "services" => [
- "traefik" => [
- "container_name" => "coolify-proxy",
- "image" => "traefik:v2.10",
- "restart" => RESTART_MODE,
- "extra_hosts" => [
- "host.docker.internal:host-gateway",
+ if ($proxy_type === 'TRAEFIK_V2') {
+ $labels = [
+ "traefik.enable=true",
+ "traefik.http.routers.traefik.entrypoints=http",
+ "traefik.http.routers.traefik.service=api@internal",
+ "traefik.http.services.traefik.loadbalancer.server.port=8080",
+ "coolify.managed=true",
+ ];
+ $config = [
+ "version" => "3.8",
+ "networks" => $array_of_networks->toArray(),
+ "services" => [
+ "traefik" => [
+ "container_name" => "coolify-proxy",
+ "image" => "traefik:v2.10",
+ "restart" => RESTART_MODE,
+ "extra_hosts" => [
+ "host.docker.internal:host-gateway",
+ ],
+ "networks" => $networks->toArray(),
+ "ports" => [
+ "80:80",
+ "443:443",
+ "8080:8080",
+ ],
+ "healthcheck" => [
+ "test" => "wget -qO- http://localhost:80/ping || exit 1",
+ "interval" => "4s",
+ "timeout" => "2s",
+ "retries" => 5,
+ ],
+ "volumes" => [
+ "/var/run/docker.sock:/var/run/docker.sock:ro",
+ "{$proxy_path}:/traefik",
+ ],
+ "command" => [
+ "--ping=true",
+ "--ping.entrypoint=http",
+ "--api.dashboard=true",
+ "--api.insecure=false",
+ "--entrypoints.http.address=:80",
+ "--entrypoints.https.address=:443",
+ "--entrypoints.http.http.encodequerysemicolons=true",
+ "--entryPoints.http.http2.maxConcurrentStreams=50",
+ "--entrypoints.https.http.encodequerysemicolons=true",
+ "--entryPoints.https.http2.maxConcurrentStreams=50",
+ "--providers.docker.exposedbydefault=false",
+ "--providers.file.directory=/traefik/dynamic/",
+ "--providers.file.watch=true",
+ "--certificatesresolvers.letsencrypt.acme.httpchallenge=true",
+ "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json",
+ "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http",
+ ],
+ "labels" => $labels,
],
- "networks" => $networks->toArray(),
- "ports" => [
- "80:80",
- "443:443",
- "8080:8080",
- ],
- "healthcheck" => [
- "test" => "wget -qO- http://localhost:80/ping || exit 1",
- "interval" => "4s",
- "timeout" => "2s",
- "retries" => 5,
- ],
- "volumes" => [
- "/var/run/docker.sock:/var/run/docker.sock:ro",
- "{$proxy_path}:/traefik",
- ],
- "command" => [
- "--ping=true",
- "--ping.entrypoint=http",
- "--api.dashboard=true",
- "--api.insecure=false",
- "--entrypoints.http.address=:80",
- "--entrypoints.https.address=:443",
- "--entrypoints.http.http.encodequerysemicolons=true",
- "--entryPoints.http.http2.maxConcurrentStreams=50",
- "--entrypoints.https.http.encodequerysemicolons=true",
- "--entryPoints.https.http2.maxConcurrentStreams=50",
- "--providers.docker.exposedbydefault=false",
- "--providers.file.directory=/traefik/dynamic/",
- "--providers.file.watch=true",
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true",
- "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json",
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http",
- ],
- "labels" => $labels,
],
- ],
- ];
- if (isDev()) {
- // $config['services']['traefik']['command'][] = "--log.level=debug";
- $config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
- $config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
- }
- if ($server->isSwarm()) {
- data_forget($config, 'services.traefik.container_name');
- data_forget($config, 'services.traefik.restart');
- data_forget($config, 'services.traefik.labels');
+ ];
+ if (isDev()) {
+ // $config['services']['traefik']['command'][] = "--log.level=debug";
+ $config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
+ $config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
+ }
+ if ($server->isSwarm()) {
+ data_forget($config, 'services.traefik.container_name');
+ data_forget($config, 'services.traefik.restart');
+ data_forget($config, 'services.traefik.labels');
- $config['services']['traefik']['command'][] = "--providers.docker.swarmMode=true";
- $config['services']['traefik']['deploy'] = [
- "labels" => $labels,
- "placement" => [
- "constraints" => [
- "node.role==manager",
+ $config['services']['traefik']['command'][] = "--providers.docker.swarmMode=true";
+ $config['services']['traefik']['deploy'] = [
+ "labels" => $labels,
+ "placement" => [
+ "constraints" => [
+ "node.role==manager",
+ ],
+ ],
+ ];
+ } else {
+ $config['services']['traefik']['command'][] = "--providers.docker=true";
+ }
+ } else if ($proxy_type === 'CADDY') {
+ $config = [
+ "version" => "3.8",
+ "networks" => $array_of_networks->toArray(),
+ "services" => [
+ "caddy" => [
+ "container_name" => "coolify-proxy",
+ "image" => "lucaslorentz/caddy-docker-proxy:2.8-alpine",
+ "restart" => RESTART_MODE,
+ "extra_hosts" => [
+ "host.docker.internal:host-gateway",
+ ],
+ "networks" => $networks->toArray(),
+ "ports" => [
+ "80:80",
+ "443:443",
+ ],
+ // "healthcheck" => [
+ // "test" => "wget -qO- http://localhost:80|| exit 1",
+ // "interval" => "4s",
+ // "timeout" => "2s",
+ // "retries" => 5,
+ // ],
+ "volumes" => [
+ "/var/run/docker.sock:/var/run/docker.sock:ro",
+ "{$proxy_path}/config:/config",
+ "{$proxy_path}/data:/data",
+ ],
],
],
];
} else {
- $config['services']['traefik']['command'][] = "--providers.docker=true";
+ return null;
}
+
$config = Yaml::dump($config, 12, 2);
SaveConfiguration::run($server, $config);
return $config;
}
function setup_dynamic_configuration()
{
- $dynamic_config_path = get_proxy_path() . "/dynamic";
$settings = InstanceSettings::get();
$server = Server::find(0);
+ $dynamic_config_path = $server->proxyPath() . "/dynamic";
if ($server) {
$file = "$dynamic_config_path/coolify.yaml";
if (empty($settings->fqdn)) {
@@ -308,7 +341,7 @@ function setup_dynamic_configuration()
}
function setup_default_redirect_404(string|null $redirect_url, Server $server)
{
- $traefik_dynamic_conf_path = get_proxy_path() . "/dynamic";
+ $traefik_dynamic_conf_path = $server->proxyPath() . "/dynamic";
$traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml";
if (empty($redirect_url)) {
instant_remote_process([
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 621251d36..359792ddc 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -1056,6 +1056,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
service_name: $serviceName
));
+ $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
+ network: $resource->destination->network,
+ uuid: $resource->uuid,
+ domains: $fqdns,
+ is_force_https_enabled: true,
+ serviceLabels: $serviceLabels,
+ is_gzip_enabled: $savedService->isGzipEnabled(),
+ is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
+ service_name: $serviceName
+ ));
}
}
if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) {
@@ -1495,7 +1505,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return $preview_fqdn;
});
}
- $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($uuid, $fqdns, serviceLabels: $serviceLabels));
+ $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
+ uuid: $uuid,
+ domains: $fqdns,
+ serviceLabels: $serviceLabels
+ ));
+ $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
+ network: $resource->destination->network,
+ uuid: $uuid,
+ domains: $fqdns,
+ serviceLabels: $serviceLabels
+ ));
}
}
}
diff --git a/config/sentry.php b/config/sentry.php
index 74eb9ba6a..2d2d440c3 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -3,7 +3,7 @@
return [
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
- 'dsn' => 'https://1bbc8f762199a52aee39196adb3e8d1a@o1082494.ingest.sentry.io/4505347448045568',
+ 'dsn' => 'https://f0b0e6be13926d4ac68d68d51d38db8f@o1082494.ingest.us.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'))
diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php
index a163bf05f..94c8c9755 100644
--- a/resources/views/components/server/sidebar.blade.php
+++ b/resources/views/components/server/sidebar.blade.php
@@ -4,11 +4,13 @@
href="{{ route('server.proxy', $parameters) }}">
Configuration
- @if (data_get($server, 'proxy.type') !== 'NONE')
-
- Dynamic Configurations
-
+ @if ($server->proxyType() !== 'NONE')
+ @if ($server->proxyType() === 'TRAEFIK_V2')
+
+ Dynamic Configurations
+
+ @endif
Logs
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php
index e87ad573a..7c6599792 100644
--- a/resources/views/livewire/server/proxy.blade.php
+++ b/resources/views/livewire/server/proxy.blade.php
@@ -1,16 +1,24 @@
@if (data_get($server, 'proxy.type'))
- @if ($selectedProxy === 'TRAEFIK_V2')
+ @if ($selectedProxy !== 'NONE')
@endif
-
+ @if ($server->proxyType() === 'TRAEFIK_V2')
+
+ @endif
@if ($proxy_settings)
-
Reset configuration to default
@@ -40,7 +51,7 @@
Configuration
Switch Proxy
-
Custom (None) Proxy Selected
+
Custom (None) Proxy Selected
@else
Configuration
@@ -57,14 +68,13 @@
Traefik
- v2
+
+
+ Caddy (experimental)
Nginx
-
- Caddy
-
@endif
From 1defed27a02a5b50c7afb176b9714bad1a5337be Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 15:19:04 +0100
Subject: [PATCH 32/97] Refactor compose file generation and add link to
documentation
---
app/Jobs/ApplicationDeploymentJob.php | 24 ++++++++++++++++++-
.../views/livewire/server/proxy.blade.php | 2 +-
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 0dedfd596..b9c60605f 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -1077,7 +1077,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
private function generate_compose_file()
{
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
-
+ $onlyPort = null;
+ if (count($ports) > 0) {
+ $onlyPort = $ports[0];
+ }
$persistent_storages = $this->generate_local_persistent_volumes();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables($ports);
@@ -1088,6 +1091,25 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$labels = $labels->filter(function ($value, $key) {
return !Str::startsWith($value, 'coolify.');
});
+ $found_caddy_labels = $labels->filter(function ($value, $key) {
+ return Str::startsWith($value, 'caddy_');
+ });
+ if ($found_caddy_labels->count() === 0) {
+ if ($this->pull_request_id !== 0) {
+ $domains = str(data_get($this->preview, 'fqdn'))->explode(',');
+ } else {
+ $domains = str(data_get($this->application, 'fqdn'))->explode(',');
+ }
+ $labels = $labels->merge(fqdnLabelsForCaddy(
+ network: $this->application->destination->network,
+ uuid: $this->application->uuid,
+ domains: $domains,
+ onlyPort: $onlyPort,
+ is_force_https_enabled: $this->application->isForceHttpsEnabled(),
+ is_gzip_enabled: $this->application->isGzipEnabled(),
+ is_stripprefix_enabled: $this->application->isStripprefixEnabled()
+ ));
+ }
$this->application->custom_labels = base64_encode($labels->implode("\n"));
$this->application->save();
} else {
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php
index 7c6599792..ba3909739 100644
--- a/resources/views/livewire/server/proxy.blade.php
+++ b/resources/views/livewire/server/proxy.blade.php
@@ -13,7 +13,7 @@
Save
- Before switching proxies, please read
this .
+ Before switching proxies, please read
this .
@if ($server->proxyType() === 'TRAEFIK_V2')
Traefik v2
@elseif ($server->proxyType() === 'CADDY')
From f24063cfea01dc9f02a04e676e5de7fd5d469baf Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 15:36:45 +0100
Subject: [PATCH 33/97] Add CADDY_DOCKER_POLLING_INTERVAL environment variable
---
bootstrap/helpers/proxy.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php
index 1edfaed2e..caa3e4fa0 100644
--- a/bootstrap/helpers/proxy.php
+++ b/bootstrap/helpers/proxy.php
@@ -186,6 +186,9 @@ function generate_default_proxy_configuration(Server $server)
"extra_hosts" => [
"host.docker.internal:host-gateway",
],
+ "environment" => [
+ "CADDY_DOCKER_POLLING_INTERVAL=5s",
+ ],
"networks" => $networks->toArray(),
"ports" => [
"80:80",
From 9bdad6bb672991959237fcc8909efcb92c1d106f Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 17:17:34 +0100
Subject: [PATCH 34/97] feat caddy dynamic configurations
---
app/Actions/Proxy/StartProxy.php | 2 +
app/Console/Commands/Init.php | 3 +-
app/Livewire/Settings/Configuration.php | 6 +-
app/Models/Server.php | 153 +++++++++++++++++++++++-
bootstrap/helpers/proxy.php | 127 +-------------------
5 files changed, 159 insertions(+), 132 deletions(-)
diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php
index 46ac816b4..a781ab442 100644
--- a/app/Actions/Proxy/StartProxy.php
+++ b/app/Actions/Proxy/StartProxy.php
@@ -37,8 +37,10 @@ class StartProxy
"echo 'Proxy started successfully.'"
]);
} else {
+ $caddfile = "import /dynamic/*.caddy";
$commands = $commands->merge([
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
+ "echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
"echo 'Creating required Docker Compose file.'",
"echo 'Pulling docker image.'",
'docker compose pull',
diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php
index c69d411dd..66a4f1aff 100644
--- a/app/Console/Commands/Init.php
+++ b/app/Console/Commands/Init.php
@@ -35,7 +35,8 @@ class Init extends Command
$this->call('cleanup:queue');
$this->call('cleanup:stucked-resources');
try {
- setup_dynamic_configuration();
+ $server = Server::find(0)->first();
+ $server->setupDynamicProxyConfiguration();
} catch (\Throwable $e) {
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
}
diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php
index bafc82447..f1c732a97 100644
--- a/app/Livewire/Settings/Configuration.php
+++ b/app/Livewire/Settings/Configuration.php
@@ -2,12 +2,9 @@
namespace App\Livewire\Settings;
-use App\Jobs\ContainerStatusJob;
use App\Models\InstanceSettings as ModelsInstanceSettings;
use App\Models\Server;
use Livewire\Component;
-use Spatie\Url\Url;
-use Symfony\Component\Yaml\Yaml;
class Configuration extends Component
{
@@ -84,7 +81,6 @@ class Configuration extends Component
private function setup_instance_fqdn()
{
- setup_dynamic_configuration();
-
+ $this->server->setupDynamicProxyConfiguration();
}
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index bc65cbdba..d2c412d9e 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -15,6 +15,8 @@ use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
use Illuminate\Support\Str;
use Illuminate\Support\Stringable;
+use Spatie\Url\Url;
+use Symfony\Component\Yaml\Yaml;
class Server extends BaseModel
{
@@ -118,7 +120,156 @@ class Server extends BaseModel
}
}
}
- public function proxyPath() {
+ public function setupDynamicProxyConfiguration()
+ {
+ $settings = InstanceSettings::get();
+ $dynamic_config_path = $this->proxyPath() . "/dynamic";
+ if ($this) {
+ if ($this->proxyType() === 'TRAEFIK_V2') {
+ $file = "$dynamic_config_path/coolify.yaml";
+ if (empty($settings->fqdn)) {
+ instant_remote_process([
+ "rm -f $file",
+ ], $this);
+ } else {
+ $url = Url::fromString($settings->fqdn);
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $traefik_dynamic_conf = [
+ 'http' =>
+ [
+ 'middlewares' => [
+ 'redirect-to-https' => [
+ 'redirectscheme' => [
+ 'scheme' => 'https',
+ ],
+ ],
+ 'gzip' => [
+ 'compress' => true,
+ ],
+ ],
+ 'routers' =>
+ [
+ 'coolify-http' =>
+ [
+ 'middlewares' => [
+ 0 => 'gzip',
+ ],
+ 'entryPoints' => [
+ 0 => 'http',
+ ],
+ 'service' => 'coolify',
+ 'rule' => "Host(`{$host}`)",
+ ],
+ 'coolify-realtime-ws' =>
+ [
+ 'entryPoints' => [
+ 0 => 'http',
+ ],
+ 'service' => 'coolify-realtime',
+ 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
+ ],
+ ],
+ 'services' =>
+ [
+ 'coolify' =>
+ [
+ 'loadBalancer' =>
+ [
+ 'servers' =>
+ [
+ 0 =>
+ [
+ 'url' => 'http://coolify:80',
+ ],
+ ],
+ ],
+ ],
+ 'coolify-realtime' =>
+ [
+ 'loadBalancer' =>
+ [
+ 'servers' =>
+ [
+ 0 =>
+ [
+ 'url' => 'http://coolify-realtime:6001',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ if ($schema === 'https') {
+ $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
+ 0 => 'redirect-to-https',
+ ];
+
+ $traefik_dynamic_conf['http']['routers']['coolify-https'] = [
+ 'entryPoints' => [
+ 0 => 'https',
+ ],
+ 'service' => 'coolify',
+ 'rule' => "Host(`{$host}`)",
+ 'tls' => [
+ 'certresolver' => 'letsencrypt',
+ ],
+ ];
+ $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [
+ 'entryPoints' => [
+ 0 => 'https',
+ ],
+ 'service' => 'coolify-realtime',
+ 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
+ 'tls' => [
+ 'certresolver' => 'letsencrypt',
+ ],
+ ];
+ }
+ $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
+ $yaml =
+ "# This file is automatically generated by Coolify.\n" .
+ "# Do not edit it manually (only if you know what are you doing).\n\n" .
+ $yaml;
+
+ $base64 = base64_encode($yaml);
+ instant_remote_process([
+ "mkdir -p $dynamic_config_path",
+ "echo '$base64' | base64 -d > $file",
+ ], $this);
+
+ if (config('app.env') == 'local') {
+ // ray($yaml);
+ }
+ }
+ } else if ($this->proxyType() === 'CADDY') {
+ $file = "$dynamic_config_path/coolify.caddy";
+ if (empty($settings->fqdn)) {
+ instant_remote_process([
+ "rm -f $file",
+ "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile",
+ ], $this);
+ } else {
+ $url = Url::fromString($settings->fqdn);
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $caddy_file = "
+$schema://$host {
+ reverse_proxy coolify:80
+}";
+ $base64 = base64_encode($caddy_file);
+ instant_remote_process([
+ "echo '$base64' | base64 -d > $file",
+ "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile",
+ ], $this);
+ }
+ }
+ }
+ }
+ public function proxyPath()
+ {
$base_path = config('coolify.base_config_path');
$proxyType = $this->proxyType();
$proxy_path = "$base_path/proxy";
diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php
index caa3e4fa0..2bdffaf63 100644
--- a/bootstrap/helpers/proxy.php
+++ b/bootstrap/helpers/proxy.php
@@ -188,6 +188,7 @@ function generate_default_proxy_configuration(Server $server)
],
"environment" => [
"CADDY_DOCKER_POLLING_INTERVAL=5s",
+ "CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile",
],
"networks" => $networks->toArray(),
"ports" => [
@@ -202,6 +203,7 @@ function generate_default_proxy_configuration(Server $server)
// ],
"volumes" => [
"/var/run/docker.sock:/var/run/docker.sock:ro",
+ "{$proxy_path}/dynamic:/dynamic",
"{$proxy_path}/config:/config",
"{$proxy_path}/data:/data",
],
@@ -216,132 +218,7 @@ function generate_default_proxy_configuration(Server $server)
SaveConfiguration::run($server, $config);
return $config;
}
-function setup_dynamic_configuration()
-{
- $settings = InstanceSettings::get();
- $server = Server::find(0);
- $dynamic_config_path = $server->proxyPath() . "/dynamic";
- if ($server) {
- $file = "$dynamic_config_path/coolify.yaml";
- if (empty($settings->fqdn)) {
- instant_remote_process([
- "rm -f $file",
- ], $server);
- } else {
- $url = Url::fromString($settings->fqdn);
- $host = $url->getHost();
- $schema = $url->getScheme();
- $traefik_dynamic_conf = [
- 'http' =>
- [
- 'middlewares' => [
- 'redirect-to-https' => [
- 'redirectscheme' => [
- 'scheme' => 'https',
- ],
- ],
- 'gzip' => [
- 'compress' => true,
- ],
- ],
- 'routers' =>
- [
- 'coolify-http' =>
- [
- 'middlewares' => [
- 0 => 'gzip',
- ],
- 'entryPoints' => [
- 0 => 'http',
- ],
- 'service' => 'coolify',
- 'rule' => "Host(`{$host}`)",
- ],
- 'coolify-realtime-ws' =>
- [
- 'entryPoints' => [
- 0 => 'http',
- ],
- 'service' => 'coolify-realtime',
- 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
- ],
- ],
- 'services' =>
- [
- 'coolify' =>
- [
- 'loadBalancer' =>
- [
- 'servers' =>
- [
- 0 =>
- [
- 'url' => 'http://coolify:80',
- ],
- ],
- ],
- ],
- 'coolify-realtime' =>
- [
- 'loadBalancer' =>
- [
- 'servers' =>
- [
- 0 =>
- [
- 'url' => 'http://coolify-realtime:6001',
- ],
- ],
- ],
- ],
- ],
- ],
- ];
- if ($schema === 'https') {
- $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
- 0 => 'redirect-to-https',
- ];
-
- $traefik_dynamic_conf['http']['routers']['coolify-https'] = [
- 'entryPoints' => [
- 0 => 'https',
- ],
- 'service' => 'coolify',
- 'rule' => "Host(`{$host}`)",
- 'tls' => [
- 'certresolver' => 'letsencrypt',
- ],
- ];
- $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [
- 'entryPoints' => [
- 0 => 'https',
- ],
- 'service' => 'coolify-realtime',
- 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
- 'tls' => [
- 'certresolver' => 'letsencrypt',
- ],
- ];
- }
- $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
- $yaml =
- "# This file is automatically generated by Coolify.\n" .
- "# Do not edit it manually (only if you know what are you doing).\n\n" .
- $yaml;
-
- $base64 = base64_encode($yaml);
- instant_remote_process([
- "mkdir -p $dynamic_config_path",
- "echo '$base64' | base64 -d > $file",
- ], $server);
-
- if (config('app.env') == 'local') {
- // ray($yaml);
- }
- }
- }
-}
function setup_default_redirect_404(string|null $redirect_url, Server $server)
{
$traefik_dynamic_conf_path = $server->proxyPath() . "/dynamic";
From 14908280691422d290eb1565557400260a0b5cf4 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 17:31:28 +0100
Subject: [PATCH 35/97] feat: dynamic configuration for caddy
---
.../Proxy/DynamicConfigurationNavbar.php | 8 ++++
.../Server/Proxy/NewDynamicConfiguration.php | 37 +++++++++++++------
app/Models/Server.php | 10 ++++-
.../views/components/server/sidebar.blade.php | 4 +-
.../proxy/dynamic-configurations.blade.php | 4 +-
.../proxy/new-dynamic-configuration.blade.php | 2 +-
6 files changed, 46 insertions(+), 19 deletions(-)
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
index aee61f7de..a9c01daed 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
@@ -15,8 +15,16 @@ class DynamicConfigurationNavbar extends Component
{
$server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first();
$proxy_path = $server->proxyPath();
+ $proxy_type = $server->proxyType();
$file = str_replace('|', '.', $fileName);
+ if ($proxy_type === 'CADDY' && $file === "Caddyfile") {
+ $this->dispatch('error', 'Cannot delete Caddyfile.');
+ return;
+ }
instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server);
+ if ($proxy_type === 'CADDY') {
+ $server->reloadCaddy();
+ }
$this->dispatch('success', 'File deleted.');
$this->dispatch('loadDynamicConfigurations');
$this->dispatch('refresh');
diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
index 302e603b3..06180d947 100644
--- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
+++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
@@ -29,7 +29,6 @@ class NewDynamicConfiguration extends Component
'fileName' => 'required',
'value' => 'required',
]);
-
if (data_get($this->parameters, 'server_uuid')) {
$this->server = Server::ownedByCurrentTeam()->whereUuid(data_get($this->parameters, 'server_uuid'))->first();
}
@@ -39,14 +38,21 @@ class NewDynamicConfiguration extends Component
if (is_null($this->server)) {
return redirect()->route('server.index');
}
- if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) {
- $this->fileName = "{$this->fileName}.yaml";
+ $proxy_type = $this->server->proxyType();
+ if ($proxy_type === 'TRAEFIK_V2') {
+ if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) {
+ $this->fileName = "{$this->fileName}.yaml";
+ }
+ if ($this->fileName === 'coolify.yaml') {
+ $this->dispatch('error', 'File name is reserved.');
+ return;
+ }
+ } else if ($proxy_type === 'CADDY') {
+ if (!str($this->fileName)->endsWith('.caddy')) {
+ $this->fileName = "{$this->fileName}.caddy";
+ }
}
- if ($this->fileName === 'coolify.yaml') {
- $this->dispatch('error', 'File name is reserved.');
- return;
- }
- $proxy_path = $this->proxyPath();
+ $proxy_path = $this->server->proxyPath();
$file = "{$proxy_path}/dynamic/{$this->fileName}";
if ($this->newFile) {
$exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server);
@@ -55,11 +61,18 @@ class NewDynamicConfiguration extends Component
return;
}
}
- $yaml = Yaml::parse($this->value);
- $yaml = Yaml::dump($yaml, 10, 2);
- $this->value = $yaml;
+ if ($proxy_type === 'TRAEFIK_V2') {
+ $yaml = Yaml::parse($this->value);
+ $yaml = Yaml::dump($yaml, 10, 2);
+ $this->value = $yaml;
+ }
$base64_value = base64_encode($this->value);
- instant_remote_process(["echo '{$base64_value}' | base64 -d > {$file}"], $this->server);
+ instant_remote_process([
+ "echo '{$base64_value}' | base64 -d > {$file}",
+ ], $this->server);
+ if ($proxy_type === 'CADDY') {
+ $this->server->reloadCaddy();
+ }
$this->dispatch('loadDynamicConfigurations');
$this->dispatch('dynamic-configuration-added');
$this->dispatch('success', 'Dynamic configuration saved.');
diff --git a/app/Models/Server.php b/app/Models/Server.php
index d2c412d9e..3cf20d7ae 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -249,8 +249,9 @@ class Server extends BaseModel
if (empty($settings->fqdn)) {
instant_remote_process([
"rm -f $file",
- "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile",
], $this);
+ $this->reloadCaddy();
+
} else {
$url = Url::fromString($settings->fqdn);
$host = $url->getHost();
@@ -262,12 +263,17 @@ $schema://$host {
$base64 = base64_encode($caddy_file);
instant_remote_process([
"echo '$base64' | base64 -d > $file",
- "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile",
], $this);
+ $this->reloadCaddy();
}
}
}
}
+ public function reloadCaddy() {
+ return instant_remote_process([
+ "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile",
+ ], $this);
+ }
public function proxyPath()
{
$base_path = config('coolify.base_config_path');
diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php
index 94c8c9755..ef2b4b8e7 100644
--- a/resources/views/components/server/sidebar.blade.php
+++ b/resources/views/components/server/sidebar.blade.php
@@ -5,12 +5,12 @@
Configuration
@if ($server->proxyType() !== 'NONE')
- @if ($server->proxyType() === 'TRAEFIK_V2')
+ {{-- @if ($server->proxyType() === 'TRAEFIK_V2') --}}
Dynamic Configurations
- @endif
+ {{-- @endif --}}
Logs
diff --git a/resources/views/livewire/server/proxy/dynamic-configurations.blade.php b/resources/views/livewire/server/proxy/dynamic-configurations.blade.php
index cb788dda4..0fd92f41e 100644
--- a/resources/views/livewire/server/proxy/dynamic-configurations.blade.php
+++ b/resources/views/livewire/server/proxy/dynamic-configurations.blade.php
@@ -19,7 +19,7 @@
Add
- You can add dynamic Traefik configurations here.
+ You can add dynamic proxy configurations here.
@@ -29,7 +29,7 @@
@if ($contents?->isNotEmpty())
@foreach ($contents as $fileName => $value)
- @if (str_replace('|', '.', $fileName) === 'coolify.yaml')
+ @if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||str_replace('|', '.', $fileName) === 'Caddyfile' )
File: {{ str_replace('|', '.', $fileName) }}
diff --git a/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php b/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php
index 9e98b0ed4..110ba289c 100644
--- a/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php
+++ b/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php
@@ -1,5 +1,5 @@
From 52120e7a380b2a744de6255cc510235316f60469 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 20:17:37 +0100
Subject: [PATCH 36/97] Add dynamic proxy configuration setup in StartProxy.php
and update proxyPath() in Server.php
---
app/Actions/Proxy/StartProxy.php | 2 ++
app/Models/Server.php | 3 +++
2 files changed, 5 insertions(+)
diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php
index a781ab442..705197586 100644
--- a/app/Actions/Proxy/StartProxy.php
+++ b/app/Actions/Proxy/StartProxy.php
@@ -66,6 +66,8 @@ class StartProxy
} catch (\Throwable $e) {
ray($e);
throw $e;
+ } finally {
+ $server->setupDynamicProxyConfiguration();
}
}
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 3cf20d7ae..b110189bb 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -279,6 +279,9 @@ $schema://$host {
$base_path = config('coolify.base_config_path');
$proxyType = $this->proxyType();
$proxy_path = "$base_path/proxy";
+ // TODO: should use /traefik for already exisiting configurations?
+ // Should move everything except /caddy and /nginx to /traefik
+ // The code needs to be modified as well, so maybe it does not worth it
if ($proxyType === ProxyTypes::TRAEFIK_V2->value) {
$proxy_path = $proxy_path;
} else if ($proxyType === ProxyTypes::CADDY->value) {
From 8eacf677254adc5b24c030a12bab89b3e68c23da Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 20:25:35 +0100
Subject: [PATCH 37/97] Remove unnecessary code in StartProxy.php
---
app/Actions/Proxy/StartProxy.php | 2 --
1 file changed, 2 deletions(-)
diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php
index 705197586..a781ab442 100644
--- a/app/Actions/Proxy/StartProxy.php
+++ b/app/Actions/Proxy/StartProxy.php
@@ -66,8 +66,6 @@ class StartProxy
} catch (\Throwable $e) {
ray($e);
throw $e;
- } finally {
- $server->setupDynamicProxyConfiguration();
}
}
}
From 6950966b06f70b9acc0436984b56fa745a7a5f26 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Mon, 11 Mar 2024 20:39:41 +0100
Subject: [PATCH 38/97] Commented out reloadCaddy() calls in
DynamicConfigurationNavbar.php, NewDynamicConfiguration.php, and Server.php
---
app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php | 2 +-
app/Livewire/Server/Proxy/NewDynamicConfiguration.php | 2 +-
app/Models/Server.php | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
index a9c01daed..d27e574d7 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
@@ -23,7 +23,7 @@ class DynamicConfigurationNavbar extends Component
}
instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server);
if ($proxy_type === 'CADDY') {
- $server->reloadCaddy();
+ // $server->reloadCaddy();
}
$this->dispatch('success', 'File deleted.');
$this->dispatch('loadDynamicConfigurations');
diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
index 06180d947..c433ce4fc 100644
--- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
+++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
@@ -71,7 +71,7 @@ class NewDynamicConfiguration extends Component
"echo '{$base64_value}' | base64 -d > {$file}",
], $this->server);
if ($proxy_type === 'CADDY') {
- $this->server->reloadCaddy();
+ // $this->server->reloadCaddy();
}
$this->dispatch('loadDynamicConfigurations');
$this->dispatch('dynamic-configuration-added');
diff --git a/app/Models/Server.php b/app/Models/Server.php
index b110189bb..098b19931 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -250,7 +250,7 @@ class Server extends BaseModel
instant_remote_process([
"rm -f $file",
], $this);
- $this->reloadCaddy();
+ // $this->reloadCaddy();
} else {
$url = Url::fromString($settings->fqdn);
@@ -264,7 +264,7 @@ $schema://$host {
instant_remote_process([
"echo '$base64' | base64 -d > $file",
], $this);
- $this->reloadCaddy();
+ // $this->reloadCaddy();
}
}
}
From b576014d07e5c7d9c7af4a94f48f5e5b31fd6081 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 10:42:56 +0100
Subject: [PATCH 39/97] fix: reload caddy issue
---
app/Livewire/Server/Proxy.php | 6 +-
app/Models/Server.php | 110 +++++++++++++++++-
bootstrap/helpers/proxy.php | 77 ------------
.../views/livewire/server/proxy.blade.php | 10 +-
4 files changed, 113 insertions(+), 90 deletions(-)
diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php
index 1e23605ff..4ccbf7d64 100644
--- a/app/Livewire/Server/Proxy.php
+++ b/app/Livewire/Server/Proxy.php
@@ -54,8 +54,7 @@ class Proxy extends Component
SaveConfiguration::run($this->server, $this->proxy_settings);
$this->server->proxy->redirect_url = $this->redirect_url;
$this->server->save();
-
- setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
+ $this->server->setupDefault404Redirect();
$this->dispatch('success', 'Proxy configuration saved.');
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -66,6 +65,9 @@ class Proxy extends Component
{
try {
$this->proxy_settings = CheckConfiguration::run($this->server, true);
+ SaveConfiguration::run($this->server, $this->proxy_settings);
+ $this->server->save();
+ $this->dispatch('success', 'Proxy configuration saved.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 098b19931..f067523df 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -120,6 +120,106 @@ class Server extends BaseModel
}
}
}
+ public function setupDefault404Redirect()
+ {
+ $dynamic_conf_path = $this->proxyPath() . "/dynamic";
+ $proxy_type = $this->proxyType();
+ $redirect_url = $this->proxy->redirect_url;
+
+ if ($proxy_type === 'TRAEFIK_V2') {
+ $default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
+ } else if ($proxy_type === 'CADDY') {
+ $default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy";
+ }
+ if (empty($redirect_url)) {
+ instant_remote_process([
+ "mkdir -p $dynamic_conf_path",
+ "rm -f $default_redirect_file",
+ ], $this);
+ return;
+ }
+ if ($proxy_type === 'TRAEFIK_V2') {
+ $dynamic_conf = [
+ 'http' =>
+ [
+ 'routers' =>
+ [
+ 'catchall' =>
+ [
+ 'entryPoints' => [
+ 0 => 'http',
+ 1 => 'https',
+ ],
+ 'service' => 'noop',
+ 'rule' => "HostRegexp(`{catchall:.*}`)",
+ 'priority' => 1,
+ 'middlewares' => [
+ 0 => 'redirect-regexp@file',
+ ],
+ ],
+ ],
+ 'services' =>
+ [
+ 'noop' =>
+ [
+ 'loadBalancer' =>
+ [
+ 'servers' =>
+ [
+ 0 =>
+ [
+ 'url' => '',
+ ],
+ ],
+ ],
+ ],
+ ],
+ 'middlewares' =>
+ [
+ 'redirect-regexp' =>
+ [
+ 'redirectRegex' =>
+ [
+ 'regex' => '(.*)',
+ 'replacement' => $redirect_url,
+ 'permanent' => false,
+ ],
+ ],
+ ],
+ ],
+ ];
+ $conf = Yaml::dump($dynamic_conf, 12, 2);
+ $conf =
+ "# This file is automatically generated by Coolify.\n" .
+ "# Do not edit it manually (only if you know what are you doing).\n\n" .
+ $conf;
+
+ $base64 = base64_encode($conf);
+ } else if ($proxy_type === 'CADDY') {
+ $conf = ":80, :443 {
+ redir $redirect_url
+}";
+ray($conf);
+ $conf =
+ "# This file is automatically generated by Coolify.\n" .
+ "# Do not edit it manually (only if you know what are you doing).\n\n" .
+ $conf;
+ $base64 = base64_encode($conf);
+ }
+
+
+ instant_remote_process([
+ "mkdir -p $dynamic_conf_path",
+ "echo '$base64' | base64 -d > $default_redirect_file",
+ ], $this);
+
+ if (config('app.env') == 'local') {
+ ray($conf);
+ }
+ if ($proxy_type === 'CADDY') {
+ $this->reloadCaddy();
+ }
+ }
public function setupDynamicProxyConfiguration()
{
$settings = InstanceSettings::get();
@@ -250,8 +350,7 @@ class Server extends BaseModel
instant_remote_process([
"rm -f $file",
], $this);
- // $this->reloadCaddy();
-
+ $this->reloadCaddy();
} else {
$url = Url::fromString($settings->fqdn);
$host = $url->getHost();
@@ -264,14 +363,15 @@ $schema://$host {
instant_remote_process([
"echo '$base64' | base64 -d > $file",
], $this);
- // $this->reloadCaddy();
+ $this->reloadCaddy();
}
}
}
}
- public function reloadCaddy() {
+ public function reloadCaddy()
+ {
return instant_remote_process([
- "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile",
+ "docker exec coolify-proxy caddy reload --config /config/caddy/Caddyfile.autosave",
], $this);
}
public function proxyPath()
diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php
index 2bdffaf63..1eea1893e 100644
--- a/bootstrap/helpers/proxy.php
+++ b/bootstrap/helpers/proxy.php
@@ -218,80 +218,3 @@ function generate_default_proxy_configuration(Server $server)
SaveConfiguration::run($server, $config);
return $config;
}
-
-function setup_default_redirect_404(string|null $redirect_url, Server $server)
-{
- $traefik_dynamic_conf_path = $server->proxyPath() . "/dynamic";
- $traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml";
- if (empty($redirect_url)) {
- instant_remote_process([
- "mkdir -p $traefik_dynamic_conf_path",
- "rm -f $traefik_default_redirect_file",
- ], $server);
- } else {
- $traefik_dynamic_conf = [
- 'http' =>
- [
- 'routers' =>
- [
- 'catchall' =>
- [
- 'entryPoints' => [
- 0 => 'http',
- 1 => 'https',
- ],
- 'service' => 'noop',
- 'rule' => "HostRegexp(`{catchall:.*}`)",
- 'priority' => 1,
- 'middlewares' => [
- 0 => 'redirect-regexp@file',
- ],
- ],
- ],
- 'services' =>
- [
- 'noop' =>
- [
- 'loadBalancer' =>
- [
- 'servers' =>
- [
- 0 =>
- [
- 'url' => '',
- ],
- ],
- ],
- ],
- ],
- 'middlewares' =>
- [
- 'redirect-regexp' =>
- [
- 'redirectRegex' =>
- [
- 'regex' => '(.*)',
- 'replacement' => $redirect_url,
- 'permanent' => false,
- ],
- ],
- ],
- ],
- ];
- $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
- $yaml =
- "# This file is automatically generated by Coolify.\n" .
- "# Do not edit it manually (only if you know what are you doing).\n\n" .
- $yaml;
-
- $base64 = base64_encode($yaml);
- instant_remote_process([
- "mkdir -p $traefik_dynamic_conf_path",
- "echo '$base64' | base64 -d > $traefik_default_redirect_file",
- ], $server);
-
- if (config('app.env') == 'local') {
- ray($yaml);
- }
- }
-}
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php
index ba3909739..60828ec3d 100644
--- a/resources/views/livewire/server/proxy.blade.php
+++ b/resources/views/livewire/server/proxy.blade.php
@@ -13,7 +13,8 @@
Save
-
Before switching proxies, please read
this .
+
Before switching proxies, please read
this .
@if ($server->proxyType() === 'TRAEFIK_V2')
Traefik v2
@elseif ($server->proxyType() === 'CADDY')
@@ -26,11 +27,8 @@
configurations.
@endif
- @if ($server->proxyType() === 'TRAEFIK_V2')
-
- @endif
+
From 6ef79f52138e003c033718f81cf822d830b6a062 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 10:57:07 +0100
Subject: [PATCH 40/97] Refactor database cleanup command to include dry-run
mode
---
app/Console/Commands/CleanupDatabase.php | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php
index 60e6ea901..110e49c49 100644
--- a/app/Console/Commands/CleanupDatabase.php
+++ b/app/Console/Commands/CleanupDatabase.php
@@ -12,9 +12,13 @@ class CleanupDatabase extends Command
public function handle()
{
- echo "Running database cleanup...\n";
+ if ($this->option('yes')) {
+ echo "Running database cleanup...\n";
+ } else {
+ echo "Running database cleanup in dry-run mode...\n";
+ }
$keep_days = 60;
-
+ echo "Keep days: $keep_days\n";
// Cleanup failed jobs table
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(7));
$count = $failed_jobs->count();
From 85c36df2a3f5aca70b05d8fe16e637a9b5fbaa6d Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 10:58:28 +0100
Subject: [PATCH 41/97] Refactor database cleanup queries to keep the last 10
entries
---
app/Console/Commands/CleanupDatabase.php | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php
index 110e49c49..0c8f9b884 100644
--- a/app/Console/Commands/CleanupDatabase.php
+++ b/app/Console/Commands/CleanupDatabase.php
@@ -36,7 +36,8 @@ class CleanupDatabase extends Command
}
// Cleanup activity_log table
- $activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days));
+ // but keep the last 10
+ $activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
$count = $activity_log->count();
echo "Delete $count entries from activity_log.\n";
if ($this->option('yes')) {
@@ -44,7 +45,7 @@ class CleanupDatabase extends Command
}
// Cleanup application_deployment_queues table
- $application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days));
+ $application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
$count = $application_deployment_queues->count();
echo "Delete $count entries from application_deployment_queues.\n";
if ($this->option('yes')) {
@@ -52,7 +53,7 @@ class CleanupDatabase extends Command
}
// Cleanup webhooks table
- $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days));
+ $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
$count = $webhooks->count();
echo "Delete $count entries from webhooks.\n";
if ($this->option('yes')) {
From b3d15f91e49fc28820326fa53d2351a1f66f292d Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 10:58:31 +0100
Subject: [PATCH 42/97] Remove skip(10) from activity_log and webhooks cleanup
---
app/Console/Commands/CleanupDatabase.php | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php
index 0c8f9b884..495b365ee 100644
--- a/app/Console/Commands/CleanupDatabase.php
+++ b/app/Console/Commands/CleanupDatabase.php
@@ -36,7 +36,6 @@ class CleanupDatabase extends Command
}
// Cleanup activity_log table
- // but keep the last 10
$activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
$count = $activity_log->count();
echo "Delete $count entries from activity_log.\n";
@@ -53,7 +52,7 @@ class CleanupDatabase extends Command
}
// Cleanup webhooks table
- $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
+ $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days));
$count = $webhooks->count();
echo "Delete $count entries from webhooks.\n";
if ($this->option('yes')) {
From 2509406d1c1c80a17ff87f7b2f2a4958e3e831d3 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 11:22:02 +0100
Subject: [PATCH 43/97] fix: startproxy event fix: add data to async remove
processes
---
.../CoolifyTask/PrepareCoolifyTask.php | 2 +-
app/Actions/CoolifyTask/RunRemoteProcess.php | 18 ++++++++++++----
app/Actions/Proxy/StartProxy.php | 5 +++--
app/Data/CoolifyTaskArgs.php | 1 +
app/Events/ProxyStarted.php | 16 ++++++++++++++
app/Jobs/CoolifyTask.php | 6 ++++--
app/Listeners/ProxyStartedNotification.php | 21 +++++++++++++++++++
app/Providers/EventServiceProvider.php | 8 ++++---
bootstrap/helpers/remoteProcess.php | 4 +++-
.../views/livewire/server/proxy.blade.php | 5 ++++-
10 files changed, 72 insertions(+), 14 deletions(-)
create mode 100644 app/Events/ProxyStarted.php
create mode 100644 app/Listeners/ProxyStartedNotification.php
diff --git a/app/Actions/CoolifyTask/PrepareCoolifyTask.php b/app/Actions/CoolifyTask/PrepareCoolifyTask.php
index b5b5a8853..e6a549756 100644
--- a/app/Actions/CoolifyTask/PrepareCoolifyTask.php
+++ b/app/Actions/CoolifyTask/PrepareCoolifyTask.php
@@ -39,7 +39,7 @@ class PrepareCoolifyTask
public function __invoke(): Activity
{
- $job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish);
+ $job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data);
dispatch($job);
$this->activity->refresh();
return $this->activity;
diff --git a/app/Actions/CoolifyTask/RunRemoteProcess.php b/app/Actions/CoolifyTask/RunRemoteProcess.php
index 327d46c68..737b4babe 100644
--- a/app/Actions/CoolifyTask/RunRemoteProcess.php
+++ b/app/Actions/CoolifyTask/RunRemoteProcess.php
@@ -21,6 +21,8 @@ class RunRemoteProcess
public $call_event_on_finish = null;
+ public $call_event_data = null;
+
protected $time_start;
protected $current_time;
@@ -34,7 +36,7 @@ class RunRemoteProcess
/**
* Create a new job instance.
*/
- public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null)
+ public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null)
{
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
@@ -45,6 +47,7 @@ class RunRemoteProcess
$this->hide_from_output = $hide_from_output;
$this->ignore_errors = $ignore_errors;
$this->call_event_on_finish = $call_event_on_finish;
+ $this->call_event_data = $call_event_data;
}
public static function decodeOutput(?Activity $activity = null): string
@@ -111,9 +114,16 @@ class RunRemoteProcess
}
if ($this->call_event_on_finish) {
try {
- event(resolve("App\\Events\\$this->call_event_on_finish", [
- 'userId' => $this->activity->causer_id,
- ]));
+ ray($this->call_event_data);
+ if ($this->call_event_data) {
+ event(resolve("App\\Events\\$this->call_event_on_finish", [
+ "data" => $this->call_event_data,
+ ]));
+ } else {
+ event(resolve("App\\Events\\$this->call_event_on_finish", [
+ 'userId' => $this->activity->causer_id,
+ ]));
+ }
} catch (\Throwable $e) {
ray($e);
}
diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php
index a781ab442..ab635fc65 100644
--- a/app/Actions/Proxy/StartProxy.php
+++ b/app/Actions/Proxy/StartProxy.php
@@ -2,7 +2,7 @@
namespace App\Actions\Proxy;
-use App\Events\ProxyStatusChanged;
+use App\Events\ProxyStarted;
use App\Models\Server;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -54,13 +54,14 @@ class StartProxy
}
if ($async) {
- $activity = remote_process($commands, $server);
+ $activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
return $activity;
} else {
instant_remote_process($commands, $server);
$server->proxy->set('status', 'running');
$server->proxy->set('type', $proxyType);
$server->save();
+ ProxyStarted::dispatch($server);
return 'OK';
}
} catch (\Throwable $e) {
diff --git a/app/Data/CoolifyTaskArgs.php b/app/Data/CoolifyTaskArgs.php
index cc717561f..e1e43f2f0 100644
--- a/app/Data/CoolifyTaskArgs.php
+++ b/app/Data/CoolifyTaskArgs.php
@@ -21,6 +21,7 @@ class CoolifyTaskArgs extends Data
public ?string $status = null ,
public bool $ignore_errors = false,
public $call_event_on_finish = null,
+ public $call_event_data = null
) {
if(is_null($status)){
$this->status = ProcessStatus::QUEUED->value;
diff --git a/app/Events/ProxyStarted.php b/app/Events/ProxyStarted.php
new file mode 100644
index 000000000..c77fe08b0
--- /dev/null
+++ b/app/Events/ProxyStarted.php
@@ -0,0 +1,16 @@
+ $this->activity,
'ignore_errors' => $this->ignore_errors,
- 'call_event_on_finish' => $this->call_event_on_finish
+ 'call_event_on_finish' => $this->call_event_on_finish,
+ 'call_event_data' => $this->call_event_data
]);
$remote_process();
diff --git a/app/Listeners/ProxyStartedNotification.php b/app/Listeners/ProxyStartedNotification.php
new file mode 100644
index 000000000..62131c22a
--- /dev/null
+++ b/app/Listeners/ProxyStartedNotification.php
@@ -0,0 +1,21 @@
+server = data_get($event, 'server');
+ $this->server->setupDefault404Redirect();
+ $this->server->setupDynamicProxyConfiguration();
+ }
+}
diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php
index a9b4496b4..0e9be72d1 100644
--- a/app/Providers/EventServiceProvider.php
+++ b/app/Providers/EventServiceProvider.php
@@ -2,8 +2,10 @@
namespace App\Providers;
+use App\Events\ProxyStarted;
use App\Listeners\MaintenanceModeDisabledNotification;
use App\Listeners\MaintenanceModeEnabledNotification;
+use App\Listeners\ProxyStartedNotification;
use Illuminate\Foundation\Events\MaintenanceModeDisabled;
use Illuminate\Foundation\Events\MaintenanceModeEnabled;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@@ -17,9 +19,9 @@ class EventServiceProvider extends ServiceProvider
MaintenanceModeDisabled::class => [
MaintenanceModeDisabledNotification::class,
],
- // Registered::class => [
- // SendEmailVerificationNotification::class,
- // ],
+ ProxyStarted::class => [
+ ProxyStartedNotification::class,
+ ],
];
public function boot(): void
{
diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php
index 13905391e..0aba82ea1 100644
--- a/bootstrap/helpers/remoteProcess.php
+++ b/bootstrap/helpers/remoteProcess.php
@@ -24,7 +24,8 @@ function remote_process(
?string $type_uuid = null,
?Model $model = null,
bool $ignore_errors = false,
- $callEventOnFinish = null
+ $callEventOnFinish = null,
+ $callEventData = null
): Activity {
if (is_null($type)) {
$type = ActivityTypes::INLINE->value;
@@ -50,6 +51,7 @@ function remote_process(
model: $model,
ignore_errors: $ignore_errors,
call_event_on_finish: $callEventOnFinish,
+ call_event_data: $callEventData,
),
])();
}
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php
index 60828ec3d..49bf654a6 100644
--- a/resources/views/livewire/server/proxy.blade.php
+++ b/resources/views/livewire/server/proxy.blade.php
@@ -13,7 +13,10 @@
Save
-
@if ($server->proxyType() === 'TRAEFIK_V2')
Traefik v2
From f8055e7976d2baed68d1485fd81ff19d40ace4eb Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 11:32:40 +0100
Subject: [PATCH 44/97] fix: /realtime endpoint
---
routes/api.php | 1 -
routes/web.php | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/routes/api.php b/routes/api.php
index c0ea836a6..c7e3598a3 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -2,7 +2,6 @@
use App\Http\Controllers\Api\Deploy;
use App\Http\Controllers\Api\Domains;
-use App\Http\Controllers\Api\Project;
use App\Http\Controllers\Api\Resources;
use App\Http\Controllers\Api\Server;
use App\Http\Controllers\Api\Team;
diff --git a/routes/web.php b/routes/web.php
index c442f6c75..0e9d3c478 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -85,7 +85,7 @@ if (isDev()) {
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot');
-Route::get('/api/v1/test/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
+Route::get('/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
Route::get('/waitlist', WaitlistIndex::class)->name('waitlist.index');
Route::get('/verify', [Controller::class, 'verify'])->middleware('auth')->name('verify.email');
Route::get('/email/verify/{id}/{hash}', [Controller::class, 'email_verify'])->middleware(['auth'])->name('verify.verify');
From bcc61b0d8b6d2fd05121b20a4490ab78c9374886 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 11:34:57 +0100
Subject: [PATCH 45/97] Add reverse proxy configuration for coolify-realtime
---
app/Models/Server.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/Models/Server.php b/app/Models/Server.php
index f067523df..5ad5130d7 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -357,6 +357,9 @@ ray($conf);
$schema = $url->getScheme();
$caddy_file = "
$schema://$host {
+ handle /app/* {
+ reverse_proxy coolify-realtime:6001
+ }
reverse_proxy coolify:80
}";
$base64 = base64_encode($caddy_file);
From 1835a914673b54527d19cbb015a07a2181a4498d Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 12:30:40 +0100
Subject: [PATCH 46/97] fix: proxy switch
---
app/Actions/CoolifyTask/RunRemoteProcess.php | 1 -
app/Events/ProxyStarted.php | 4 +-
app/Listeners/ProxyStartedNotification.php | 2 +-
.../Server/Proxy/DynamicConfigurations.php | 12 +-
app/Livewire/Settings/Configuration.php | 7 +-
app/Models/Server.php | 241 +++++++++---------
6 files changed, 133 insertions(+), 134 deletions(-)
diff --git a/app/Actions/CoolifyTask/RunRemoteProcess.php b/app/Actions/CoolifyTask/RunRemoteProcess.php
index 737b4babe..2c90750e6 100644
--- a/app/Actions/CoolifyTask/RunRemoteProcess.php
+++ b/app/Actions/CoolifyTask/RunRemoteProcess.php
@@ -114,7 +114,6 @@ class RunRemoteProcess
}
if ($this->call_event_on_finish) {
try {
- ray($this->call_event_data);
if ($this->call_event_data) {
event(resolve("App\\Events\\$this->call_event_on_finish", [
"data" => $this->call_event_data,
diff --git a/app/Events/ProxyStarted.php b/app/Events/ProxyStarted.php
index c77fe08b0..a4e053171 100644
--- a/app/Events/ProxyStarted.php
+++ b/app/Events/ProxyStarted.php
@@ -2,7 +2,6 @@
namespace App\Events;
-use App\Models\Server;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
@@ -10,7 +9,8 @@ use Illuminate\Queue\SerializesModels;
class ProxyStarted
{
use Dispatchable, InteractsWithSockets, SerializesModels;
- public function __construct(public Server $server)
+ public function __construct(public $data)
{
+
}
}
diff --git a/app/Listeners/ProxyStartedNotification.php b/app/Listeners/ProxyStartedNotification.php
index 62131c22a..e6be605ce 100644
--- a/app/Listeners/ProxyStartedNotification.php
+++ b/app/Listeners/ProxyStartedNotification.php
@@ -14,7 +14,7 @@ class ProxyStartedNotification
public function handle(ProxyStarted $event): void
{
- $this->server = data_get($event, 'server');
+ $this->server = data_get($event, 'data');
$this->server->setupDefault404Redirect();
$this->server->setupDynamicProxyConfiguration();
}
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurations.php b/app/Livewire/Server/Proxy/DynamicConfigurations.php
index b9e89321c..36219dc7e 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurations.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurations.php
@@ -11,7 +11,15 @@ class DynamicConfigurations extends Component
public ?Server $server = null;
public $parameters = [];
public Collection $contents;
- protected $listeners = ['loadDynamicConfigurations', 'refresh' => '$refresh'];
+ public function getListeners()
+ {
+ $teamId = auth()->user()->currentTeam()->id;
+ return [
+ "echo-private:team.{$teamId},ProxyStatusChanged" => 'loadDynamicConfigurations',
+ 'loadDynamicConfigurations',
+ 'refresh' => '$refresh'
+ ];
+ }
protected $rules = [
'contents.*' => 'nullable|string',
];
@@ -24,6 +32,7 @@ class DynamicConfigurations extends Component
$files = $files->sort();
if ($files->contains('coolify.yaml')) {
$files = $files->filter(fn ($file) => $file !== 'coolify.yaml')->prepend('coolify.yaml');
+ $files = $files->filter(fn ($file) => $file !== 'Caddyfile')->prepend('Caddyfile');
}
$contents = collect([]);
foreach ($files as $file) {
@@ -31,6 +40,7 @@ class DynamicConfigurations extends Component
$contents[$without_extension] = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server);
}
$this->contents = $contents;
+ $this->dispatch('refresh');
}
public function mount()
{
diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php
index f1c732a97..79ff3bcb6 100644
--- a/app/Livewire/Settings/Configuration.php
+++ b/app/Livewire/Settings/Configuration.php
@@ -75,12 +75,7 @@ class Configuration extends Component
$this->settings->save();
$this->server = Server::findOrFail(0);
- $this->setup_instance_fqdn();
+ $this->server->setupDynamicProxyConfiguration();
$this->dispatch('success', 'Instance settings updated successfully!');
}
-
- private function setup_instance_fqdn()
- {
- $this->server->setupDynamicProxyConfiguration();
- }
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 5ad5130d7..f361adc7b 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -3,7 +3,6 @@
namespace App\Models;
use App\Actions\Server\InstallDocker;
-use App\Enums\ProxyStatus;
use App\Enums\ProxyTypes;
use App\Notifications\Server\Revived;
use App\Notifications\Server\Unreachable;
@@ -125,7 +124,6 @@ class Server extends BaseModel
$dynamic_conf_path = $this->proxyPath() . "/dynamic";
$proxy_type = $this->proxyType();
$redirect_url = $this->proxy->redirect_url;
-
if ($proxy_type === 'TRAEFIK_V2') {
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
} else if ($proxy_type === 'CADDY') {
@@ -199,7 +197,6 @@ class Server extends BaseModel
$conf = ":80, :443 {
redir $redirect_url
}";
-ray($conf);
$conf =
"# This file is automatically generated by Coolify.\n" .
"# Do not edit it manually (only if you know what are you doing).\n\n" .
@@ -224,150 +221,148 @@ ray($conf);
{
$settings = InstanceSettings::get();
$dynamic_config_path = $this->proxyPath() . "/dynamic";
- if ($this) {
- if ($this->proxyType() === 'TRAEFIK_V2') {
- $file = "$dynamic_config_path/coolify.yaml";
- if (empty($settings->fqdn)) {
- instant_remote_process([
- "rm -f $file",
- ], $this);
- } else {
- $url = Url::fromString($settings->fqdn);
- $host = $url->getHost();
- $schema = $url->getScheme();
- $traefik_dynamic_conf = [
- 'http' =>
+ if ($this->proxyType() === 'TRAEFIK_V2') {
+ $file = "$dynamic_config_path/coolify.yaml";
+ if (empty($settings->fqdn)) {
+ instant_remote_process([
+ "rm -f $file",
+ ], $this);
+ } else {
+ $url = Url::fromString($settings->fqdn);
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $traefik_dynamic_conf = [
+ 'http' =>
+ [
+ 'middlewares' => [
+ 'redirect-to-https' => [
+ 'redirectscheme' => [
+ 'scheme' => 'https',
+ ],
+ ],
+ 'gzip' => [
+ 'compress' => true,
+ ],
+ ],
+ 'routers' =>
[
- 'middlewares' => [
- 'redirect-to-https' => [
- 'redirectscheme' => [
- 'scheme' => 'https',
- ],
- ],
- 'gzip' => [
- 'compress' => true,
- ],
- ],
- 'routers' =>
+ 'coolify-http' =>
[
- 'coolify-http' =>
- [
- 'middlewares' => [
- 0 => 'gzip',
- ],
- 'entryPoints' => [
- 0 => 'http',
- ],
- 'service' => 'coolify',
- 'rule' => "Host(`{$host}`)",
+ 'middlewares' => [
+ 0 => 'gzip',
],
- 'coolify-realtime-ws' =>
- [
- 'entryPoints' => [
- 0 => 'http',
- ],
- 'service' => 'coolify-realtime',
- 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
+ 'entryPoints' => [
+ 0 => 'http',
],
+ 'service' => 'coolify',
+ 'rule' => "Host(`{$host}`)",
],
- 'services' =>
+ 'coolify-realtime-ws' =>
[
- 'coolify' =>
+ 'entryPoints' => [
+ 0 => 'http',
+ ],
+ 'service' => 'coolify-realtime',
+ 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
+ ],
+ ],
+ 'services' =>
+ [
+ 'coolify' =>
+ [
+ 'loadBalancer' =>
[
- 'loadBalancer' =>
+ 'servers' =>
[
- 'servers' =>
+ 0 =>
[
- 0 =>
- [
- 'url' => 'http://coolify:80',
- ],
+ 'url' => 'http://coolify:80',
],
],
],
- 'coolify-realtime' =>
+ ],
+ 'coolify-realtime' =>
+ [
+ 'loadBalancer' =>
[
- 'loadBalancer' =>
+ 'servers' =>
[
- 'servers' =>
+ 0 =>
[
- 0 =>
- [
- 'url' => 'http://coolify-realtime:6001',
- ],
+ 'url' => 'http://coolify-realtime:6001',
],
],
],
],
],
+ ],
+ ];
+
+ if ($schema === 'https') {
+ $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
+ 0 => 'redirect-to-https',
];
- if ($schema === 'https') {
- $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
- 0 => 'redirect-to-https',
- ];
-
- $traefik_dynamic_conf['http']['routers']['coolify-https'] = [
- 'entryPoints' => [
- 0 => 'https',
- ],
- 'service' => 'coolify',
- 'rule' => "Host(`{$host}`)",
- 'tls' => [
- 'certresolver' => 'letsencrypt',
- ],
- ];
- $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [
- 'entryPoints' => [
- 0 => 'https',
- ],
- 'service' => 'coolify-realtime',
- 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
- 'tls' => [
- 'certresolver' => 'letsencrypt',
- ],
- ];
- }
- $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
- $yaml =
- "# This file is automatically generated by Coolify.\n" .
- "# Do not edit it manually (only if you know what are you doing).\n\n" .
- $yaml;
-
- $base64 = base64_encode($yaml);
- instant_remote_process([
- "mkdir -p $dynamic_config_path",
- "echo '$base64' | base64 -d > $file",
- ], $this);
-
- if (config('app.env') == 'local') {
- // ray($yaml);
- }
+ $traefik_dynamic_conf['http']['routers']['coolify-https'] = [
+ 'entryPoints' => [
+ 0 => 'https',
+ ],
+ 'service' => 'coolify',
+ 'rule' => "Host(`{$host}`)",
+ 'tls' => [
+ 'certresolver' => 'letsencrypt',
+ ],
+ ];
+ $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [
+ 'entryPoints' => [
+ 0 => 'https',
+ ],
+ 'service' => 'coolify-realtime',
+ 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)",
+ 'tls' => [
+ 'certresolver' => 'letsencrypt',
+ ],
+ ];
}
- } else if ($this->proxyType() === 'CADDY') {
- $file = "$dynamic_config_path/coolify.caddy";
- if (empty($settings->fqdn)) {
- instant_remote_process([
- "rm -f $file",
- ], $this);
- $this->reloadCaddy();
- } else {
- $url = Url::fromString($settings->fqdn);
- $host = $url->getHost();
- $schema = $url->getScheme();
- $caddy_file = "
+ $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
+ $yaml =
+ "# This file is automatically generated by Coolify.\n" .
+ "# Do not edit it manually (only if you know what are you doing).\n\n" .
+ $yaml;
+
+ $base64 = base64_encode($yaml);
+ instant_remote_process([
+ "mkdir -p $dynamic_config_path",
+ "echo '$base64' | base64 -d > $file",
+ ], $this);
+
+ if (config('app.env') == 'local') {
+ // ray($yaml);
+ }
+ }
+ } else if ($this->proxyType() === 'CADDY') {
+ $file = "$dynamic_config_path/coolify.caddy";
+ if (empty($settings->fqdn)) {
+ instant_remote_process([
+ "rm -f $file",
+ ], $this);
+ $this->reloadCaddy();
+ } else {
+ $url = Url::fromString($settings->fqdn);
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $caddy_file = "
$schema://$host {
handle /app/* {
reverse_proxy coolify-realtime:6001
}
reverse_proxy coolify:80
}";
- $base64 = base64_encode($caddy_file);
- instant_remote_process([
- "echo '$base64' | base64 -d > $file",
- ], $this);
- $this->reloadCaddy();
- }
+ $base64 = base64_encode($caddy_file);
+ instant_remote_process([
+ "echo '$base64' | base64 -d > $file",
+ ], $this);
+ $this->reloadCaddy();
}
}
}
@@ -396,16 +391,16 @@ $schema://$host {
}
public function proxyType()
{
- $proxyType = $this->proxy->get('type');
- if ($proxyType === ProxyTypes::NONE->value) {
- return $proxyType;
- }
+ // $proxyType = $this->proxy->get('type');
+ // if ($proxyType === ProxyTypes::NONE->value) {
+ // return $proxyType;
+ // }
// if (is_null($proxyType)) {
// $this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
// $this->proxy->status = ProxyStatus::EXITED->value;
// $this->save();
// }
- return $this->proxy->get('type');
+ return data_get($this->proxy, 'type.type');
}
public function scopeWithProxy(): Builder
{
From 4d181eef8ee4216202e2ab5fe52e0a666bfeca56 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 12:45:55 +0100
Subject: [PATCH 47/97] Refactor proxy type retrieval in Server and Proxy
classes
---
app/Livewire/Server/Proxy.php | 2 +-
app/Models/Server.php | 2 +-
resources/views/livewire/server/proxy.blade.php | 11 ++++++-----
.../views/livewire/server/proxy/deploy.blade.php | 4 ++--
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php
index 4ccbf7d64..dab7f54be 100644
--- a/app/Livewire/Server/Proxy.php
+++ b/app/Livewire/Server/Proxy.php
@@ -21,7 +21,7 @@ class Proxy extends Component
public function mount()
{
- $this->selectedProxy = data_get($this->server, 'proxy.type');
+ $this->selectedProxy = $this->server->proxyType();
$this->redirect_url = data_get($this->server, 'proxy.redirect_url');
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index f361adc7b..c632520a1 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -400,7 +400,7 @@ $schema://$host {
// $this->proxy->status = ProxyStatus::EXITED->value;
// $this->save();
// }
- return data_get($this->proxy, 'type.type');
+ return data_get($this->proxy, 'type');
}
public function scopeWithProxy(): Builder
{
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php
index 49bf654a6..f2d9b839e 100644
--- a/resources/views/livewire/server/proxy.blade.php
+++ b/resources/views/livewire/server/proxy.blade.php
@@ -1,5 +1,5 @@
- @if (data_get($server, 'proxy.type'))
+ @if ($server->proxyType())
@if ($selectedProxy !== 'NONE')
-
@if ($server->proxyType() === 'TRAEFIK_V2')
Traefik v2
diff --git a/resources/views/livewire/server/proxy/deploy.blade.php b/resources/views/livewire/server/proxy/deploy.blade.php
index 7a27eeab3..85652c982 100644
--- a/resources/views/livewire/server/proxy/deploy.blade.php
+++ b/resources/views/livewire/server/proxy/deploy.blade.php
@@ -16,10 +16,10 @@
- @if ($server->isFunctional() && data_get($server, 'proxy.type') !== 'NONE')
+ @if ($server->isFunctional() && $server->proxyType() !== 'NONE')
@if (data_get($server, 'proxy.status') === 'running')
- @if ($currentRoute === 'server.proxy' && $traefikDashboardAvailable)
+ @if ($currentRoute === 'server.proxy' && $traefikDashboardAvailable && $server->proxyType() === 'TRAEFIK_V2')
Traefik Dashboard
From a67576b447f131a90e03c7704bd12db8a1d80fc9 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 13:01:15 +0100
Subject: [PATCH 48/97] Update reverse proxy configuration in docker.php
---
bootstrap/helpers/docker.php | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 53b0bbe80..74295f6a5 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -236,7 +236,11 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
if ($serviceLabels) {
$labels->push("caddy_ingress_network={$uuid}");
- $labels->push("caddy_{$loop}.reverse_proxy={{upstreams}}");
+ if ($port) {
+ $labels->push("caddy_{$loop}.reverse_proxy={{upstreams $port}}");
+ } else {
+ $labels->push("caddy_{$loop}.reverse_proxy={{upstreams}}");
+ }
} else {
$labels->push("caddy_ingress_network={$network}");
if ($port) {
From 25ae54cab704e5306f051bd39f3afb5e540941a1 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 15:09:24 +0100
Subject: [PATCH 49/97] fix: service ports for services + caddy
---
app/Console/Commands/ServicesGenerate.php | 9 +++
app/Livewire/Admin/Index.php | 1 -
bootstrap/helpers/docker.php | 1 +
bootstrap/helpers/services.php | 9 ++-
bootstrap/helpers/shared.php | 71 ++++++++++++++++---
templates/compose/changedetection.yaml | 3 +-
templates/compose/code-server.yaml | 3 +-
templates/compose/dashboard.yaml | 3 +-
.../compose/directus-with-postgresql.yaml | 3 +-
templates/compose/directus.yaml | 8 ++-
templates/compose/docker-registry.yaml | 3 +-
templates/compose/duplicati.yaml | 3 +-
templates/compose/emby.yaml | 3 +-
templates/compose/embystat.yaml | 3 +-
templates/compose/fider.yaml | 3 +-
templates/compose/firefly.yaml | 3 +-
templates/compose/formbricks.yaml | 3 +-
templates/compose/ghost.yaml | 3 +-
templates/compose/glitchtip.yaml | 3 +-
.../compose/grafana-with-postgresql.yaml | 3 +-
templates/compose/grafana.yaml | 3 +-
templates/compose/plausible.yaml | 3 +-
templates/service-templates.json | 70 ++++++++++--------
23 files changed, 156 insertions(+), 61 deletions(-)
diff --git a/app/Console/Commands/ServicesGenerate.php b/app/Console/Commands/ServicesGenerate.php
index 75df4756b..7619a1d85 100644
--- a/app/Console/Commands/ServicesGenerate.php
+++ b/app/Console/Commands/ServicesGenerate.php
@@ -100,6 +100,12 @@ class ServicesGenerate extends Command
} else {
$tags = null;
}
+ $port = collect(preg_grep('/^# port:/', explode("\n", $content)))->values();
+ if ($port->count() > 0) {
+ $port = str($port[0])->after('# port:')->trim()->value();
+ } else {
+ $port = null;
+ }
$json = Yaml::parse($content);
$yaml = base64_encode(Yaml::dump($json, 10, 2));
$payload = [
@@ -111,6 +117,9 @@ class ServicesGenerate extends Command
'logo' => $logo,
'minversion' => $minversion,
];
+ if ($port) {
+ $payload['port'] = $port;
+ }
if ($env_file) {
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
$env_file_base64 = base64_encode($env_file_content);
diff --git a/app/Livewire/Admin/Index.php b/app/Livewire/Admin/Index.php
index c6b1d0a34..60bd6f5ea 100644
--- a/app/Livewire/Admin/Index.php
+++ b/app/Livewire/Admin/Index.php
@@ -4,7 +4,6 @@ namespace App\Livewire\Admin;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
-use Illuminate\Support\Facades\Crypt;
use Livewire\Component;
class Index extends Component
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 74295f6a5..f9da5fe3d 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -233,6 +233,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
}
$labels->push("caddy_{$loop}={$schema}://{$host}");
$labels->push("caddy_{$loop}.header=-Server");
+ $labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
if ($serviceLabels) {
$labels->push("caddy_ingress_network={$uuid}");
diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php
index c46c1e542..dd05c67eb 100644
--- a/bootstrap/helpers/services.php
+++ b/bootstrap/helpers/services.php
@@ -80,7 +80,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneS
return handleError($e);
}
}
-function updateCompose($resource)
+function updateCompose(ServiceApplication|ServiceDatabase $resource)
{
try {
$name = data_get($resource, 'name');
@@ -90,6 +90,9 @@ function updateCompose($resource)
// Switch Image
$image = data_get($resource, 'image');
data_set($dockerCompose, "services.{$name}.image", $image);
+ $dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
+ $resource->service->docker_compose_raw = $dockerComposeRaw;
+ $resource->service->save();
if (!str($resource->fqdn)->contains(',')) {
// Update FQDN
@@ -105,7 +108,6 @@ function updateCompose($resource)
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$url = Url::fromString($resource->fqdn);
$url = $url->getHost();
- ray($url);
if ($generatedEnv) {
$url = Str::of($resource->fqdn)->after('://');
$generatedEnv->value = $url;
@@ -113,9 +115,6 @@ function updateCompose($resource)
}
}
- $dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
- $resource->service->docker_compose_raw = $dockerComposeRaw;
- $resource->service->save();
} catch (\Throwable $e) {
return handleError($e);
}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 359792ddc..28d7fc164 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -615,7 +615,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
-
+ $allServices = getServiceTemplates();
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
@@ -630,7 +630,22 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
}
$definedNetwork = collect([$resource->uuid]);
- $services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource) {
+ $services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $allServices) {
+ // Workarounds for beta users.
+ if ($serviceName === 'registry') {
+ $tempServiceName = "docker-registry";
+ } else {
+ $tempServiceName = $serviceName;
+ }
+ if (str(data_get($service,'image'))->contains('glitchtip')) {
+ $tempServiceName = 'glitchtip';
+ }
+ $serviceDefinition = data_get($allServices, $tempServiceName);
+ $predefinedPort = data_get($serviceDefinition, 'port');
+ if ($serviceName === 'plausible') {
+ $predefinedPort = '8000';
+ }
+ // End of workarounds for beta users.
$serviceVolumes = collect(data_get($service, 'volumes', []));
$servicePorts = collect(data_get($service, 'ports', []));
$serviceNetworks = collect(data_get($service, 'networks', []));
@@ -905,10 +920,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$fqdn = "$fqdn:$port";
}
if (substr_count($key->value(), '_') >= 2) {
- if (is_null($value)) {
- $value = Str::of('/');
+ if ($value) {
+ $path = $value->value();
+ } else {
+ $path = null;
}
- $path = $value->value();
if ($generatedServiceFQDNS->count() > 0) {
$alreadyGenerated = $generatedServiceFQDNS->has($key->value());
if ($alreadyGenerated) {
@@ -939,6 +955,25 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'is_preview' => false,
]);
}
+ // Caddy needs exact port in some cases.
+
+ if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) {
+ if ($resource->server->proxyType() === 'CADDY') {
+ $env = EnvironmentVariable::where([
+ 'key' => $key,
+ 'service_id' => $resource->id,
+ ])->first();
+ if ($env) {
+ $env_url = Url::fromString($savedService->fqdn);
+ $env_port = $env_url->getPort();
+ if ($env_port !== $predefinedPort) {
+ $env_url = $env_url->withPort($predefinedPort);
+ $savedService->fqdn = $env_url->__toString();
+ $savedService->save();
+ }
+ }
+ }
+ }
// data_forget($service, "environment.$variableName");
// $yaml = data_forget($yaml, "services.$serviceName.environment.$variableName");
// if (count(data_get($yaml, 'services.' . $serviceName . '.environment')) === 0) {
@@ -987,6 +1022,25 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$savedService->fqdn = $fqdn;
$savedService->save();
}
+ // Caddy needs exact port in some cases.
+ ray($predefinedPort, $key, $fqdn);
+ if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) {
+ if ($resource->server->proxyType() === 'CADDY') {
+ $env = EnvironmentVariable::where([
+ 'key' => $key,
+ 'service_id' => $resource->id,
+ ])->first();
+ if ($env) {
+ $env_url = Url::fromString($env->value);
+ $env_port = $env_url->getPort();
+ if ($env_port !== $predefinedPort) {
+ $env_url = $env_url->withPort($predefinedPort);
+ $savedService->fqdn = $env_url->__toString();
+ $savedService->save();
+ }
+ }
+ }
+ }
}
} else {
$generatedValue = generateEnvValue($command, $resource);
@@ -1364,10 +1418,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$fqdn = "$fqdn:$port";
}
if (substr_count($key->value(), '_') >= 2) {
- if (is_null($value)) {
- $value = Str::of('/');
+ if ($value) {
+ $path = $value->value();
+ } else {
+ $path = null;
}
- $path = $value->value();
if ($generatedServiceFQDNS->count() > 0) {
$alreadyGenerated = $generatedServiceFQDNS->has($key->value());
if ($alreadyGenerated) {
diff --git a/templates/compose/changedetection.yaml b/templates/compose/changedetection.yaml
index be8a4e0d2..9d7892ce1 100644
--- a/templates/compose/changedetection.yaml
+++ b/templates/compose/changedetection.yaml
@@ -2,6 +2,7 @@
# slogan: Website change detection monitor and notifications.
# tags: web, alert, monitor
# logo: svgs/changedetection.png
+# port: 5000
services:
changedetection:
@@ -9,7 +10,7 @@ services:
volumes:
- changedetection-data:/datastore
environment:
- - SERVICE_FQDN_CHANGEDETECTION
+ - SERVICE_FQDN_CHANGEDETECTION_5000
- PUID=1000
- PGID=1000
- BASE_URL=$SERVICE_FQDN_CHANGEDETECTION
diff --git a/templates/compose/code-server.yaml b/templates/compose/code-server.yaml
index 65bb8872d..44ac87593 100644
--- a/templates/compose/code-server.yaml
+++ b/templates/compose/code-server.yaml
@@ -2,12 +2,13 @@
# slogan: Code-Server is a web-based code editor that enables remote coding and collaboration from any device, anywhere.
# tags: code, editor, remote, collaboration
# logo: svgs/code-server.svg
+# port: 8443
services:
code-server:
image: lscr.io/linuxserver/code-server:latest
environment:
- - SERVICE_FQDN_CODESERVER
+ - SERVICE_FQDN_CODESERVER_8443
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
diff --git a/templates/compose/dashboard.yaml b/templates/compose/dashboard.yaml
index 3e3c7c835..0fe628956 100644
--- a/templates/compose/dashboard.yaml
+++ b/templates/compose/dashboard.yaml
@@ -1,12 +1,13 @@
# documentation: https://github.com/phntxx/dashboard?tab=readme-ov-file#dashboard
# slogan: A dashboard, inspired by SUI.
# tags: dashboard, web, search, bookmarks
+# port: 8080
services:
dashboard:
image: phntxx/dashboard:latest
environment:
- - SERVICE_FQDN_DASHBOARD
+ - SERVICE_FQDN_DASHBOARD_8080
volumes:
- dashboard-data:/app/data
healthcheck:
diff --git a/templates/compose/directus-with-postgresql.yaml b/templates/compose/directus-with-postgresql.yaml
index c9c0c26c6..a088ed25f 100644
--- a/templates/compose/directus-with-postgresql.yaml
+++ b/templates/compose/directus-with-postgresql.yaml
@@ -2,6 +2,7 @@
# slogan: Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.
# tags: directus, cms, database, sql
# logo: svgs/directus.svg
+# port: 8055
services:
directus:
@@ -10,7 +11,7 @@ services:
- directus-uploads:/directus/uploads
- directus-extensions:/directus/extensions
environment:
- - SERVICE_FQDN_DIRECTUS
+ - SERVICE_FQDN_DIRECTUS_8055
- KEY=$SERVICE_BASE64_64_KEY
- SECRET=$SERVICE_BASE64_64_SECRET
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
diff --git a/templates/compose/directus.yaml b/templates/compose/directus.yaml
index 05006a320..467a210f9 100644
--- a/templates/compose/directus.yaml
+++ b/templates/compose/directus.yaml
@@ -2,15 +2,17 @@
# slogan: Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.
# tags: directus, cms, database, sql
# logo: svgs/directus.svg
+# port: 8055
services:
directus:
- image: directus/directus:10.7
+ image: directus/directus:10
volumes:
- - directus-database:/directus/database
- directus-uploads:/directus/uploads
+ - directus-database:/directus/database
+ - directus-extensions:/directus/extensions
environment:
- - SERVICE_FQDN_DIRECTUS
+ - SERVICE_FQDN_DIRECTUS_8055
- KEY=$SERVICE_BASE64_64_KEY
- SECRET=$SERVICE_BASE64_64_SECRET
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@example.com}
diff --git a/templates/compose/docker-registry.yaml b/templates/compose/docker-registry.yaml
index 67f551f2b..edd285ef5 100644
--- a/templates/compose/docker-registry.yaml
+++ b/templates/compose/docker-registry.yaml
@@ -2,12 +2,13 @@
# slogan: The Docker Registry is lets you distribute Docker images.
# tags: registry,images,docker
# logo: svgs/docker-registry.png
+# port: 5000
services:
registry:
image: registry:2
environment:
- - SERVICE_FQDN_REGISTRY
+ - SERVICE_FQDN_REGISTRY_5000
- REGISTRY_AUTH=htpasswd
- REGISTRY_AUTH_HTPASSWD_REALM=Registry
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.password
diff --git a/templates/compose/duplicati.yaml b/templates/compose/duplicati.yaml
index 435d6d860..74cc4e15a 100644
--- a/templates/compose/duplicati.yaml
+++ b/templates/compose/duplicati.yaml
@@ -2,12 +2,13 @@
# slogan: Duplicati is a backup solution, allowing you to make scheduled backups with encryption.
# tags: backup, encryption
# logo: svgs/duplicati.webp
+# port: 8200
services:
duplicati:
image: lscr.io/linuxserver/duplicati:latest
environment:
- - SERVICE_FQDN_DUPLICATI
+ - SERVICE_FQDN_DUPLICATI_8200
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
diff --git a/templates/compose/emby.yaml b/templates/compose/emby.yaml
index 8f0241b66..6172ae159 100644
--- a/templates/compose/emby.yaml
+++ b/templates/compose/emby.yaml
@@ -2,12 +2,13 @@
# slogan: A media server software that allows you to organize, stream, and access your multimedia content effortlessly.
# tags: media, server, movies, tv, music
# logo: svgs/emby.png
+# port: 8096
services:
emby:
image: lscr.io/linuxserver/emby:latest
environment:
- - SERVICE_FQDN_EMBY
+ - SERVICE_FQDN_EMBY_8096
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
diff --git a/templates/compose/embystat.yaml b/templates/compose/embystat.yaml
index c80863645..6fbbcab07 100644
--- a/templates/compose/embystat.yaml
+++ b/templates/compose/embystat.yaml
@@ -1,12 +1,13 @@
# documentation: https://github.com/mregni/EmbyStat
# slogan: EmnyStat is a web analytics tool, designed to provide insight into website traffic and user behavior.
# tags: media, server, movies, tv, music
+# port: 6555
services:
embystat:
image: lscr.io/linuxserver/embystat:latest
environment:
- - SERVICE_FQDN_EMBYSTAT
+ - SERVICE_FQDN_EMBYSTAT_6555
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
diff --git a/templates/compose/fider.yaml b/templates/compose/fider.yaml
index dce414aef..6314bb5c3 100644
--- a/templates/compose/fider.yaml
+++ b/templates/compose/fider.yaml
@@ -2,12 +2,13 @@
# slogan: Fider is a feedback platform for collecting and managing user feedback.
# tags: feedback, user-feedback
# logo: svgs/fider.svg
+# port: 3000
services:
fider:
image: getfider/fider:stable
environment:
- BASE_URL: $SERVICE_FQDN_FIDER
+ BASE_URL: $SERVICE_FQDN_FIDER_3000
DATABASE_URL: postgres://$SERVICE_USER_MYSQL:$SERVICE_PASSWORD_MYSQL@database:5432/fider?sslmode=disable
JWT_SECRET: $SERVICE_PASSWORD_64_FIDER
EMAIL_NOREPLY: ${EMAIL_NOREPLY:-noreply@example.com}
diff --git a/templates/compose/firefly.yaml b/templates/compose/firefly.yaml
index 4526fa249..14ff062c7 100644
--- a/templates/compose/firefly.yaml
+++ b/templates/compose/firefly.yaml
@@ -2,12 +2,13 @@
# slogan: A personal finances manager that can help you save money.
# tags: finance, money, personal, manager
# logo: svgs/firefly.svg
+# port: 8080
services:
firefly:
image: fireflyiii/core:latest
environment:
- - SERVICE_FQDN_FIREFLY
+ - SERVICE_FQDN_FIREFLY_8080
- APP_KEY=$SERVICE_BASE64_APPKEY
- DB_HOST=mysql
- DB_PORT=3306
diff --git a/templates/compose/formbricks.yaml b/templates/compose/formbricks.yaml
index ac738f877..0871712bc 100644
--- a/templates/compose/formbricks.yaml
+++ b/templates/compose/formbricks.yaml
@@ -2,12 +2,13 @@
# slogan: Open Source Experience Management
# tags: form, builder, forms, open source, experience, management, self-hosted, docker
# logo: svgs/formbricks.png
+# port: 3000
services:
formbricks:
image: formbricks/formbricks:latest
environment:
- - SERVICE_FQDN_FORMBRICKS
+ - SERVICE_FQDN_FORMBRICKS_3000
- WEBAPP_URL=$SERVICE_FQDN_FORMBRICKS
- DATABASE_URL=postgres://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgresql:5432/${POSTGRESQL_DATABASE:-formbricks}
- NEXTAUTH_SECRET=$SERVICE_BASE64_64_NEXTAUTH
diff --git a/templates/compose/ghost.yaml b/templates/compose/ghost.yaml
index 9c01e5cb4..6a453af7a 100644
--- a/templates/compose/ghost.yaml
+++ b/templates/compose/ghost.yaml
@@ -2,6 +2,7 @@
# slogan: Ghost is a content management system (CMS) and blogging platform.
# tags: cms, blog, content, management, system
# logo: svgs/ghost.svg
+# port: 2368
services:
ghost:
@@ -9,7 +10,7 @@ services:
volumes:
- ghost-content-data:/var/lib/ghost/content
environment:
- - url=$SERVICE_FQDN_GHOST
+ - url=$SERVICE_FQDN_GHOST_2368
- database__client=mysql
- database__connection__host=mysql
- database__connection__user=$SERVICE_USER_MYSQL
diff --git a/templates/compose/glitchtip.yaml b/templates/compose/glitchtip.yaml
index b2bdbf8ff..9c323359f 100644
--- a/templates/compose/glitchtip.yaml
+++ b/templates/compose/glitchtip.yaml
@@ -2,6 +2,7 @@
# slogan: GlitchTip is a self-hosted, open-source error tracking system.
# tags: error, tracking, open-source, self-hosted, sentry
# logo: svgs/glitchtip.png
+# port: 8080
version: "3.8"
services:
@@ -21,7 +22,7 @@ services:
- postgres
- redis
environment:
- - SERVICE_FQDN_GLITCHTIP
+ - SERVICE_FQDN_GLITCHTIP_8080
- DATABASE_URL=postgres://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgres:5432/${POSTGRESQL_DATABASE:-glitchtip}
- SECRET_KEY=$SERVICE_BASE64_64_ENCRYPTION
- EMAIL_URL=${EMAIL_URL:-consolemail://}
diff --git a/templates/compose/grafana-with-postgresql.yaml b/templates/compose/grafana-with-postgresql.yaml
index 3aa326d68..3b2896541 100644
--- a/templates/compose/grafana-with-postgresql.yaml
+++ b/templates/compose/grafana-with-postgresql.yaml
@@ -2,12 +2,13 @@
# slogan: Grafana is the open source analytics & monitoring solution for every database.
# tags: grafana,analytics,monitoring,dashboard
# logo: svgs/grafana.svg
+# port: 3000
services:
grafana:
image: grafana/grafana-oss
environment:
- - SERVICE_FQDN_GRAFANA
+ - SERVICE_FQDN_GRAFANA_3000
- GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA}
- GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA}
- GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA}
diff --git a/templates/compose/grafana.yaml b/templates/compose/grafana.yaml
index a4e5b4042..749ae1141 100644
--- a/templates/compose/grafana.yaml
+++ b/templates/compose/grafana.yaml
@@ -2,12 +2,13 @@
# slogan: Grafana is the open source analytics & monitoring solution for every database.
# tags: grafana,analytics,monitoring,dashboard
# logo: svgs/grafana.svg
+# port: 3000
services:
grafana:
image: grafana/grafana-oss
environment:
- - SERVICE_FQDN_GRAFANA
+ - SERVICE_FQDN_GRAFANA_3000
- GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA}
- GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA}
- GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA}
diff --git a/templates/compose/plausible.yaml b/templates/compose/plausible.yaml
index 7c4b449ba..5428e08b8 100644
--- a/templates/compose/plausible.yaml
+++ b/templates/compose/plausible.yaml
@@ -2,6 +2,7 @@
# documentation: https://plausible.io/docs/self-hosting
# slogan: "Plausible Analytics is a simple, open-source, lightweight (< 1 KB) and privacy-friendly web analytics alternative to Google Analytics."
# tags: analytics, privacy, google, alternative
+# port: 8000
version: "3.3"
services:
@@ -10,7 +11,7 @@ services:
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
environment:
- DATABASE_URL=postgres://postgres:$SERVICE_PASSWORD_POSTGRES@plausible_db/plausible
- - BASE_URL=$SERVICE_FQDN_PLAUSIBLE
+ - BASE_URL=$SERVICE_FQDN_PLAUSIBLE_8000
- SECRET_KEY_BASE=$SERVICE_BASE64_64_PLAUSIBLE
- TOTP_VAULT_KEY=$SERVICE_REALBASE64_TOTP
depends_on:
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 2c0ee7754..a094448de 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -54,19 +54,20 @@
"changedetection": {
"documentation": "https:\/\/github.com\/dgtlmoon\/changedetection.io\/",
"slogan": "Website change detection monitor and notifications.",
- "compose": "c2VydmljZXM6CiAgY2hhbmdlZGV0ZWN0aW9uOgogICAgaW1hZ2U6IGdoY3IuaW8vZGd0bG1vb24vY2hhbmdlZGV0ZWN0aW9uLmlvCiAgICB2b2x1bWVzOgogICAgICAtICdjaGFuZ2VkZXRlY3Rpb24tZGF0YTovZGF0YXN0b3JlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQU5HRURFVEVDVElPTgogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtIEJBU0VfVVJMPSRTRVJWSUNFX0ZRRE5fQ0hBTkdFREVURUNUSU9OCiAgICAgIC0gJ1BMQVlXUklHSFRfRFJJVkVSX1VSTD13czovL3BsYXl3cmlnaHQtY2hyb21lOjMwMDAvP3N0ZWFsdGg9MSYtLWRpc2FibGUtd2ViLXNlY3VyaXR5PXRydWUnCiAgICAgIC0gSElERV9SRUZFUkVSPXRydWUKICAgIGRlcGVuZHNfb246CiAgICAgIHBsYXl3cmlnaHQtY2hyb21lOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgcGxheXdyaWdodC1jaHJvbWU6CiAgICBpbWFnZTogJ2RndGxtb29uL3NvY2twdXBwZXRicm93c2VyOmxhdGVzdCcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTQ1JFRU5fV0lEVEg9MTkyMAogICAgICAtIFNDUkVFTl9IRUlHSFQ9MTAyNAogICAgICAtIFNDUkVFTl9ERVBUSD0xNgogICAgICAtIE1BWF9DT05DVVJSRU5UX0NIUk9NRV9QUk9DRVNTRVM9MTAK",
+ "compose": "c2VydmljZXM6CiAgY2hhbmdlZGV0ZWN0aW9uOgogICAgaW1hZ2U6IGdoY3IuaW8vZGd0bG1vb24vY2hhbmdlZGV0ZWN0aW9uLmlvCiAgICB2b2x1bWVzOgogICAgICAtICdjaGFuZ2VkZXRlY3Rpb24tZGF0YTovZGF0YXN0b3JlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQU5HRURFVEVDVElPTl81MDAwCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gQkFTRV9VUkw9JFNFUlZJQ0VfRlFETl9DSEFOR0VERVRFQ1RJT04KICAgICAgLSAnUExBWVdSSUdIVF9EUklWRVJfVVJMPXdzOi8vcGxheXdyaWdodC1jaHJvbWU6MzAwMC8\/c3RlYWx0aD0xJi0tZGlzYWJsZS13ZWItc2VjdXJpdHk9dHJ1ZScKICAgICAgLSBISURFX1JFRkVSRVI9dHJ1ZQogICAgZGVwZW5kc19vbjoKICAgICAgcGxheXdyaWdodC1jaHJvbWU6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICBwbGF5d3JpZ2h0LWNocm9tZToKICAgIGltYWdlOiAnZGd0bG1vb24vc29ja3B1cHBldGJyb3dzZXI6bGF0ZXN0JwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGVudmlyb25tZW50OgogICAgICAtIFNDUkVFTl9XSURUSD0xOTIwCiAgICAgIC0gU0NSRUVOX0hFSUdIVD0xMDI0CiAgICAgIC0gU0NSRUVOX0RFUFRIPTE2CiAgICAgIC0gTUFYX0NPTkNVUlJFTlRfQ0hST01FX1BST0NFU1NFUz0xMAo=",
"tags": [
"web",
"alert",
"monitor"
],
"logo": "svgs\/changedetection.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "5000"
},
"code-server": {
"documentation": "https:\/\/coder.com\/docs\/code-server\/latest",
"slogan": "Code-Server is a web-based code editor that enables remote coding and collaboration from any device, anywhere.",
- "compose": "c2VydmljZXM6CiAgY29kZS1zZXJ2ZXI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvY29kZS1zZXJ2ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVTRVJWRVIKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfUEFTU1dPUkRDT0RFU0VSVkVSCiAgICAgIC0gU1VET19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9TVURPQ09ERVNFUlZFUgogICAgICAtIERFRkFVTFRfV09SS1NQQUNFPS9jb25maWcvd29ya3NwYWNlCiAgICB2b2x1bWVzOgogICAgICAtICdjb2RlLXNlcnZlci1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4NDQzJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgY29kZS1zZXJ2ZXI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvY29kZS1zZXJ2ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVTRVJWRVJfODQ0MwogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtIFRaPUV1cm9wZS9NYWRyaWQKICAgICAgLSBQQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF82NF9QQVNTV09SRENPREVTRVJWRVIKICAgICAgLSBTVURPX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1NVRE9DT0RFU0VSVkVSCiAgICAgIC0gREVGQVVMVF9XT1JLU1BBQ0U9L2NvbmZpZy93b3Jrc3BhY2UKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2NvZGUtc2VydmVyLWNvbmZpZzovY29uZmlnJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0Ojg0NDMnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"code",
"editor",
@@ -74,12 +75,13 @@
"collaboration"
],
"logo": "svgs\/code-server.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8443"
},
"dashboard": {
"documentation": "https:\/\/github.com\/phntxx\/dashboard?tab=readme-ov-file#dashboard",
"slogan": "A dashboard, inspired by SUI.",
- "compose": "c2VydmljZXM6CiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdwaG50eHgvZGFzaGJvYXJkOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9EQVNIQk9BUkQKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Rhc2hib2FyZC1kYXRhOi9hcHAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MDgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdwaG50eHgvZGFzaGJvYXJkOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9EQVNIQk9BUkRfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAnZGFzaGJvYXJkLWRhdGE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"dashboard",
"web",
@@ -87,12 +89,13 @@
"bookmarks"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"directus-with-postgresql": {
"documentation": "https:\/\/directus.io",
"slogan": "Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.",
- "compose": "c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZXh0ZW5zaW9uczovZGlyZWN0dXMvZXh0ZW5zaW9ucycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ESVJFQ1RVUwogICAgICAtIEtFWT0kU0VSVklDRV9CQVNFNjRfNjRfS0VZCiAgICAgIC0gU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF82NF9TRUNSRVQKICAgICAgLSAnQURNSU5fRU1BSUw9JHtBRE1JTl9FTUFJTDotYWRtaW5AZXhhbXBsZS5jb219JwogICAgICAtIEFETUlOX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX0FETUlOCiAgICAgIC0gREJfQ0xJRU5UPXBvc3RncmVzCiAgICAgIC0gREJfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gREJfUE9SVD01NDMyCiAgICAgIC0gJ0RCX0RBVEFCQVNFPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZGlyZWN0dXN9JwogICAgICAtIERCX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMCiAgICAgIC0gREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTAogICAgICAtIFJFRElTX0hPU1Q9cmVkaXMKICAgICAgLSBSRURJU19QT1JUPTYzNzkKICAgICAgLSBXRUJTT0NLRVRTX0VOQUJMRUQ9dHJ1ZQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2RpcmVjdHVzLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWRpcmVjdHVzfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny1hbHBpbmUnCiAgICBjb21tYW5kOiAncmVkaXMtc2VydmVyIC0tYXBwZW5kb25seSB5ZXMnCiAgICB2b2x1bWVzOgogICAgICAtICdkaXJlY3R1cy1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
+ "compose": "c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZXh0ZW5zaW9uczovZGlyZWN0dXMvZXh0ZW5zaW9ucycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9ESVJFQ1RVU184MDU1CiAgICAgIC0gS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9LRVkKICAgICAgLSBTRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzY0X1NFQ1JFVAogICAgICAtICdBRE1JTl9FTUFJTD0ke0FETUlOX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gQURNSU5fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfQURNSU4KICAgICAgLSBEQl9DTElFTlQ9cG9zdGdyZXMKICAgICAgLSBEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1JUPTU0MzIKICAgICAgLSAnREJfREFUQUJBU0U9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1kaXJlY3R1c30nCiAgICAgIC0gREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUwKICAgICAgLSBEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMCiAgICAgIC0gUkVESVNfSE9TVD1yZWRpcwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFdFQlNPQ0tFVFNfRU5BQkxFRD10cnVlCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZGlyZWN0dXN9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHJlZGlzOgogICAgaW1hZ2U6ICdyZWRpczo3LWFscGluZScKICAgIGNvbW1hbmQ6ICdyZWRpcy1zZXJ2ZXIgLS1hcHBlbmRvbmx5IHllcycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2RpcmVjdHVzLXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
"tags": [
"directus",
"cms",
@@ -100,12 +103,13 @@
"sql"
],
"logo": "svgs\/directus.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8055"
},
"directus": {
"documentation": "https:\/\/directus.io",
"slogan": "Directus wraps databases with a dynamic API, and provides an intuitive app for managing its content.",
- "compose": "c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwLjcnCiAgICB2b2x1bWVzOgogICAgICAtICdkaXJlY3R1cy1kYXRhYmFzZTovZGlyZWN0dXMvZGF0YWJhc2UnCiAgICAgIC0gJ2RpcmVjdHVzLXVwbG9hZHM6L2RpcmVjdHVzL3VwbG9hZHMnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRElSRUNUVVMKICAgICAgLSBLRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0tFWQogICAgICAtIFNFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUCiAgICAgIC0gJ0FETUlOX0VNQUlMPSR7QURNSU5fRU1BSUw6LWFkbWluQGV4YW1wbGUuY29tfScKICAgICAgLSBBRE1JTl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9BRE1JTgogICAgICAtIERCX0NMSUVOVD1zcWxpdGUzCiAgICAgIC0gREJfRklMRU5BTUU9L2RpcmVjdHVzL2RhdGFiYXNlL2RhdGEuZGIKICAgICAgLSBXRUJTT0NLRVRTX0VOQUJMRUQ9dHJ1ZQo=",
+ "compose": "c2VydmljZXM6CiAgZGlyZWN0dXM6CiAgICBpbWFnZTogJ2RpcmVjdHVzL2RpcmVjdHVzOjEwJwogICAgdm9sdW1lczoKICAgICAgLSAnZGlyZWN0dXMtdXBsb2FkczovZGlyZWN0dXMvdXBsb2FkcycKICAgICAgLSAnZGlyZWN0dXMtZGF0YWJhc2U6L2RpcmVjdHVzL2RhdGFiYXNlJwogICAgICAtICdkaXJlY3R1cy1leHRlbnNpb25zOi9kaXJlY3R1cy9leHRlbnNpb25zJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RJUkVDVFVTXzgwNTUKICAgICAgLSBLRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0tFWQogICAgICAtIFNFQ1JFVD0kU0VSVklDRV9CQVNFNjRfNjRfU0VDUkVUCiAgICAgIC0gJ0FETUlOX0VNQUlMPSR7QURNSU5fRU1BSUw6LWFkbWluQGV4YW1wbGUuY29tfScKICAgICAgLSBBRE1JTl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9BRE1JTgogICAgICAtIERCX0NMSUVOVD1zcWxpdGUzCiAgICAgIC0gREJfRklMRU5BTUU9L2RpcmVjdHVzL2RhdGFiYXNlL2RhdGEuZGIKICAgICAgLSBXRUJTT0NLRVRTX0VOQUJMRUQ9dHJ1ZQo=",
"tags": [
"directus",
"cms",
@@ -113,19 +117,21 @@
"sql"
],
"logo": "svgs\/directus.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8055"
},
"docker-registry": {
"documentation": "https:\/\/docs.docker.com\/registry\/",
"slogan": "The Docker Registry is lets you distribute Docker images.",
- "compose": "c2VydmljZXM6CiAgcmVnaXN0cnk6CiAgICBpbWFnZTogJ3JlZ2lzdHJ5OjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUkVHSVNUUlkKICAgICAgLSBSRUdJU1RSWV9BVVRIPWh0cGFzc3dkCiAgICAgIC0gUkVHSVNUUllfQVVUSF9IVFBBU1NXRF9SRUFMTT1SZWdpc3RyeQogICAgICAtIFJFR0lTVFJZX0FVVEhfSFRQQVNTV0RfUEFUSD0vYXV0aC9yZWdpc3RyeS5wYXNzd29yZAogICAgICAtIFJFR0lTVFJZX1NUT1JBR0VfRklMRVNZU1RFTV9ST09URElSRUNUT1JZPS9kYXRhCiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgICAgdGFyZ2V0OiAvYXV0aC9yZWdpc3RyeS5wYXNzd29yZAogICAgICAgIGlzRGlyZWN0b3J5OiBmYWxzZQogICAgICAgIGNvbnRlbnQ6ICd0ZXN0dXNlcjokMnkkMDUkL28ySnZtSTJiaEV4WEl0Nk9xeGE3ZWtZQjd2M3NjajF3RkVmNnRCc2xKdkpPTW9QUUwuR3knCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2NvbmZpZy9jb25maWcueW1sCiAgICAgICAgdGFyZ2V0OiAvZXRjL2RvY2tlci9yZWdpc3RyeS9jb25maWcueW1sCiAgICAgICAgaXNEaXJlY3Rvcnk6IGZhbHNlCiAgICAgICAgY29udGVudDogInZlcnNpb246IDAuMVxubG9nOlxuICBmaWVsZHM6XG4gICAgc2VydmljZTogcmVnaXN0cnlcbnN0b3JhZ2U6XG4gIGNhY2hlOlxuICAgIGJsb2JkZXNjcmlwdG9yOiBpbm1lbW9yeVxuICBmaWxlc3lzdGVtOlxuICAgIHJvb3RkaXJlY3Rvcnk6IC92YXIvbGliL3JlZ2lzdHJ5XG5odHRwOlxuICBhZGRyOiA6NTAwMFxuICBoZWFkZXJzOlxuICAgIFgtQ29udGVudC1UeXBlLU9wdGlvbnM6IFtub3NuaWZmXVxuaGVhbHRoOlxuICBzdG9yYWdlZHJpdmVyOlxuICAgIGVuYWJsZWQ6IHRydWVcbiAgICBpbnRlcnZhbDogMTBzXG4gICAgdGhyZXNob2xkOiAzIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9kYXRhCiAgICAgICAgdGFyZ2V0OiAvZGF0YQogICAgICAgIGlzRGlyZWN0b3J5OiB0cnVlCg==",
+ "compose": "c2VydmljZXM6CiAgcmVnaXN0cnk6CiAgICBpbWFnZTogJ3JlZ2lzdHJ5OjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fUkVHSVNUUllfNTAwMAogICAgICAtIFJFR0lTVFJZX0FVVEg9aHRwYXNzd2QKICAgICAgLSBSRUdJU1RSWV9BVVRIX0hUUEFTU1dEX1JFQUxNPVJlZ2lzdHJ5CiAgICAgIC0gUkVHSVNUUllfQVVUSF9IVFBBU1NXRF9QQVRIPS9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgIC0gUkVHSVNUUllfU1RPUkFHRV9GSUxFU1lTVEVNX1JPT1RESVJFQ1RPUlk9L2RhdGEKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2F1dGgvcmVnaXN0cnkucGFzc3dvcmQKICAgICAgICB0YXJnZXQ6IC9hdXRoL3JlZ2lzdHJ5LnBhc3N3b3JkCiAgICAgICAgaXNEaXJlY3Rvcnk6IGZhbHNlCiAgICAgICAgY29udGVudDogJ3Rlc3R1c2VyOiQyeSQwNSQvbzJKdm1JMmJoRXhYSXQ2T3F4YTdla1lCN3Yzc2NqMXdGRWY2dEJzbEp2Sk9Nb1BRTC5HeScKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vY29uZmlnL2NvbmZpZy55bWwKICAgICAgICB0YXJnZXQ6IC9ldGMvZG9ja2VyL3JlZ2lzdHJ5L2NvbmZpZy55bWwKICAgICAgICBpc0RpcmVjdG9yeTogZmFsc2UKICAgICAgICBjb250ZW50OiAidmVyc2lvbjogMC4xXG5sb2c6XG4gIGZpZWxkczpcbiAgICBzZXJ2aWNlOiByZWdpc3RyeVxuc3RvcmFnZTpcbiAgY2FjaGU6XG4gICAgYmxvYmRlc2NyaXB0b3I6IGlubWVtb3J5XG4gIGZpbGVzeXN0ZW06XG4gICAgcm9vdGRpcmVjdG9yeTogL3Zhci9saWIvcmVnaXN0cnlcbmh0dHA6XG4gIGFkZHI6IDo1MDAwXG4gIGhlYWRlcnM6XG4gICAgWC1Db250ZW50LVR5cGUtT3B0aW9uczogW25vc25pZmZdXG5oZWFsdGg6XG4gIHN0b3JhZ2Vkcml2ZXI6XG4gICAgZW5hYmxlZDogdHJ1ZVxuICAgIGludGVydmFsOiAxMHNcbiAgICB0aHJlc2hvbGQ6IDMiCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2RhdGEKICAgICAgICB0YXJnZXQ6IC9kYXRhCiAgICAgICAgaXNEaXJlY3Rvcnk6IHRydWUK",
"tags": [
"registry",
"images",
"docker"
],
"logo": "svgs\/docker-registry.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "5000"
},
"dokuwiki": {
"documentation": "https:\/\/www.dokuwiki.org\/",
@@ -143,18 +149,19 @@
"duplicati": {
"documentation": "https:\/\/duplicati.readthedocs.io",
"slogan": "Duplicati is a backup solution, allowing you to make scheduled backups with encryption.",
- "compose": "c2VydmljZXM6CiAgZHVwbGljYXRpOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2R1cGxpY2F0aTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRFVQTElDQVRJCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZHVwbGljYXRpLWNvbmZpZzovY29uZmlnJwogICAgICAtICdkdXBsaWNhdGktYmFja3VwczovYmFja3VwcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MjAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgZHVwbGljYXRpOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2R1cGxpY2F0aTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRFVQTElDQVRJXzgyMDAKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdkdXBsaWNhdGktY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ2R1cGxpY2F0aS1iYWNrdXBzOi9iYWNrdXBzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgyMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"backup",
"encryption"
],
"logo": "svgs\/duplicati.webp",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8200"
},
"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.",
- "compose": "c2VydmljZXM6CiAgZW1ieToKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9lbWJ5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FTUJZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZW1ieS1jb25maWc6L2NvbmZpZycKICAgICAgLSAnZW1ieS10dnNob3dzOi90dnNob3dzJwogICAgICAtICdlbWJ5LW1vdmllczovbW92aWVzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwOTYnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "compose": "c2VydmljZXM6CiAgZW1ieToKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9lbWJ5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FTUJZXzgwOTYKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdlbWJ5LWNvbmZpZzovY29uZmlnJwogICAgICAtICdlbWJ5LXR2c2hvd3M6L3R2c2hvd3MnCiAgICAgIC0gJ2VtYnktbW92aWVzOi9tb3ZpZXMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA5NicKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
"tags": [
"media",
"server",
@@ -163,12 +170,13 @@
"music"
],
"logo": "svgs\/emby.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8096"
},
"embystat": {
"documentation": "https:\/\/github.com\/mregni\/EmbyStat",
"slogan": "EmnyStat is a web analytics tool, designed to provide insight into website traffic and user behavior.",
- "compose": "c2VydmljZXM6CiAgZW1ieXN0YXQ6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZW1ieXN0YXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0VNQllTVEFUCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZW1ieXN0YXQtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NjU1NScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
+ "compose": "c2VydmljZXM6CiAgZW1ieXN0YXQ6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZW1ieXN0YXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0VNQllTVEFUXzY1NTUKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICB2b2x1bWVzOgogICAgICAtICdlbWJ5c3RhdC1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo2NTU1JwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
"tags": [
"media",
"server",
@@ -177,18 +185,20 @@
"music"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "6555"
},
"fider": {
"documentation": "https:\/\/fider.io",
"slogan": "Fider is a feedback platform for collecting and managing user feedback.",
- "compose": "c2VydmljZXM6CiAgZmlkZXI6CiAgICBpbWFnZTogJ2dldGZpZGVyL2ZpZGVyOnN0YWJsZScKICAgIGVudmlyb25tZW50OgogICAgICBCQVNFX1VSTDogJFNFUlZJQ0VfRlFETl9GSURFUgogICAgICBEQVRBQkFTRV9VUkw6ICdwb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfTVlTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUxAZGF0YWJhc2U6NTQzMi9maWRlcj9zc2xtb2RlPWRpc2FibGUnCiAgICAgIEpXVF9TRUNSRVQ6ICRTRVJWSUNFX1BBU1NXT1JEXzY0X0ZJREVSCiAgICAgIEVNQUlMX05PUkVQTFk6ICcke0VNQUlMX05PUkVQTFk6LW5vcmVwbHlAZXhhbXBsZS5jb219JwogICAgICBFTUFJTF9NQUlMR1VOX0FQSTogJEVNQUlMX01BSUxHVU5fQVBJCiAgICAgIEVNQUlMX01BSUxHVU5fRE9NQUlOOiAkRU1BSUxfTUFJTEdVTl9ET01BSU4KICAgICAgRU1BSUxfTUFJTEdVTl9SRUdJT046ICRFTUFJTF9NQUlMR1VOX1JFR0lPTgogICAgICBFTUFJTF9TTVRQX0hPU1Q6ICcke0VNQUlMX1NNVFBfSE9TVDotc210cC5tYWlsZ3VuLmNvbX0nCiAgICAgIEVNQUlMX1NNVFBfUE9SVDogJyR7RU1BSUxfU01UUF9QT1JUOi01ODd9JwogICAgICBFTUFJTF9TTVRQX1VTRVJOQU1FOiAnJHtFTUFJTF9TTVRQX1VTRVJOQU1FOi1wb3N0bWFzdGVyQG1haWxndW4uY29tfScKICAgICAgRU1BSUxfU01UUF9QQVNTV09SRDogJEVNQUlMX1NNVFBfUEFTU1dPUkQKICAgICAgRU1BSUxfU01UUF9FTkFCTEVfU1RBUlRUTFM6ICRFTUFJTF9TTVRQX0VOQUJMRV9TVEFSVFRMUwogICAgICBFTUFJTF9BV1NTRVNfUkVHSU9OOiAkRU1BSUxfQVdTU0VTX1JFR0lPTgogICAgICBFTUFJTF9BV1NTRVNfQUNDRVNTX0tFWV9JRDogJEVNQUlMX0FXU1NFU19BQ0NFU1NfS0VZX0lECiAgICAgIEVNQUlMX0FXU1NFU19TRUNSRVRfQUNDRVNTX0tFWTogJEVNQUlMX0FXU1NFU19TRUNSRVRfQUNDRVNTX0tFWQogIGRhdGFiYXNlOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnX2RhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIFBPU1RHUkVTX1VTRVI6ICRTRVJWSUNFX1VTRVJfTVlTUUwKICAgICAgUE9TVEdSRVNfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIFBPU1RHUkVTX0RCOiAnJHtQT1NUR1JFU19EQjotZmlkZXJ9Jwo=",
+ "compose": "c2VydmljZXM6CiAgZmlkZXI6CiAgICBpbWFnZTogJ2dldGZpZGVyL2ZpZGVyOnN0YWJsZScKICAgIGVudmlyb25tZW50OgogICAgICBCQVNFX1VSTDogJFNFUlZJQ0VfRlFETl9GSURFUl8zMDAwCiAgICAgIERBVEFCQVNFX1VSTDogJ3Bvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9NWVNRTDokU0VSVklDRV9QQVNTV09SRF9NWVNRTEBkYXRhYmFzZTo1NDMyL2ZpZGVyP3NzbG1vZGU9ZGlzYWJsZScKICAgICAgSldUX1NFQ1JFVDogJFNFUlZJQ0VfUEFTU1dPUkRfNjRfRklERVIKICAgICAgRU1BSUxfTk9SRVBMWTogJyR7RU1BSUxfTk9SRVBMWTotbm9yZXBseUBleGFtcGxlLmNvbX0nCiAgICAgIEVNQUlMX01BSUxHVU5fQVBJOiAkRU1BSUxfTUFJTEdVTl9BUEkKICAgICAgRU1BSUxfTUFJTEdVTl9ET01BSU46ICRFTUFJTF9NQUlMR1VOX0RPTUFJTgogICAgICBFTUFJTF9NQUlMR1VOX1JFR0lPTjogJEVNQUlMX01BSUxHVU5fUkVHSU9OCiAgICAgIEVNQUlMX1NNVFBfSE9TVDogJyR7RU1BSUxfU01UUF9IT1NUOi1zbXRwLm1haWxndW4uY29tfScKICAgICAgRU1BSUxfU01UUF9QT1JUOiAnJHtFTUFJTF9TTVRQX1BPUlQ6LTU4N30nCiAgICAgIEVNQUlMX1NNVFBfVVNFUk5BTUU6ICcke0VNQUlMX1NNVFBfVVNFUk5BTUU6LXBvc3RtYXN0ZXJAbWFpbGd1bi5jb219JwogICAgICBFTUFJTF9TTVRQX1BBU1NXT1JEOiAkRU1BSUxfU01UUF9QQVNTV09SRAogICAgICBFTUFJTF9TTVRQX0VOQUJMRV9TVEFSVFRMUzogJEVNQUlMX1NNVFBfRU5BQkxFX1NUQVJUVExTCiAgICAgIEVNQUlMX0FXU1NFU19SRUdJT046ICRFTUFJTF9BV1NTRVNfUkVHSU9OCiAgICAgIEVNQUlMX0FXU1NFU19BQ0NFU1NfS0VZX0lEOiAkRU1BSUxfQVdTU0VTX0FDQ0VTU19LRVlfSUQKICAgICAgRU1BSUxfQVdTU0VTX1NFQ1JFVF9BQ0NFU1NfS0VZOiAkRU1BSUxfQVdTU0VTX1NFQ1JFVF9BQ0NFU1NfS0VZCiAgZGF0YWJhc2U6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjEyJwogICAgdm9sdW1lczoKICAgICAgLSAncGdfZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgUE9TVEdSRVNfVVNFUjogJFNFUlZJQ0VfVVNFUl9NWVNRTAogICAgICBQT1NUR1JFU19QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKICAgICAgUE9TVEdSRVNfREI6ICcke1BPU1RHUkVTX0RCOi1maWRlcn0nCg==",
"tags": [
"feedback",
"user-feedback"
],
"logo": "svgs\/fider.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"filebrowser": {
"documentation": "https:\/\/filebrowser.org",
@@ -207,7 +217,7 @@
"firefly": {
"documentation": "https:\/\/firefly-iii.org",
"slogan": "A personal finances manager that can help you save money.",
- "compose": "c2VydmljZXM6CiAgZmlyZWZseToKICAgIGltYWdlOiAnZmlyZWZseWlpaS9jb3JlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSVJFRkxZCiAgICAgIC0gQVBQX0tFWT0kU0VSVklDRV9CQVNFNjRfQVBQS0VZCiAgICAgIC0gREJfSE9TVD1teXNxbAogICAgICAtIERCX1BPUlQ9MzMwNgogICAgICAtIERCX0NPTk5FQ1RJT049bXlzcWwKICAgICAgLSAnREJfREFUQUJBU0U9JHtNWVNRTF9EQVRBQkFTRTotZmlyZWZseX0nCiAgICAgIC0gREJfVVNFUk5BTUU9JFNFUlZJQ0VfVVNFUl9NWVNRTAogICAgICAtIERCX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIC0gU1RBVElDX0NST05fVE9LRU49JFNFUlZJQ0VfQkFTRTY0X0NST05UT0tFTgogICAgICAtICdUUlVTVEVEX1BST1hJRVM9KicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ZpcmVmbHktdXBsb2FkOi92YXIvd3d3L2h0bWwvc3RvcmFnZS91cGxvYWQnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogICAgZGVwZW5kc19vbjoKICAgICAgbXlzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICBteXNxbDoKICAgIGltYWdlOiAnbWFyaWFkYjpsdHMnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NWVNRTH0nCiAgICAgIC0gJ01ZU1FMX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTH0nCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7TVlTUUxfREFUQUJBU0U6LWZpcmVmbHl9JwogICAgICAtICdNWVNRTF9ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTFJPT1R9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIGxvY2FsaG9zdAogICAgICAgIC0gJy11cm9vdCcKICAgICAgICAtICctcCR7U0VSVklDRV9QQVNTV09SRF9NWVNRTFJPT1R9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICB2b2x1bWVzOgogICAgICAtICdmaXJlZmx5LW15c3FsLWRhdGE6L3Zhci9saWIvbXlzcWwnCiAgY3JvbjoKICAgIGltYWdlOiBhbHBpbmUKICAgIGVudHJ5cG9pbnQ6CiAgICAgIC0gL2VudHJ5cG9pbnQuc2gKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2VudHJ5cG9pbnQuc2gKICAgICAgICB0YXJnZXQ6IC9lbnRyeXBvaW50LnNoCiAgICAgICAgY29udGVudDogIiMhL2Jpbi9zaFxuIyBTdWJzdGl0dXRlIHRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpbnRvIHRoZSBjcm9uIGNvbW1hbmRcbkNST05fQ09NTUFORD1cIjAgMyAqICogKiB3Z2V0IC1xTy0gaHR0cDovL2ZpcmVmbHk6ODA4MC9hcGkvdjEvY3Jvbi8ke1NUQVRJQ19DUk9OX1RPS0VOfVwiXG4jIEFkZCB0aGUgY3JvbiBjb21tYW5kIHRvIHRoZSBjcm9udGFiXG5lY2hvIFwiJENST05fQ09NTUFORFwiIHwgY3JvbnRhYiAtXG4jIFN0YXJ0IHRoZSBjcm9uIGRhZW1vbiBpbiB0aGUgZm9yZWdyb3VuZCB3aXRoIGxvZ2dpbmcgdG8gc3Rkb3V0XG5jcm9uZCAtZiAtTCAvZGV2L3N0ZG91dCIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNUQVRJQ19DUk9OX1RPS0VOPSRTRVJWSUNFX0JBU0U2NF9DUk9OVE9LRU4K",
+ "compose": "c2VydmljZXM6CiAgZmlyZWZseToKICAgIGltYWdlOiAnZmlyZWZseWlpaS9jb3JlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSVJFRkxZXzgwODAKICAgICAgLSBBUFBfS0VZPSRTRVJWSUNFX0JBU0U2NF9BUFBLRVkKICAgICAgLSBEQl9IT1NUPW15c3FsCiAgICAgIC0gREJfUE9SVD0zMzA2CiAgICAgIC0gREJfQ09OTkVDVElPTj1teXNxbAogICAgICAtICdEQl9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFOi1maXJlZmx5fScKICAgICAgLSBEQl9VU0VSTkFNRT0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUwKICAgICAgLSBTVEFUSUNfQ1JPTl9UT0tFTj0kU0VSVklDRV9CQVNFNjRfQ1JPTlRPS0VOCiAgICAgIC0gJ1RSVVNURURfUFJPWElFUz0qJwogICAgdm9sdW1lczoKICAgICAgLSAnZmlyZWZseS11cGxvYWQ6L3Zhci93d3cvaHRtbC9zdG9yYWdlL3VwbG9hZCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MDgwJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdtYXJpYWRiOmx0cycKICAgIGVudmlyb25tZW50OgogICAgICAtICdNWVNRTF9VU0VSPSR7U0VSVklDRV9VU0VSX01ZU1FMfScKICAgICAgLSAnTVlTUUxfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtNWVNRTF9EQVRBQkFTRTotZmlyZWZseX0nCiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbXlzcWxhZG1pbgogICAgICAgIC0gcGluZwogICAgICAgIC0gJy1oJwogICAgICAgIC0gbG9jYWxob3N0CiAgICAgICAgLSAnLXVyb290JwogICAgICAgIC0gJy1wJHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2ZpcmVmbHktbXlzcWwtZGF0YTovdmFyL2xpYi9teXNxbCcKICBjcm9uOgogICAgaW1hZ2U6IGFscGluZQogICAgZW50cnlwb2ludDoKICAgICAgLSAvZW50cnlwb2ludC5zaAogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vZW50cnlwb2ludC5zaAogICAgICAgIHRhcmdldDogL2VudHJ5cG9pbnQuc2gKICAgICAgICBjb250ZW50OiAiIyEvYmluL3NoXG4jIFN1YnN0aXR1dGUgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlIGludG8gdGhlIGNyb24gY29tbWFuZFxuQ1JPTl9DT01NQU5EPVwiMCAzICogKiAqIHdnZXQgLXFPLSBodHRwOi8vZmlyZWZseTo4MDgwL2FwaS92MS9jcm9uLyR7U1RBVElDX0NST05fVE9LRU59XCJcbiMgQWRkIHRoZSBjcm9uIGNvbW1hbmQgdG8gdGhlIGNyb250YWJcbmVjaG8gXCIkQ1JPTl9DT01NQU5EXCIgfCBjcm9udGFiIC1cbiMgU3RhcnQgdGhlIGNyb24gZGFlbW9uIGluIHRoZSBmb3JlZ3JvdW5kIHdpdGggbG9nZ2luZyB0byBzdGRvdXRcbmNyb25kIC1mIC1MIC9kZXYvc3Rkb3V0IgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU1RBVElDX0NST05fVE9LRU49JFNFUlZJQ0VfQkFTRTY0X0NST05UT0tFTgo=",
"tags": [
"finance",
"money",
@@ -215,12 +225,13 @@
"manager"
],
"logo": "svgs\/firefly.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"formbricks": {
"documentation": "https:\/\/formbricks.com",
"slogan": "Open Source Experience Management",
- "compose": "c2VydmljZXM6CiAgZm9ybWJyaWNrczoKICAgIGltYWdlOiAnZm9ybWJyaWNrcy9mb3JtYnJpY2tzOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTCiAgICAgIC0gV0VCQVBQX1VSTD0kU0VSVklDRV9GUUROX0ZPUk1CUklDS1MKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXNxbDo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZm9ybWJyaWNrc30nCiAgICAgIC0gTkVYVEFVVEhfU0VDUkVUPSRTRVJWSUNFX0JBU0U2NF82NF9ORVhUQVVUSAogICAgICAtIE5FWFRBVVRIX1VSTD0kU0VSVklDRV9GUUROX0ZPUk1CUklDS1MKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ01BSUxfRlJPTT0ke01BSUxfRlJPTTotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ1NNVFBfSE9TVD0ke1NNVFBfSE9TVDotdGVzdC5leGFtcGxlLmNvbX0nCiAgICAgIC0gJ1NNVFBfUE9SVD0ke1NNVFBfUE9SVDotNTg3fScKICAgICAgLSAnU01UUF9VU0VSPSR7U01UUF9VU0VSOi10ZXN0fScKICAgICAgLSAnU01UUF9QQVNTV09SRD0ke1NNVFBfUEFTU1dPUkQ6LXRlc3R9JwogICAgICAtICdTTVRQX1NFQ1VSRV9FTkFCTEVEPSR7U01UUF9TRUNVUkVfRU5BQkxFRDotMH0nCiAgICAgIC0gJ1NIT1JUX1VSTF9CQVNFPSR7U0hPUlRfVVJMX0JBU0V9JwogICAgICAtICdFTUFJTF9WRVJJRklDQVRJT05fRElTQUJMRUQ9JHtFTUFJTF9WRVJJRklDQVRJT05fRElTQUJMRUQ6LTF9JwogICAgICAtICdQQVNTV09SRF9SRVNFVF9ESVNBQkxFRD0ke1BBU1NXT1JEX1JFU0VUX0RJU0FCTEVEOi0xfScKICAgICAgLSAnU0lHTlVQX0RJU0FCTEVEPSR7U0lHTlVQX0RJU0FCTEVEOi0wfScKICAgICAgLSAnSU5WSVRFX0RJU0FCTEVEPSR7SU5WSVRFX0RJU0FCTEVEOi0wfScKICAgICAgLSAnUFJJVkFDWV9VUkw9JHtQUklWQUNZX1VSTH0nCiAgICAgIC0gJ1RFUk1TX1VSTD0ke1RFUk1TX1VSTH0nCiAgICAgIC0gJ0lNUFJJTlRfVVJMPSR7SU1QUklOVF9VUkx9JwogICAgICAtICdHSVRIVUJfQVVUSF9FTkFCTEVEPSR7R0lUSFVCX0FVVEhfRU5BQkxFRDotMH0nCiAgICAgIC0gJ0dJVEhVQl9JRD0ke0dJVEhVQl9JRH0nCiAgICAgIC0gJ0dJVEhVQl9TRUNSRVQ9JHtHSVRIVUJfU0VDUkVUfScKICAgICAgLSAnR09PR0xFX0FVVEhfRU5BQkxFRD0ke0dPT0dMRV9BVVRIX0VOQUJMRUQ6LTB9JwogICAgICAtICdHT09HTEVfQ0xJRU5UX0lEPSR7R09PR0xFX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0dPT0dMRV9DTElFTlRfU0VDUkVUPSR7R09PR0xFX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdBU1NFVF9QUkVGSVhfVVJMPSR7QVNTRVRfUFJFRklYX1VSTH0nCiAgICB2b2x1bWVzOgogICAgICAtICdmb3JtYnJpY2tzLXVwbG9hZHM6L2FwcHMvd2ViL3VwbG9hZHMvJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdmb3JtYnJpY2tzLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWZvcm1icmlja3N9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
+ "compose": "c2VydmljZXM6CiAgZm9ybWJyaWNrczoKICAgIGltYWdlOiAnZm9ybWJyaWNrcy9mb3JtYnJpY2tzOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GT1JNQlJJQ0tTXzMwMDAKICAgICAgLSBXRUJBUFBfVVJMPSRTRVJWSUNFX0ZRRE5fRk9STUJSSUNLUwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlc3FsOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1mb3JtYnJpY2tzfScKICAgICAgLSBORVhUQVVUSF9TRUNSRVQ9JFNFUlZJQ0VfQkFTRTY0XzY0X05FWFRBVVRICiAgICAgIC0gTkVYVEFVVEhfVVJMPSRTRVJWSUNFX0ZRRE5fRk9STUJSSUNLUwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfScKICAgICAgLSAnTUFJTF9GUk9NPSR7TUFJTF9GUk9NOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnU01UUF9IT1NUPSR7U01UUF9IT1NUOi10ZXN0LmV4YW1wbGUuY29tfScKICAgICAgLSAnU01UUF9QT1JUPSR7U01UUF9QT1JUOi01ODd9JwogICAgICAtICdTTVRQX1VTRVI9JHtTTVRQX1VTRVI6LXRlc3R9JwogICAgICAtICdTTVRQX1BBU1NXT1JEPSR7U01UUF9QQVNTV09SRDotdGVzdH0nCiAgICAgIC0gJ1NNVFBfU0VDVVJFX0VOQUJMRUQ9JHtTTVRQX1NFQ1VSRV9FTkFCTEVEOi0wfScKICAgICAgLSAnU0hPUlRfVVJMX0JBU0U9JHtTSE9SVF9VUkxfQkFTRX0nCiAgICAgIC0gJ0VNQUlMX1ZFUklGSUNBVElPTl9ESVNBQkxFRD0ke0VNQUlMX1ZFUklGSUNBVElPTl9ESVNBQkxFRDotMX0nCiAgICAgIC0gJ1BBU1NXT1JEX1JFU0VUX0RJU0FCTEVEPSR7UEFTU1dPUkRfUkVTRVRfRElTQUJMRUQ6LTF9JwogICAgICAtICdTSUdOVVBfRElTQUJMRUQ9JHtTSUdOVVBfRElTQUJMRUQ6LTB9JwogICAgICAtICdJTlZJVEVfRElTQUJMRUQ9JHtJTlZJVEVfRElTQUJMRUQ6LTB9JwogICAgICAtICdQUklWQUNZX1VSTD0ke1BSSVZBQ1lfVVJMfScKICAgICAgLSAnVEVSTVNfVVJMPSR7VEVSTVNfVVJMfScKICAgICAgLSAnSU1QUklOVF9VUkw9JHtJTVBSSU5UX1VSTH0nCiAgICAgIC0gJ0dJVEhVQl9BVVRIX0VOQUJMRUQ9JHtHSVRIVUJfQVVUSF9FTkFCTEVEOi0wfScKICAgICAgLSAnR0lUSFVCX0lEPSR7R0lUSFVCX0lEfScKICAgICAgLSAnR0lUSFVCX1NFQ1JFVD0ke0dJVEhVQl9TRUNSRVR9JwogICAgICAtICdHT09HTEVfQVVUSF9FTkFCTEVEPSR7R09PR0xFX0FVVEhfRU5BQkxFRDotMH0nCiAgICAgIC0gJ0dPT0dMRV9DTElFTlRfSUQ9JHtHT09HTEVfQ0xJRU5UX0lEfScKICAgICAgLSAnR09PR0xFX0NMSUVOVF9TRUNSRVQ9JHtHT09HTEVfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ0FTU0VUX1BSRUZJWF9VUkw9JHtBU1NFVF9QUkVGSVhfVVJMfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Zvcm1icmlja3MtdXBsb2FkczovYXBwcy93ZWIvdXBsb2Fkcy8nCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Zvcm1icmlja3MtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZm9ybWJyaWNrc30nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": [
"form",
"builder",
@@ -232,12 +243,13 @@
"docker"
],
"logo": "svgs\/formbricks.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"ghost": {
"documentation": "https:\/\/ghost.org",
"slogan": "Ghost is a content management system (CMS) and blogging platform.",
- "compose": "c2VydmljZXM6CiAgZ2hvc3Q6CiAgICBpbWFnZTogJ2dob3N0OjUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1jb250ZW50LWRhdGE6L3Zhci9saWIvZ2hvc3QvY29udGVudCcKICAgIGVudmlyb25tZW50OgogICAgICAtIHVybD0kU0VSVklDRV9GUUROX0dIT1NUCiAgICAgIC0gZGF0YWJhc2VfX2NsaWVudD1teXNxbAogICAgICAtIGRhdGFiYXNlX19jb25uZWN0aW9uX19ob3N0PW15c3FsCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX3VzZXI9JFNFUlZJQ0VfVVNFUl9NWVNRTAogICAgICAtIGRhdGFiYXNlX19jb25uZWN0aW9uX19wYXNzd29yZD0kU0VSVklDRV9QQVNTV09SRF9NWVNRTAogICAgICAtICdkYXRhYmFzZV9fY29ubmVjdGlvbl9fZGF0YWJhc2U9JHtNWVNRTF9EQVRBQkFTRS1naG9zdH0nCiAgICAgIC0gbWFpbF9fdHJhbnNwb3J0PVNNVFAKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fYXV0aF9fcGFzcz0ke01BSUxfT1BUSU9OU19BVVRIX1BBU1N9JwogICAgICAtICdtYWlsX19vcHRpb25zX19hdXRoX191c2VyPSR7TUFJTF9PUFRJT05TX0FVVEhfVVNFUn0nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX3NlY3VyZT0ke01BSUxfT1BUSU9OU19TRUNVUkU6LXRydWV9JwogICAgICAtICdtYWlsX19vcHRpb25zX19wb3J0PSR7TUFJTF9PUFRJT05TX1BPUlQ6LTQ2NX0nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX3NlcnZpY2U9JHtNQUlMX09QVElPTlNfU0VSVklDRTotTWFpbGd1bn0nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX2hvc3Q9JHtNQUlMX09QVElPTlNfSE9TVH0nCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSBsb2NhbGhvc3QKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
+ "compose": "c2VydmljZXM6CiAgZ2hvc3Q6CiAgICBpbWFnZTogJ2dob3N0OjUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1jb250ZW50LWRhdGE6L3Zhci9saWIvZ2hvc3QvY29udGVudCcKICAgIGVudmlyb25tZW50OgogICAgICAtIHVybD0kU0VSVklDRV9GUUROX0dIT1NUXzIzNjgKICAgICAgLSBkYXRhYmFzZV9fY2xpZW50PW15c3FsCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX2hvc3Q9bXlzcWwKICAgICAgLSBkYXRhYmFzZV9fY29ubmVjdGlvbl9fdXNlcj0kU0VSVklDRV9VU0VSX01ZU1FMCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX3Bhc3N3b3JkPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIC0gJ2RhdGFiYXNlX19jb25uZWN0aW9uX19kYXRhYmFzZT0ke01ZU1FMX0RBVEFCQVNFLWdob3N0fScKICAgICAgLSBtYWlsX190cmFuc3BvcnQ9U01UUAogICAgICAtICdtYWlsX19vcHRpb25zX19hdXRoX19wYXNzPSR7TUFJTF9PUFRJT05TX0FVVEhfUEFTU30nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX2F1dGhfX3VzZXI9JHtNQUlMX09QVElPTlNfQVVUSF9VU0VSfScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fc2VjdXJlPSR7TUFJTF9PUFRJT05TX1NFQ1VSRTotdHJ1ZX0nCiAgICAgIC0gJ21haWxfX29wdGlvbnNfX3BvcnQ9JHtNQUlMX09QVElPTlNfUE9SVDotNDY1fScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19fc2VydmljZT0ke01BSUxfT1BUSU9OU19TRVJWSUNFOi1NYWlsZ3VufScKICAgICAgLSAnbWFpbF9fb3B0aW9uc19faG9zdD0ke01BSUxfT1BUSU9OU19IT1NUfScKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgbXlzcWw6CiAgICBpbWFnZTogJ215c3FsOjguMCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dob3N0LW15c3FsLWRhdGE6L3Zhci9saWIvbXlzcWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NWVNRTH0nCiAgICAgIC0gJ01ZU1FMX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTH0nCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7TVlTUUxfREFUQUJBU0V9JwogICAgICAtICdNWVNRTF9ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTFJPT1R9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIGxvY2FsaG9zdAogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": [
"cms",
"blog",
@@ -246,7 +258,8 @@
"system"
],
"logo": "svgs\/ghost.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "2368"
},
"gitea-with-mariadb": {
"documentation": "https:\/\/docs.gitea.com",
@@ -310,7 +323,7 @@
"glitchtip": {
"documentation": "https:\/\/glitchtip.com",
"slogan": "GlitchTip is a self-hosted, open-source error tracking system.",
- "compose": "dmVyc2lvbjogJzMuOCcKc2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgd2ViOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dMSVRDSFRJUAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdHTElUQ0hUSVBfRE9NQUlOPSR7U0VSVklDRV9GUUROX0dMSVRDSFRJUH0nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9JwogICAgdm9sdW1lczoKICAgICAgLSAndXBsb2FkczovY29kZS91cGxvYWRzJwogIHdvcmtlcjoKICAgIGltYWdlOiBnbGl0Y2h0aXAvZ2xpdGNodGlwCiAgICBjb21tYW5kOiAuL2Jpbi9ydW4tY2VsZXJ5LXdpdGgtYmVhdC5zaAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3JlcwogICAgICAtIHJlZGlzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7REVGQVVMVF9GUk9NX0VNQUlMOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9BVVRPU0NBTEU9JHtDRUxFUllfV09SS0VSX0FVVE9TQ0FMRTotMSwzfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEPSR7Q0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEOi0xMDAwMH0nCiAgICB2b2x1bWVzOgogICAgICAtICd1cGxvYWRzOi9jb2RlL3VwbG9hZHMnCiAgbWlncmF0ZToKICAgIGltYWdlOiBnbGl0Y2h0aXAvZ2xpdGNodGlwCiAgICByZXN0YXJ0OiAnbm8nCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGNvbW1hbmQ6ICcuL21hbmFnZS5weSBtaWdyYXRlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1nbGl0Y2h0aXB9JwogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnRU1BSUxfVVJMPSR7RU1BSUxfVVJMOi1jb25zb2xlbWFpbDovL30nCiAgICAgIC0gJ0RFRkFVTFRfRlJPTV9FTUFJTD0ke0RFRkFVTFRfRlJPTV9FTUFJTDotdGVzdEBleGFtcGxlLmNvbX0nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFPSR7Q0VMRVJZX1dPUktFUl9BVVRPU0NBTEU6LTEsM30nCiAgICAgIC0gJ0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRD0ke0NFTEVSWV9XT1JLRVJfTUFYX1RBU0tTX1BFUl9DSElMRDotMTAwMDB9Jwo=",
+ "compose": "dmVyc2lvbjogJzMuOCcKc2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogIHJlZGlzOgogICAgaW1hZ2U6IHJlZGlzCiAgd2ViOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dMSVRDSFRJUF84MDgwCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfUE9TVEdSRVNRTDokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMQHBvc3RncmVzOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1nbGl0Y2h0aXB9JwogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfQkFTRTY0XzY0X0VOQ1JZUFRJT04KICAgICAgLSAnRU1BSUxfVVJMPSR7RU1BSUxfVVJMOi1jb25zb2xlbWFpbDovL30nCiAgICAgIC0gJ0dMSVRDSFRJUF9ET01BSU49JHtTRVJWSUNFX0ZRRE5fR0xJVENIVElQfScKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7REVGQVVMVF9GUk9NX0VNQUlMOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9BVVRPU0NBTEU9JHtDRUxFUllfV09SS0VSX0FVVE9TQ0FMRTotMSwzfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEPSR7Q0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEOi0xMDAwMH0nCiAgICB2b2x1bWVzOgogICAgICAtICd1cGxvYWRzOi9jb2RlL3VwbG9hZHMnCiAgd29ya2VyOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIGNvbW1hbmQ6IC4vYmluL3J1bi1jZWxlcnktd2l0aC1iZWF0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTEBwb3N0Z3Jlczo1NDMyLyR7UE9TVEdSRVNRTF9EQVRBQkFTRTotZ2xpdGNodGlwfScKICAgICAgLSBTRUNSRVRfS0VZPSRTRVJWSUNFX0JBU0U2NF82NF9FTkNSWVBUSU9OCiAgICAgIC0gJ0VNQUlMX1VSTD0ke0VNQUlMX1VSTDotY29uc29sZW1haWw6Ly99JwogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtERUZBVUxUX0ZST01fRU1BSUw6LXRlc3RAZXhhbXBsZS5jb219JwogICAgICAtICdDRUxFUllfV09SS0VSX0FVVE9TQ0FMRT0ke0NFTEVSWV9XT1JLRVJfQVVUT1NDQUxFOi0xLDN9JwogICAgICAtICdDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ9JHtDRUxFUllfV09SS0VSX01BWF9UQVNLU19QRVJfQ0hJTEQ6LTEwMDAwfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwbG9hZHM6L2NvZGUvdXBsb2FkcycKICBtaWdyYXRlOgogICAgaW1hZ2U6IGdsaXRjaHRpcC9nbGl0Y2h0aXAKICAgIHJlc3RhcnQ6ICdubycKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSByZWRpcwogICAgY29tbWFuZDogJy4vbWFuYWdlLnB5IG1pZ3JhdGUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUxAcG9zdGdyZXM6NTQzMi8ke1BPU1RHUkVTUUxfREFUQUJBU0U6LWdsaXRjaHRpcH0nCiAgICAgIC0gU0VDUkVUX0tFWT0kU0VSVklDRV9CQVNFNjRfNjRfRU5DUllQVElPTgogICAgICAtICdFTUFJTF9VUkw9JHtFTUFJTF9VUkw6LWNvbnNvbGVtYWlsOi8vfScKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7REVGQVVMVF9GUk9NX0VNQUlMOi10ZXN0QGV4YW1wbGUuY29tfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9BVVRPU0NBTEU9JHtDRUxFUllfV09SS0VSX0FVVE9TQ0FMRTotMSwzfScKICAgICAgLSAnQ0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEPSR7Q0VMRVJZX1dPUktFUl9NQVhfVEFTS1NfUEVSX0NISUxEOi0xMDAwMH0nCg==",
"tags": [
"error",
"tracking",
@@ -319,7 +332,8 @@
"sentry"
],
"logo": "svgs\/glitchtip.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"grafana-with-postgresql": {
"documentation": "https:\/\/grafana.com",
From d4d0330f70b78adbe01accfe311d8b402d5f0bd6 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 17:35:53 +0100
Subject: [PATCH 50/97] Fix reloadCaddy() method call in delete() function
---
app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
index d27e574d7..a9c01daed 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
@@ -23,7 +23,7 @@ class DynamicConfigurationNavbar extends Component
}
instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server);
if ($proxy_type === 'CADDY') {
- // $server->reloadCaddy();
+ $server->reloadCaddy();
}
$this->dispatch('success', 'File deleted.');
$this->dispatch('loadDynamicConfigurations');
From 0519ce2001829ac8db19ef210444b95700d28550 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 17:36:32 +0100
Subject: [PATCH 51/97] Fix reloadCaddy() method call in
addDynamicConfiguration()
---
app/Livewire/Server/Proxy/NewDynamicConfiguration.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
index c433ce4fc..06180d947 100644
--- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
+++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php
@@ -71,7 +71,7 @@ class NewDynamicConfiguration extends Component
"echo '{$base64_value}' | base64 -d > {$file}",
], $this->server);
if ($proxy_type === 'CADDY') {
- // $this->server->reloadCaddy();
+ $this->server->reloadCaddy();
}
$this->dispatch('loadDynamicConfigurations');
$this->dispatch('dynamic-configuration-added');
From 87b56d538d37caada442a92987d29a0b8497b4ca Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 17:43:00 +0100
Subject: [PATCH 52/97] Add Grafana service configuration and update service
templates
---
app/Models/Service.php | 23 +++++++++++++++++++++++
templates/service-templates.json | 10 ++++++----
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 3e6d2b9db..b3430ac0a 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -102,6 +102,29 @@ class Service extends BaseModel
foreach ($applications as $application) {
$image = str($application->image)->before(':')->value();
switch ($image) {
+ case str($image)?->contains('grafana'):
+ $data = collect([]);
+ $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GRAFANA')->first();
+ $data = $data->merge([
+ 'Admin User' => [
+ 'key' => 'GF_SECURITY_ADMIN_USER',
+ 'value' => 'admin',
+ 'readonly' => true,
+ 'rules' => 'required',
+ ],
+ ]);
+ if ($admin_password) {
+ $data = $data->merge([
+ 'Admin Password' => [
+ 'key' => 'GF_SECURITY_ADMIN_PASSWORD',
+ 'value' => data_get($admin_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ ]);
+ }
+ $fields->put('Grafana', $data);
+ break;
case str($image)?->contains('directus'):
$data = collect([]);
$admin_email = $this->environment_variables()->where('key', 'ADMIN_EMAIL')->first();
diff --git a/templates/service-templates.json b/templates/service-templates.json
index a094448de..2b9838e66 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -338,7 +338,7 @@
"grafana-with-postgresql": {
"documentation": "https:\/\/grafana.com",
"slogan": "Grafana is the open source analytics & monitoring solution for every database.",
- "compose": "c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQQogICAgICAtICdHRl9TRVJWRVJfUk9PVF9VUkw9JHtTRVJWSUNFX0ZRRE5fR1JBRkFOQX0nCiAgICAgIC0gJ0dGX1NFUlZFUl9ET01BSU49JHtTRVJWSUNFX0ZRRE5fR1JBRkFOQX0nCiAgICAgIC0gJ0dGX1NFQ1VSSVRZX0FETUlOX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9HUkFGQU5BfScKICAgICAgLSBHRl9EQVRBQkFTRV9UWVBFPXBvc3RncmVzCiAgICAgIC0gR0ZfREFUQUJBU0VfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gR0ZfREFUQUJBU0VfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gR0ZfREFUQUJBU0VfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnR0ZfREFUQUJBU0VfTkFNRT0ke1BPU1RHUkVTX0RCOi1ncmFmYW5hfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dyYWZhbmEtZGF0YTovdmFyL2xpYi9ncmFmYW5hJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjMwMDAvYXBpL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWdyYWZhbmF9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
+ "compose": "c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQV8zMDAwCiAgICAgIC0gJ0dGX1NFUlZFUl9ST09UX1VSTD0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VDVVJJVFlfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0dSQUZBTkF9JwogICAgICAtIEdGX0RBVEFCQVNFX1RZUEU9cG9zdGdyZXMKICAgICAgLSBHRl9EQVRBQkFTRV9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBHRl9EQVRBQkFTRV9VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBHRl9EQVRBQkFTRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdHRl9EQVRBQkFTRV9OQU1FPSR7UE9TVEdSRVNfREI6LWdyYWZhbmF9JwogICAgdm9sdW1lczoKICAgICAgLSAnZ3JhZmFuYS1kYXRhOi92YXIvbGliL2dyYWZhbmEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hcGkvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzcWwKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZ3JhZmFuYX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": [
"grafana",
"analytics",
@@ -346,12 +346,13 @@
"dashboard"
],
"logo": "svgs\/grafana.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"grafana": {
"documentation": "https:\/\/grafana.com",
"slogan": "Grafana is the open source analytics & monitoring solution for every database.",
- "compose": "c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQQogICAgICAtICdHRl9TRVJWRVJfUk9PVF9VUkw9JHtTRVJWSUNFX0ZRRE5fR1JBRkFOQX0nCiAgICAgIC0gJ0dGX1NFUlZFUl9ET01BSU49JHtTRVJWSUNFX0ZRRE5fR1JBRkFOQX0nCiAgICAgIC0gJ0dGX1NFQ1VSSVRZX0FETUlOX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9HUkFGQU5BfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2dyYWZhbmEtZGF0YTovdmFyL2xpYi9ncmFmYW5hJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjMwMDAvYXBpL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
+ "compose": "c2VydmljZXM6CiAgZ3JhZmFuYToKICAgIGltYWdlOiBncmFmYW5hL2dyYWZhbmEtb3NzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fR1JBRkFOQV8zMDAwCiAgICAgIC0gJ0dGX1NFUlZFUl9ST09UX1VSTD0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VSVkVSX0RPTUFJTj0ke1NFUlZJQ0VfRlFETl9HUkFGQU5BfScKICAgICAgLSAnR0ZfU0VDVVJJVFlfQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0dSQUZBTkF9JwogICAgdm9sdW1lczoKICAgICAgLSAnZ3JhZmFuYS1kYXRhOi92YXIvbGliL2dyYWZhbmEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hcGkvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": [
"grafana",
"analytics",
@@ -359,7 +360,8 @@
"dashboard"
],
"logo": "svgs\/grafana.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"grocy": {
"documentation": "https:\/\/github.com\/grocy\/grocy",
From 7027931095359ba7e41e86012fb197a0e53d7c7c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 17:47:32 +0100
Subject: [PATCH 53/97] Add port configuration for services
---
templates/compose/jellyfin.yaml | 3 ++-
templates/compose/kuzzle.yaml | 2 +-
templates/compose/mattermost.yaml | 2 +-
templates/compose/meilisearch.yaml | 3 ++-
templates/service-templates.json | 13 ++++++++-----
5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/templates/compose/jellyfin.yaml b/templates/compose/jellyfin.yaml
index ab093cc76..47149869f 100644
--- a/templates/compose/jellyfin.yaml
+++ b/templates/compose/jellyfin.yaml
@@ -2,12 +2,13 @@
# slogan: Jellyfin is a media server for hosting and streaming your media collection.
# tags: media, server, movies, tv, music
# logo: svgs/jellyfin.svg
+# port: 8096
services:
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
environment:
- - SERVICE_FQDN_JELLYFIN
+ - SERVICE_FQDN_JELLYFIN_8096
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
diff --git a/templates/compose/kuzzle.yaml b/templates/compose/kuzzle.yaml
index f228e9baf..e62509d82 100644
--- a/templates/compose/kuzzle.yaml
+++ b/templates/compose/kuzzle.yaml
@@ -1,7 +1,7 @@
# documentation: https://kuzzle.io
# slogan: Kuzzle is a generic backend offering the basic building blocks common to every application.
# tags: backend, api, realtime, websocket, mqtt, rest, sdk, iot, geofencing, low-code
-
+# port: 7512
services:
redis:
diff --git a/templates/compose/mattermost.yaml b/templates/compose/mattermost.yaml
index 2afe9a537..ce3e09531 100644
--- a/templates/compose/mattermost.yaml
+++ b/templates/compose/mattermost.yaml
@@ -5,7 +5,7 @@
services:
mattermost:
- image: mattermost/mattermost-team-edition:9.1
+ image: mattermost/mattermost-team-edition:9
volumes:
- mattermost-data:/mattermost
environment:
diff --git a/templates/compose/meilisearch.yaml b/templates/compose/meilisearch.yaml
index 4daab4f9a..6dff24e5d 100644
--- a/templates/compose/meilisearch.yaml
+++ b/templates/compose/meilisearch.yaml
@@ -2,12 +2,13 @@
# slogan: MeiliSearch is a powerful, fast, easy to use and deploy search engine.
# tags: search,engine,fulltext,full,text,meilisearch
# logo: svgs/meilisearch.svg
+# port: 7700
services:
meilisearch:
image: getmeili/meilisearch:latest
environment:
- - SERVICE_FQDN_MEILISEARCH
+ - SERVICE_FQDN_MEILISEARCH_7700
- MEILI_NO_ANALYTICS=${MEILI_NO_ANALYTICS:-true}
- MEILI_ENV=${MEILI_ENV:-production}
- MEILI_MASTER_KEY=${SERVICE_PASSWORD_MEILISEARCH}
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 2b9838e66..5d8a8c751 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -393,7 +393,7 @@
"jellyfin": {
"documentation": "https:\/\/jellyfin.org",
"slogan": "Jellyfin is a media server for hosting and streaming your media collection.",
- "compose": "c2VydmljZXM6CiAgamVsbHlmaW46CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvamVsbHlmaW46bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0pFTExZRklOCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIEpFTExZRklOX1B1Ymxpc2hlZFNlcnZlclVybD0kU0VSVklDRV9GUUROX0pFTExZRklOCiAgICB2b2x1bWVzOgogICAgICAtICdqZWxseWZpbi1jb25maWc6L2NvbmZpZycKICAgICAgLSAnamVsbHlmaW4tdHZzaG93czovZGF0YS90dnNob3dzJwogICAgICAtICdqZWxseWZpbi1tb3ZpZXM6L2RhdGEvbW92aWVzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwOTYnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "compose": "c2VydmljZXM6CiAgamVsbHlmaW46CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvamVsbHlmaW46bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0pFTExZRklOXzgwOTYKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gSkVMTFlGSU5fUHVibGlzaGVkU2VydmVyVXJsPSRTRVJWSUNFX0ZRRE5fSkVMTFlGSU4KICAgIHZvbHVtZXM6CiAgICAgIC0gJ2plbGx5ZmluLWNvbmZpZzovY29uZmlnJwogICAgICAtICdqZWxseWZpbi10dnNob3dzOi9kYXRhL3R2c2hvd3MnCiAgICAgIC0gJ2plbGx5ZmluLW1vdmllczovZGF0YS9tb3ZpZXMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA5NicKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
"tags": [
"media",
"server",
@@ -402,7 +402,8 @@
"music"
],
"logo": "svgs\/jellyfin.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8096"
},
"kuzzle": {
"documentation": "https:\/\/kuzzle.io",
@@ -421,12 +422,13 @@
"low-code"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "7512"
},
"meilisearch": {
"documentation": "https:\/\/www.meilisearch.com",
"slogan": "MeiliSearch is a powerful, fast, easy to use and deploy search engine.",
- "compose": "c2VydmljZXM6CiAgbWVpbGlzZWFyY2g6CiAgICBpbWFnZTogJ2dldG1laWxpL21laWxpc2VhcmNoOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRUlMSVNFQVJDSAogICAgICAtICdNRUlMSV9OT19BTkFMWVRJQ1M9JHtNRUlMSV9OT19BTkFMWVRJQ1M6LXRydWV9JwogICAgICAtICdNRUlMSV9FTlY9JHtNRUlMSV9FTlY6LXByb2R1Y3Rpb259JwogICAgICAtICdNRUlMSV9NQVNURVJfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9NRUlMSVNFQVJDSH0nCiAgICB2b2x1bWVzOgogICAgICAtICdtZWlsaXNlYXJjaC1kYXRhOi9tZWlsaV9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0Ojc3MDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgbWVpbGlzZWFyY2g6CiAgICBpbWFnZTogJ2dldG1laWxpL21laWxpc2VhcmNoOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRUlMSVNFQVJDSF83NzAwCiAgICAgIC0gJ01FSUxJX05PX0FOQUxZVElDUz0ke01FSUxJX05PX0FOQUxZVElDUzotdHJ1ZX0nCiAgICAgIC0gJ01FSUxJX0VOVj0ke01FSUxJX0VOVjotcHJvZHVjdGlvbn0nCiAgICAgIC0gJ01FSUxJX01BU1RFUl9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01FSUxJU0VBUkNIfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21laWxpc2VhcmNoLWRhdGE6L21laWxpX2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NzcwMC9oZWFsdGgnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"search",
"engine",
@@ -436,7 +438,8 @@
"meilisearch"
],
"logo": "svgs\/meilisearch.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "7700"
},
"metabase": {
"documentation": "https:\/\/www.metabase.com",
From 336d44a5ccb76897575c8c6344972c3c9977c6ab Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 19:08:52 +0100
Subject: [PATCH 54/97] Refactor fqdnLabelsForCaddy function to handle
serviceLabels parameter
---
bootstrap/helpers/docker.php | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index f9da5fe3d..638700050 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -219,6 +219,11 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource,
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null)
{
$labels = collect([]);
+ if ($serviceLabels) {
+ $labels->push("caddy_ingress_network={$uuid}");
+ } else {
+ $labels->push("caddy_ingress_network={$network}");
+ }
foreach ($domains as $loop => $domain) {
$loop = $loop;
$url = Url::fromString($domain);
@@ -236,14 +241,12 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
if ($serviceLabels) {
- $labels->push("caddy_ingress_network={$uuid}");
if ($port) {
$labels->push("caddy_{$loop}.reverse_proxy={{upstreams $port}}");
} else {
$labels->push("caddy_{$loop}.reverse_proxy={{upstreams}}");
}
} else {
- $labels->push("caddy_ingress_network={$network}");
if ($port) {
$labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams $port}}");
} else {
From c835c02bf2a82d054e1fbd90f382e0ac46298de5 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 19:09:08 +0100
Subject: [PATCH 55/97] Update port numbers for services
---
bootstrap/helpers/shared.php | 29 +++++++++----------
.../livewire/project/new/select.blade.php | 4 +--
templates/compose/metabase.yaml | 3 +-
templates/compose/metube.yaml | 3 +-
templates/compose/moodle.yaml | 3 +-
templates/compose/n8n.yaml | 3 +-
templates/service-templates.json | 20 ++++++++-----
7 files changed, 35 insertions(+), 30 deletions(-)
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 28d7fc164..1a99ff414 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -637,7 +637,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} else {
$tempServiceName = $serviceName;
}
- if (str(data_get($service,'image'))->contains('glitchtip')) {
+ if (str(data_get($service, 'image'))->contains('glitchtip')) {
$tempServiceName = 'glitchtip';
}
$serviceDefinition = data_get($allServices, $tempServiceName);
@@ -1023,21 +1023,18 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$savedService->save();
}
// Caddy needs exact port in some cases.
- ray($predefinedPort, $key, $fqdn);
- if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) {
- if ($resource->server->proxyType() === 'CADDY') {
- $env = EnvironmentVariable::where([
- 'key' => $key,
- 'service_id' => $resource->id,
- ])->first();
- if ($env) {
- $env_url = Url::fromString($env->value);
- $env_port = $env_url->getPort();
- if ($env_port !== $predefinedPort) {
- $env_url = $env_url->withPort($predefinedPort);
- $savedService->fqdn = $env_url->__toString();
- $savedService->save();
- }
+ if ($predefinedPort && !$key->endsWith("_{$predefinedPort}") && $command?->value() === 'FQDN' && $resource->server->proxyType() === 'CADDY') {
+ $env = EnvironmentVariable::where([
+ 'key' => $key,
+ 'service_id' => $resource->id,
+ ])->first();
+ if ($env) {
+ $env_url = Url::fromString($env->value);
+ $env_port = $env_url->getPort();
+ if ($env_port !== $predefinedPort) {
+ $env_url = $env_url->withPort($predefinedPort);
+ $savedService->fqdn = $env_url->__toString();
+ $savedService->save();
}
}
}
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php
index 97bcd179f..d0a86a245 100644
--- a/resources/views/livewire/project/new/select.blade.php
+++ b/resources/views/livewire/project/new/select.blade.php
@@ -289,10 +289,10 @@
@endforelse
- @if ($isDatabase)
+ {{-- @if ($isDatabase)
Swarm clusters are excluded from this type of resource at the moment. It will
be activated soon. Stay tuned.
- @endif
+ @endif --}}
@endif
@if ($current_step === 'destinations')
diff --git a/templates/compose/metabase.yaml b/templates/compose/metabase.yaml
index a9df49dc7..1e3b5e20b 100644
--- a/templates/compose/metabase.yaml
+++ b/templates/compose/metabase.yaml
@@ -2,6 +2,7 @@
# slogan: Fast analytics with the friendly UX and integrated tooling to let your company explore data on their own.
# tags: analytics,bi,business,intelligence
# logo: svgs/metabase.svg
+# port: 3000
services:
metabase:
@@ -9,7 +10,7 @@ services:
volumes:
- /dev/urandom:/dev/random:ro
environment:
- - SERVICE_FQDN_METABASE
+ - SERVICE_FQDN_METABASE_3000
- MB_DB_TYPE=postgres
- MB_DB_HOST=postgresql
- MB_DB_PORT=5432
diff --git a/templates/compose/metube.yaml b/templates/compose/metube.yaml
index 9a39e4959..05eda044f 100644
--- a/templates/compose/metube.yaml
+++ b/templates/compose/metube.yaml
@@ -1,12 +1,13 @@
# documentation: https://github.com/alexta69/metube
# slogan: A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.
# tags: youtube, download, videos, playlist
+# port: 8081
services:
metube:
image: ghcr.io/alexta69/metube:latest
environment:
- - SERVICE_FQDN_METUBE
+ - SERVICE_FQDN_METUBE_8081
- UID=1000
- GID=1000
volumes:
diff --git a/templates/compose/moodle.yaml b/templates/compose/moodle.yaml
index ee3504518..fb7f5e4f8 100644
--- a/templates/compose/moodle.yaml
+++ b/templates/compose/moodle.yaml
@@ -2,6 +2,7 @@
# slogan: Moodle is the world’s most customisable and trusted eLearning solution that empowers educators to improve our world.
# tags: moodle, elearning, education, lms, cms, open, source, low, code
# logo: svgs/moodle.png
+# port: 8080
services:
mariadb:
@@ -20,7 +21,7 @@ services:
moodle:
image: docker.io/bitnami/moodle:4.3
environment:
- - SERVICE_FQDN_MOODLE
+ - SERVICE_FQDN_MOODLE_8080
- MOODLE_DATABASE_HOST=mariadb
- MOODLE_DATABASE_PORT_NUMBER=3306
- MOODLE_DATABASE_USER=$SERVICE_USER_MARIADB
diff --git a/templates/compose/n8n.yaml b/templates/compose/n8n.yaml
index ed31a641a..350ddb861 100644
--- a/templates/compose/n8n.yaml
+++ b/templates/compose/n8n.yaml
@@ -2,12 +2,13 @@
# slogan: n8n is an extendable workflow automation tool.
# tags: n8n,workflow,automation,open,source,low,code
# logo: svgs/n8n.png
+# port: 5678
services:
n8n:
image: docker.n8n.io/n8nio/n8n
environment:
- - SERVICE_FQDN_N8N
+ - SERVICE_FQDN_N8N_5678
- N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
- WEBHOOK_URL=${SERVICE_FQDN_N8N}
- N8N_HOST=${SERVICE_URL_N8N}
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 5d8a8c751..763602c4f 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -444,7 +444,7 @@
"metabase": {
"documentation": "https:\/\/www.metabase.com",
"slogan": "Fast analytics with the friendly UX and integrated tooling to let your company explore data on their own.",
- "compose": "c2VydmljZXM6CiAgbWV0YWJhc2U6CiAgICBpbWFnZTogJ21ldGFiYXNlL21ldGFiYXNlOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJy9kZXYvdXJhbmRvbTovZGV2L3JhbmRvbTpybycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRVRBQkFTRQogICAgICAtIE1CX0RCX1RZUEU9cG9zdGdyZXMKICAgICAgLSBNQl9EQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBNQl9EQl9QT1JUPTU0MzIKICAgICAgLSAnTUJfREJfREJOQU1FPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotbWV0YWJhc2V9JwogICAgICAtIE1CX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMCiAgICAgIC0gTUJfREJfUEFTUz0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogJ2N1cmwgLS1mYWlsIC1JIGh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hcGkvaGVhbHRoIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21ldGFiYXNlLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LW1ldGFiYXNlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
+ "compose": "c2VydmljZXM6CiAgbWV0YWJhc2U6CiAgICBpbWFnZTogJ21ldGFiYXNlL21ldGFiYXNlOmxhdGVzdCcKICAgIHZvbHVtZXM6CiAgICAgIC0gJy9kZXYvdXJhbmRvbTovZGV2L3JhbmRvbTpybycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NRVRBQkFTRV8zMDAwCiAgICAgIC0gTUJfREJfVFlQRT1wb3N0Z3JlcwogICAgICAtIE1CX0RCX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtIE1CX0RCX1BPUlQ9NTQzMgogICAgICAtICdNQl9EQl9EQk5BTUU9JHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1tZXRhYmFzZX0nCiAgICAgIC0gTUJfREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTUUwKICAgICAgLSBNQl9EQl9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUwKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnY3VybCAtLWZhaWwgLUkgaHR0cDovL2xvY2FsaG9zdDozMDAwL2FwaS9oZWFsdGggfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnbWV0YWJhc2UtcG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVNRTH0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNRTF9EQVRBQkFTRTotbWV0YWJhc2V9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
"tags": [
"analytics",
"bi",
@@ -452,12 +452,13 @@
"intelligence"
],
"logo": "svgs\/metabase.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"metube": {
"documentation": "https:\/\/github.com\/alexta69\/metube",
"slogan": "A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.",
- "compose": "c2VydmljZXM6CiAgbWV0dWJlOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FsZXh0YTY5L21ldHViZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTUVUVUJFCiAgICAgIC0gVUlEPTEwMDAKICAgICAgLSBHSUQ9MTAwMAogICAgdm9sdW1lczoKICAgICAgLSAnbWV0dWJlLWRvd25sb2FkczovZG93bmxvYWRzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwODEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "compose": "c2VydmljZXM6CiAgbWV0dWJlOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FsZXh0YTY5L21ldHViZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTUVUVUJFXzgwODEKICAgICAgLSBVSUQ9MTAwMAogICAgICAtIEdJRD0xMDAwCiAgICB2b2x1bWVzOgogICAgICAtICdtZXR1YmUtZG93bmxvYWRzOi9kb3dubG9hZHMnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
"tags": [
"youtube",
"download",
@@ -465,7 +466,8 @@
"playlist"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8081"
},
"minio": {
"documentation": "https:\/\/min.io\/docs\/minio\/container\/index.html",
@@ -484,7 +486,7 @@
"moodle": {
"documentation": "https:\/\/moodle.org",
"slogan": "Moodle is the world\u2019s most customisable and trusted eLearning solution that empowers educators to improve our world.",
- "compose": "c2VydmljZXM6CiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMS4xJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gQUxMT1dfRU1QVFlfUEFTU1dPUkQ9bm8KICAgICAgLSBNWVNRTF9ST09UX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgLSBNWVNRTF9EQVRBQkFTRT1iaXRuYW1pX21vb2RsZQogICAgICAtIE1ZU1FMX1VTRVI9JFNFUlZJQ0VfVVNFUl9NQVJJQURCCiAgICAgIC0gTVlTUUxfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQgogICAgICAtIE1BUklBREJfQ0hBUkFDVEVSX1NFVD11dGY4bWI0CiAgICAgIC0gTUFSSUFEQl9DT0xMQVRFPXV0ZjhtYjRfdW5pY29kZV9jaQogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogIG1vb2RsZToKICAgIGltYWdlOiAnZG9ja2VyLmlvL2JpdG5hbWkvbW9vZGxlOjQuMycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NT09ETEUKICAgICAgLSBNT09ETEVfREFUQUJBU0VfSE9TVD1tYXJpYWRiCiAgICAgIC0gTU9PRExFX0RBVEFCQVNFX1BPUlRfTlVNQkVSPTMzMDYKICAgICAgLSBNT09ETEVfREFUQUJBU0VfVVNFUj0kU0VSVklDRV9VU0VSX01BUklBREIKICAgICAgLSBNT09ETEVfREFUQUJBU0VfTkFNRT1iaXRuYW1pX21vb2RsZQogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NQVJJQURCCiAgICAgIC0gQUxMT1dfRU1QVFlfUEFTU1dPUkQ9bm8KICAgICAgLSAnTU9PRExFX1VTRVJOQU1FPSR7TU9PRExFX1VTRVJOQU1FOi11c2VyfScKICAgICAgLSBNT09ETEVfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTU9PRExFCiAgICAgIC0gTU9PRExFX0VNQUlMPXVzZXJAZXhhbXBsZS5jb20KICAgICAgLSAnTU9PRExFX1NJVEVfTkFNRT0ke01PT0RMRV9TSVRFX05BTUU6LU5ldyBTaXRlfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ21vb2RsZS1kYXRhOi9iaXRuYW1pL21vb2RsZScKICAgICAgLSAnbW9vZGxlZGF0YS1kYXRhOi9iaXRuYW1pL21vb2RsZWRhdGEnCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIK",
+ "compose": "c2VydmljZXM6CiAgbWFyaWFkYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMS4xJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gQUxMT1dfRU1QVFlfUEFTU1dPUkQ9bm8KICAgICAgLSBNWVNRTF9ST09UX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgLSBNWVNRTF9EQVRBQkFTRT1iaXRuYW1pX21vb2RsZQogICAgICAtIE1ZU1FMX1VTRVI9JFNFUlZJQ0VfVVNFUl9NQVJJQURCCiAgICAgIC0gTVlTUUxfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQgogICAgICAtIE1BUklBREJfQ0hBUkFDVEVSX1NFVD11dGY4bWI0CiAgICAgIC0gTUFSSUFEQl9DT0xMQVRFPXV0ZjhtYjRfdW5pY29kZV9jaQogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogIG1vb2RsZToKICAgIGltYWdlOiAnZG9ja2VyLmlvL2JpdG5hbWkvbW9vZGxlOjQuMycKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NT09ETEVfODA4MAogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9IT1NUPW1hcmlhZGIKICAgICAgLSBNT09ETEVfREFUQUJBU0VfUE9SVF9OVU1CRVI9MzMwNgogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9VU0VSPSRTRVJWSUNFX1VTRVJfTUFSSUFEQgogICAgICAtIE1PT0RMRV9EQVRBQkFTRV9OQU1FPWJpdG5hbWlfbW9vZGxlCiAgICAgIC0gTU9PRExFX0RBVEFCQVNFX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX01BUklBREIKICAgICAgLSBBTExPV19FTVBUWV9QQVNTV09SRD1ubwogICAgICAtICdNT09ETEVfVVNFUk5BTUU9JHtNT09ETEVfVVNFUk5BTUU6LXVzZXJ9JwogICAgICAtIE1PT0RMRV9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9NT09ETEUKICAgICAgLSBNT09ETEVfRU1BSUw9dXNlckBleGFtcGxlLmNvbQogICAgICAtICdNT09ETEVfU0lURV9OQU1FPSR7TU9PRExFX1NJVEVfTkFNRTotTmV3IFNpdGV9JwogICAgdm9sdW1lczoKICAgICAgLSAnbW9vZGxlLWRhdGE6L2JpdG5hbWkvbW9vZGxlJwogICAgICAtICdtb29kbGVkYXRhLWRhdGE6L2JpdG5hbWkvbW9vZGxlZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgo=",
"tags": [
"moodle",
"elearning",
@@ -497,7 +499,8 @@
"code"
],
"logo": "svgs\/moodle.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"n8n-with-postgresql": {
"documentation": "https:\/\/n8n.io",
@@ -518,7 +521,7 @@
"n8n": {
"documentation": "https:\/\/n8n.io",
"slogan": "n8n is an extendable workflow automation tool.",
- "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnV0VCSE9PS19VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX1VSTF9OOE59JwogICAgICAtICdHRU5FUklDX1RJTUVaT05FPSJFdXJvcGUvQmVybGluIicKICAgICAgLSAnVFo9IkV1cm9wZS9CZXJsaW4iJwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwo=",
+ "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOXzU2NzgKICAgICAgLSAnTjhOX0VESVRPUl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdXRUJIT09LX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdOOE5fSE9TVD0ke1NFUlZJQ0VfVVJMX044Tn0nCiAgICAgIC0gJ0dFTkVSSUNfVElNRVpPTkU9IkV1cm9wZS9CZXJsaW4iJwogICAgICAtICdUWj0iRXVyb3BlL0JlcmxpbiInCiAgICB2b2x1bWVzOgogICAgICAtICduOG4tZGF0YTovaG9tZS9ub2RlLy5uOG4nCg==",
"tags": [
"n8n",
"workflow",
@@ -529,7 +532,8 @@
"code"
],
"logo": "svgs\/n8n.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "5678"
},
"next-image-transformation": {
"documentation": "https:\/\/github.com\/coollabsio\/next-image-transformation",
From 73e9410264d8204a1e17e554fa7afc75cec4399c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 12 Mar 2024 20:03:11 +0100
Subject: [PATCH 56/97] Add port configuration for services
---
templates/compose/n8n-with-postgresql.yaml | 3 +-
.../compose/next-image-transformation.yaml | 3 +-
templates/compose/nocodb.yaml | 3 +-
templates/compose/openblocks.yaml | 3 +-
templates/compose/pairdrop.yaml | 3 +-
templates/compose/pocketbase.yaml | 3 +-
templates/compose/stirling-pdf.yaml | 3 +-
templates/service-templates.json | 35 +++++++++++--------
8 files changed, 35 insertions(+), 21 deletions(-)
diff --git a/templates/compose/n8n-with-postgresql.yaml b/templates/compose/n8n-with-postgresql.yaml
index b529cd093..3d4d00528 100644
--- a/templates/compose/n8n-with-postgresql.yaml
+++ b/templates/compose/n8n-with-postgresql.yaml
@@ -2,12 +2,13 @@
# slogan: n8n is an extendable workflow automation tool.
# tags: n8n,workflow,automation,open,source,low,code
# logo: svgs/n8n.png
+# port: 5678
services:
n8n:
image: docker.n8n.io/n8nio/n8n
environment:
- - SERVICE_FQDN_N8N
+ - SERVICE_FQDN_N8N_5678
- N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
- WEBHOOK_URL=${SERVICE_FQDN_N8N}
- N8N_HOST=${SERVICE_URL_N8N}
diff --git a/templates/compose/next-image-transformation.yaml b/templates/compose/next-image-transformation.yaml
index 5cefb7b45..4d438204d 100644
--- a/templates/compose/next-image-transformation.yaml
+++ b/templates/compose/next-image-transformation.yaml
@@ -1,12 +1,13 @@
# documentation: https://github.com/coollabsio/next-image-transformation
# slogan: Drop-in replacement for Vercel's Nextjs image optimization service.
# tags: nextjs,image,transformation,service
+# port: 3000
services:
next-image-transformation:
image: ghcr.io/coollabsio/next-image-transformation:latest
environment:
- - SERVICE_FQDN_TRANSFORMATION
+ - SERVICE_FQDN_TRANSFORMATION_3000
- NODE_ENV=production
- ALLOWED_REMOTE_DOMAINS=${ALLOWED_REMOTE_DOMAINS:-*}
- IMGPROXY_URL=${IMGPROXY_URL:-http://imgproxy:8080}
diff --git a/templates/compose/nocodb.yaml b/templates/compose/nocodb.yaml
index f3b33c731..bc6d98d91 100644
--- a/templates/compose/nocodb.yaml
+++ b/templates/compose/nocodb.yaml
@@ -2,12 +2,13 @@
# slogan: NocoDB is an open source Airtable alternative. Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.
# tags: nocodb,airtable,mysql,postgresql,sqlserver,sqlite,mariadb
# logo: svgs/nocodb.svg
+# port: 8080
services:
nocodb:
image: nocodb/nocodb
environment:
- - SERVICE_FQDN_NOCODB
+ - SERVICE_FQDN_NOCODB_8080
volumes:
- nocodb-data:/usr/app/data/
healthcheck:
diff --git a/templates/compose/openblocks.yaml b/templates/compose/openblocks.yaml
index 082d817cc..473b62883 100644
--- a/templates/compose/openblocks.yaml
+++ b/templates/compose/openblocks.yaml
@@ -2,12 +2,13 @@
# slogan: OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.
# tags: openblocks,low,code,platform,open,source,low,code
# logo: svgs/openblocks.svg
+# port: 3000
services:
openblocks:
image: openblocksdev/openblocks-ce
environment:
- - SERVICE_FQDN_OPENBLOCKS
+ - SERVICE_FQDN_OPENBLOCKS_3000
- ENABLE_USER_SIGN_UP=${ENABLE_USER_SIGN_UP:-true}
- ENCRYPTION_PASSWORD=$SERVICE_PASSWORD_ENCRYPTION
- ENCRYPTION_SALT=$SERVICE_PASSWORD_SALT
diff --git a/templates/compose/pairdrop.yaml b/templates/compose/pairdrop.yaml
index 57b4cc025..b00f7d5da 100644
--- a/templates/compose/pairdrop.yaml
+++ b/templates/compose/pairdrop.yaml
@@ -1,12 +1,13 @@
# documentation: https://pairdrop.net/
# 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
+# port: 3000
services:
pairdrop:
image: lscr.io/linuxserver/pairdrop:latest
environment:
- - SERVICE_FQDN_PAIRDROP
+ - SERVICE_FQDN_PAIRDROP_3000
- PUID=1000
- PGID=1000
- TZ=Europe/Madrid
diff --git a/templates/compose/pocketbase.yaml b/templates/compose/pocketbase.yaml
index 27272813c..29a2534ba 100644
--- a/templates/compose/pocketbase.yaml
+++ b/templates/compose/pocketbase.yaml
@@ -2,11 +2,12 @@
# slogan: Open Source backend for your next SaaS and Mobile app in 1 file
# tags: pocketbase,backend,saas,mobile,api
# logo: svgs/pocketbase.svg
+# port: 8080
services:
pocketbase:
image: ghcr.io/coollabsio/pocketbase:latest
environment:
- - SERVICE_FQDN_POCKETBASE
+ - SERVICE_FQDN_POCKETBASE_8080
volumes:
- pocketbase-data:/app/pb_data
diff --git a/templates/compose/stirling-pdf.yaml b/templates/compose/stirling-pdf.yaml
index bdc15ab66..ac2737adc 100644
--- a/templates/compose/stirling-pdf.yaml
+++ b/templates/compose/stirling-pdf.yaml
@@ -2,6 +2,7 @@
# slogan: Stirling is a powerful web based PDF manipulation tool
# tags: pdf, manipulation, web, tool
# logo: svgs/stirling.png
+# port: 8080
services:
stirling-pdf:
@@ -12,5 +13,5 @@ services:
- stirling-custom-files:/customFiles/
- stirling-logs:/logs/
environment:
- - SERVICE_FQDN_SPDF
+ - SERVICE_FQDN_SPDF_8080
- DOCKER_ENABLE_SECURITY=false
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 763602c4f..e23de7d16 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -505,7 +505,7 @@
"n8n-with-postgresql": {
"documentation": "https:\/\/n8n.io",
"slogan": "n8n is an extendable workflow automation tool.",
- "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnV0VCSE9PS19VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX1VSTF9OOE59JwogICAgICAtICdHRU5FUklDX1RJTUVaT05FPSJFdXJvcGUvQmVybGluIicKICAgICAgLSAnVFo9IkV1cm9wZS9CZXJsaW4iJwogICAgICAtIERCX1RZUEU9cG9zdGdyZXNkYgogICAgICAtICdEQl9QT1NUR1JFU0RCX0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LW44bn0nCiAgICAgIC0gREJfUE9TVEdSRVNEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BPUlQ9NTQzMgogICAgICAtIERCX1BPU1RHUkVTREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gREJfUE9TVEdSRVNEQl9TQ0hFTUE9cHVibGljCiAgICAgIC0gREJfUE9TVEdSRVNEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LW44bn0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
+ "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOXzU2NzgKICAgICAgLSAnTjhOX0VESVRPUl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdXRUJIT09LX1VSTD0ke1NFUlZJQ0VfRlFETl9OOE59JwogICAgICAtICdOOE5fSE9TVD0ke1NFUlZJQ0VfVVJMX044Tn0nCiAgICAgIC0gJ0dFTkVSSUNfVElNRVpPTkU9IkV1cm9wZS9CZXJsaW4iJwogICAgICAtICdUWj0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gREJfVFlQRT1wb3N0Z3Jlc2RiCiAgICAgIC0gJ0RCX1BPU1RHUkVTREJfREFUQUJBU0U9JHtQT1NUR1JFU19EQjotbjhufScKICAgICAgLSBEQl9QT1NUR1JFU0RCX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtIERCX1BPU1RHUkVTREJfUE9SVD01NDMyCiAgICAgIC0gREJfUE9TVEdSRVNEQl9VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBEQl9QT1NUR1JFU0RCX1NDSEVNQT1wdWJsaWMKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICB2b2x1bWVzOgogICAgICAtICduOG4tZGF0YTovaG9tZS9ub2RlLy5uOG4nCiAgICBkZXBlbmRzX29uOgogICAgICAtIHBvc3RncmVzcWwKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotbjhufScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
"tags": [
"n8n",
"workflow",
@@ -516,7 +516,8 @@
"code"
],
"logo": "svgs\/n8n.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "5678"
},
"n8n": {
"documentation": "https:\/\/n8n.io",
@@ -538,7 +539,7 @@
"next-image-transformation": {
"documentation": "https:\/\/github.com\/coollabsio\/next-image-transformation",
"slogan": "Drop-in replacement for Vercel's Nextjs image optimization service.",
- "compose": "c2VydmljZXM6CiAgbmV4dC1pbWFnZS10cmFuc2Zvcm1hdGlvbjoKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL25leHQtaW1hZ2UtdHJhbnNmb3JtYXRpb246bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1RSQU5TRk9STUFUSU9OCiAgICAgIC0gTk9ERV9FTlY9cHJvZHVjdGlvbgogICAgICAtICdBTExPV0VEX1JFTU9URV9ET01BSU5TPSR7QUxMT1dFRF9SRU1PVEVfRE9NQUlOUzotKn0nCiAgICAgIC0gJ0lNR1BST1hZX1VSTD0ke0lNR1BST1hZX1VSTDotaHR0cDovL2ltZ3Byb3h5OjgwODB9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC1xTy0gaHR0cDovL2xvY2FsaG9zdDozMDAwL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogIGltZ3Byb3h5OgogICAgaW1hZ2U6IGRhcnRoc2ltL2ltZ3Byb3h5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBJTUdQUk9YWV9FTkFCTEVfV0VCUF9ERVRFQ1RJT049dHJ1ZQogICAgICAtIElNR1BST1hZX0pQRUdfUFJPR1JFU1NJVkU9dHJ1ZQogICAgICAtIElNR1BST1hZX1VTRV9FVEFHPXRydWUKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBpbWdwcm94eQogICAgICAgIC0gaGVhbHRoCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQo=",
+ "compose": "c2VydmljZXM6CiAgbmV4dC1pbWFnZS10cmFuc2Zvcm1hdGlvbjoKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL25leHQtaW1hZ2UtdHJhbnNmb3JtYXRpb246bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1RSQU5TRk9STUFUSU9OXzMwMDAKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FMTE9XRURfUkVNT1RFX0RPTUFJTlM9JHtBTExPV0VEX1JFTU9URV9ET01BSU5TOi0qfScKICAgICAgLSAnSU1HUFJPWFlfVVJMPSR7SU1HUFJPWFlfVVJMOi1odHRwOi8vaW1ncHJveHk6ODA4MH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogJ3dnZXQgLXFPLSBodHRwOi8vbG9jYWxob3N0OjMwMDAvaGVhbHRoIHx8IGV4aXQgMScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1CiAgaW1ncHJveHk6CiAgICBpbWFnZTogZGFydGhzaW0vaW1ncHJveHkKICAgIGVudmlyb25tZW50OgogICAgICAtIElNR1BST1hZX0VOQUJMRV9XRUJQX0RFVEVDVElPTj10cnVlCiAgICAgIC0gSU1HUFJPWFlfSlBFR19QUk9HUkVTU0lWRT10cnVlCiAgICAgIC0gSU1HUFJPWFlfVVNFX0VUQUc9dHJ1ZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGltZ3Byb3h5CiAgICAgICAgLSBoZWFsdGgKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiA1Cg==",
"tags": [
"nextjs",
"image",
@@ -546,7 +547,8 @@
"service"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"nextcloud": {
"documentation": "https:\/\/docs.nextcloud.com",
@@ -565,7 +567,7 @@
"nocodb": {
"documentation": "https:\/\/nocodb.com\/",
"slogan": "NocoDB is an open source Airtable alternative. Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.",
- "compose": "c2VydmljZXM6CiAgbm9jb2RiOgogICAgaW1hZ2U6IG5vY29kYi9ub2NvZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9OT0NPREIKICAgIHZvbHVtZXM6CiAgICAgIC0gJ25vY29kYi1kYXRhOi91c3IvYXBwL2RhdGEvJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctcScKICAgICAgICAtICctLXNwaWRlcicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwODAnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
+ "compose": "c2VydmljZXM6CiAgbm9jb2RiOgogICAgaW1hZ2U6IG5vY29kYi9ub2NvZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9OT0NPREJfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAnbm9jb2RiLWRhdGE6L3Vzci9hcHAvZGF0YS8nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy1xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
"tags": [
"nocodb",
"airtable",
@@ -576,12 +578,13 @@
"mariadb"
],
"logo": "svgs\/nocodb.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"openblocks": {
"documentation": "https:\/\/openblocks.dev",
"slogan": "OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.",
- "compose": "c2VydmljZXM6CiAgb3BlbmJsb2NrczoKICAgIGltYWdlOiBvcGVuYmxvY2tzZGV2L29wZW5ibG9ja3MtY2UKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PUEVOQkxPQ0tTCiAgICAgIC0gJ0VOQUJMRV9VU0VSX1NJR05fVVA9JHtFTkFCTEVfVVNFUl9TSUdOX1VQOi10cnVlfScKICAgICAgLSBFTkNSWVBUSU9OX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX0VOQ1JZUFRJT04KICAgICAgLSBFTkNSWVBUSU9OX1NBTFQ9JFNFUlZJQ0VfUEFTU1dPUkRfU0FMVAogICAgdm9sdW1lczoKICAgICAgLSAnb3BlbmJsb2Nrcy1kYXRhOi9vcGVuYmxvY2tzLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDozMDAwL2hlYWx0aCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
+ "compose": "c2VydmljZXM6CiAgb3BlbmJsb2NrczoKICAgIGltYWdlOiBvcGVuYmxvY2tzZGV2L29wZW5ibG9ja3MtY2UKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9PUEVOQkxPQ0tTXzMwMDAKICAgICAgLSAnRU5BQkxFX1VTRVJfU0lHTl9VUD0ke0VOQUJMRV9VU0VSX1NJR05fVVA6LXRydWV9JwogICAgICAtIEVOQ1JZUFRJT05fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfRU5DUllQVElPTgogICAgICAtIEVOQ1JZUFRJT05fU0FMVD0kU0VSVklDRV9QQVNTV09SRF9TQUxUCiAgICB2b2x1bWVzOgogICAgICAtICdvcGVuYmxvY2tzLWRhdGE6L29wZW5ibG9ja3Mtc3RhY2tzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjMwMDAvaGVhbHRoJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
"tags": [
"openblocks",
"low",
@@ -593,12 +596,13 @@
"code"
],
"logo": "svgs\/openblocks.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"pairdrop": {
"documentation": "https:\/\/pairdrop.net\/",
"slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.",
- "compose": "c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIERFQlVHX01PREU9ZmFsc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QXzMwMDAKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gREVCVUdfTU9ERT1mYWxzZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjMwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"file",
"sharing",
@@ -606,7 +610,8 @@
"teamwork"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"phpmyadmin": {
"documentation": "https:\/\/phpmyadmin.net",
@@ -621,7 +626,7 @@
"pocketbase": {
"documentation": "https:\/\/pocketbase.io\/docs\/",
"slogan": "Open Source backend for your next SaaS and Mobile app in 1 file",
- "compose": "c2VydmljZXM6CiAgcG9ja2V0YmFzZToKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL3BvY2tldGJhc2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BPQ0tFVEJBU0UKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BvY2tldGJhc2UtZGF0YTovYXBwL3BiX2RhdGEnCg==",
+ "compose": "c2VydmljZXM6CiAgcG9ja2V0YmFzZToKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL3BvY2tldGJhc2U6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BPQ0tFVEJBU0VfODA4MAogICAgdm9sdW1lczoKICAgICAgLSAncG9ja2V0YmFzZS1kYXRhOi9hcHAvcGJfZGF0YScK",
"tags": [
"pocketbase",
"backend",
@@ -630,7 +635,8 @@
"api"
],
"logo": "svgs\/pocketbase.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"posthog": {
"documentation": "https:\/\/posthog.com",
@@ -665,7 +671,7 @@
"stirling-pdf": {
"documentation": "https:\/\/github.com\/Stirling-Tools\/Stirling-PDF",
"slogan": "Stirling is a powerful web based PDF manipulation tool",
- "compose": "c2VydmljZXM6CiAgc3RpcmxpbmctcGRmOgogICAgaW1hZ2U6ICdmcm9vb2RsZS9zLXBkZjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdzdGlybGluZy10cmFpbmluZy1kYXRhOi91c3Ivc2hhcmUvdGVzc2VyYWN0LW9jci81L3Rlc3NkYXRhJwogICAgICAtICdzdGlybGluZy1jb25maWdzOi9jb25maWdzJwogICAgICAtICdzdGlybGluZy1jdXN0b20tZmlsZXM6L2N1c3RvbUZpbGVzLycKICAgICAgLSAnc3RpcmxpbmctbG9nczovbG9ncy8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1BERgogICAgICAtIERPQ0tFUl9FTkFCTEVfU0VDVVJJVFk9ZmFsc2UK",
+ "compose": "c2VydmljZXM6CiAgc3RpcmxpbmctcGRmOgogICAgaW1hZ2U6ICdmcm9vb2RsZS9zLXBkZjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdzdGlybGluZy10cmFpbmluZy1kYXRhOi91c3Ivc2hhcmUvdGVzc2VyYWN0LW9jci81L3Rlc3NkYXRhJwogICAgICAtICdzdGlybGluZy1jb25maWdzOi9jb25maWdzJwogICAgICAtICdzdGlybGluZy1jdXN0b20tZmlsZXM6L2N1c3RvbUZpbGVzLycKICAgICAgLSAnc3RpcmxpbmctbG9nczovbG9ncy8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1BERl84MDgwCiAgICAgIC0gRE9DS0VSX0VOQUJMRV9TRUNVUklUWT1mYWxzZQo=",
"tags": [
"pdf",
"manipulation",
@@ -673,7 +679,8 @@
"tool"
],
"logo": "svgs\/stirling.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"supabase": {
"documentation": "https:\/\/supabase.io",
From 2f1a7f8f4052d9ee1192ef75223e9ff7b2843b05 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 13 Mar 2024 09:27:42 +0100
Subject: [PATCH 57/97] Update service environment variables
---
bootstrap/helpers/docker.php | 17 ++------
templates/compose/syncthing.yaml | 3 +-
.../trigger-with-external-database.yaml | 3 +-
templates/compose/trigger.yaml | 3 +-
templates/compose/umami.yaml | 3 +-
templates/compose/uptime-kuma.yaml | 3 +-
templates/compose/weblate.yaml | 3 +-
templates/compose/whoogle.yaml | 3 +-
templates/compose/wordpress-with-mariadb.yaml | 18 ++++----
templates/compose/wordpress-with-mysql.yaml | 18 ++++----
.../compose/wordpress-without-database.yaml | 6 +--
templates/service-templates.json | 41 +++++++++++--------
12 files changed, 61 insertions(+), 60 deletions(-)
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 638700050..754e65a97 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -240,21 +240,12 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$labels->push("caddy_{$loop}.header=-Server");
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
- if ($serviceLabels) {
- if ($port) {
- $labels->push("caddy_{$loop}.reverse_proxy={{upstreams $port}}");
- } else {
- $labels->push("caddy_{$loop}.reverse_proxy={{upstreams}}");
- }
+ if ($port) {
+ $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams $port}}");
} else {
- if ($port) {
- $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams $port}}");
- } else {
- $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams}}");
- }
- $labels->push("caddy_{$loop}.handle_path={$path}*");
+ $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams}}");
}
-
+ $labels->push("caddy_{$loop}.handle_path={$path}*");
if ($is_gzip_enabled) {
$labels->push("caddy_{$loop}.encode=zstd gzip");
}
diff --git a/templates/compose/syncthing.yaml b/templates/compose/syncthing.yaml
index af3e050fa..b6c896294 100644
--- a/templates/compose/syncthing.yaml
+++ b/templates/compose/syncthing.yaml
@@ -2,12 +2,13 @@
# slogan: Syncthing synchronizes files between two or more computers in real time.
# tags: filestorage, data, synchronization
# logo: svgs/syncthing.svg
+# port: 8384
services:
syncthing:
image: 'lscr.io/linuxserver/syncthing:latest'
environment:
- - SERVICE_FQDN_SYNCTHING
+ - SERVICE_FQDN_SYNCTHING_8384
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
diff --git a/templates/compose/trigger-with-external-database.yaml b/templates/compose/trigger-with-external-database.yaml
index ee76ec9c3..dcd3e2b97 100644
--- a/templates/compose/trigger-with-external-database.yaml
+++ b/templates/compose/trigger-with-external-database.yaml
@@ -2,12 +2,13 @@
# slogan: The open source Background Jobs framework for TypeScript
# tags: trigger.dev, background jobs, typescript, trigger, jobs, cron, scheduler
# logo: svgs/trigger.png
+# port: 3000
services:
trigger:
image: ghcr.io/triggerdotdev/trigger.dev:latest
environment:
- - SERVICE_FQDN_TRIGGER
+ - SERVICE_FQDN_TRIGGER_3000
- LOGIN_ORIGIN=$SERVICE_FQDN_TRIGGER
- APP_ORIGIN=$SERVICE_FQDN_TRIGGER
- MAGIC_LINK_SECRET=$SERVICE_PASSWORD_64_MAGIC
diff --git a/templates/compose/trigger.yaml b/templates/compose/trigger.yaml
index 6c15eed86..6181a6925 100644
--- a/templates/compose/trigger.yaml
+++ b/templates/compose/trigger.yaml
@@ -2,12 +2,13 @@
# slogan: The open source Background Jobs framework for TypeScript
# tags: trigger.dev, background jobs, typescript, trigger, jobs, cron, scheduler
# logo: svgs/trigger.png
+# port: 3000
services:
trigger:
image: ghcr.io/triggerdotdev/trigger.dev:latest
environment:
- - SERVICE_FQDN_TRIGGER
+ - SERVICE_FQDN_TRIGGER_3000
- LOGIN_ORIGIN=$SERVICE_FQDN_TRIGGER
- APP_ORIGIN=$SERVICE_FQDN_TRIGGER
- MAGIC_LINK_SECRET=$SERVICE_PASSWORD_64_MAGIC
diff --git a/templates/compose/umami.yaml b/templates/compose/umami.yaml
index 4bbf89b9e..1f70ad64c 100644
--- a/templates/compose/umami.yaml
+++ b/templates/compose/umami.yaml
@@ -2,12 +2,13 @@
# slogan: Umami is web analytics platform which provides insights into visitor behavior without compromising user privacy.
# tags: analytics, insights, privacy
# logo: svgs/umami.svg
+# port: 3000
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
environment:
- - SERVICE_FQDN_UMAMI
+ - SERVICE_FQDN_UMAMI_3000
- DATABASE_URL=postgres://$SERVICE_USER_POSTGRES:$SERVICE_PASSWORD_POSTGRES@postgresql:5432/$POSTGRES_DB
- DATABASE_TYPE=postgres
- APP_SECRET=$SERVICE_PASSWORD_64_UMAMI
diff --git a/templates/compose/uptime-kuma.yaml b/templates/compose/uptime-kuma.yaml
index 24dbad3ef..d5c07684d 100644
--- a/templates/compose/uptime-kuma.yaml
+++ b/templates/compose/uptime-kuma.yaml
@@ -2,12 +2,13 @@
# slogan: Uptime Kuma is a monitoring tool for tracking the status and performance of your applications in real-time.
# tags: monitoring, status, performance, web, services, applications, real-time
# logo: svgs/uptime-kuma.svg
+# port: 3001
services:
uptime-kuma:
image: louislam/uptime-kuma:1
environment:
- - SERVICE_FQDN
+ - SERVICE_FQDN_3001
volumes:
- uptime-kuma:/app/data
healthcheck:
diff --git a/templates/compose/weblate.yaml b/templates/compose/weblate.yaml
index 927033622..3090b7a91 100644
--- a/templates/compose/weblate.yaml
+++ b/templates/compose/weblate.yaml
@@ -2,12 +2,13 @@
# slogan: Weblate is a libre software web-based continuous localization system.
# tags: localization, translation, web, web-based, continuous, libre, software
# logo: svgs/weblate.webp
+# port: 8080
services:
weblate:
image: weblate/weblate:latest
environment:
- - SERVICE_FQDN_WEBLATE
+ - SERVICE_FQDN_WEBLATE_8080
- WEBLATE_SITE_DOMAIN=$SERVICE_URL_WEBLATE
- WEBLATE_ADMIN_NAME=${WEBLATE_ADMIN_NAME:-Admin}
- WEBLATE_ADMIN_EMAIL=${WEBLATE_ADMIN_EMAIL:-admin@example.com}
diff --git a/templates/compose/whoogle.yaml b/templates/compose/whoogle.yaml
index 3121dd0f9..245c09dd3 100644
--- a/templates/compose/whoogle.yaml
+++ b/templates/compose/whoogle.yaml
@@ -1,12 +1,13 @@
# documentation: https://github.com/benbusby/whoogle-search?tab=readme-ov-file
# slogan: Whoogle is a self-hosted, privacy-focused search engine front-end for accessing Google search results without tracking and data collection.
# tags: privacy, search engine
+# port: 5000
services:
whoogle:
image: benbusby/whoogle-search:latest
environment:
- - SERVICE_FQDN_WHOOGLE
+ - SERVICE_FQDN_WHOOGLE_5000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000"]
interval: 2s
diff --git a/templates/compose/wordpress-with-mariadb.yaml b/templates/compose/wordpress-with-mariadb.yaml
index a908fbfd4..8e1686d8f 100644
--- a/templates/compose/wordpress-with-mariadb.yaml
+++ b/templates/compose/wordpress-with-mariadb.yaml
@@ -9,11 +9,11 @@ services:
volumes:
- wordpress-files:/var/www/html
environment:
- SERVICE_FQDN:
- WORDPRESS_DB_HOST: mariadb
- WORDPRESS_DB_USER: $SERVICE_USER_WORDPRESS
- WORDPRESS_DB_PASSWORD: $SERVICE_PASSWORD_WORDPRESS
- WORDPRESS_DB_NAME: wordpress
+ - SERVICE_FQDN
+ - WORDPRESS_DB_HOST=mariadb
+ - WORDPRESS_DB_USER=$SERVICE_USER_WORDPRESS
+ - WORDPRESS_DB_PASSWORD=$SERVICE_PASSWORD_WORDPRESS
+ - WORDPRESS_DB_NAME=wordpress
depends_on:
- mariadb
@@ -22,7 +22,7 @@ services:
volumes:
- mariadb-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD: $SERVICE_PASSWORD_ROOT
- MYSQL_DATABASE: wordpress
- MYSQL_USER: $SERVICE_USER_WORDPRESS
- MYSQL_PASSWORD: $SERVICE_PASSWORD_WORDPRESS
+ - MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_ROOT
+ - MYSQL_DATABASE=wordpress
+ - MYSQL_USER=$SERVICE_USER_WORDPRESS
+ - MYSQL_PASSWORD=$SERVICE_PASSWORD_WORDPRESS
diff --git a/templates/compose/wordpress-with-mysql.yaml b/templates/compose/wordpress-with-mysql.yaml
index b0a6cdffc..0609f3bc6 100644
--- a/templates/compose/wordpress-with-mysql.yaml
+++ b/templates/compose/wordpress-with-mysql.yaml
@@ -9,11 +9,11 @@ services:
volumes:
- wordpress-files:/var/www/html
environment:
- SERVICE_FQDN:
- WORDPRESS_DB_HOST: mysql
- WORDPRESS_DB_USER: $SERVICE_USER_WORDPRESS
- WORDPRESS_DB_PASSWORD: $SERVICE_PASSWORD_WORDPRESS
- WORDPRESS_DB_NAME: wordpress
+ - SERVICE_FQDN
+ - WORDPRESS_DB_HOST=mysql
+ - WORDPRESS_DB_USER=$SERVICE_USER_WORDPRESS
+ - WORDPRESS_DB_PASSWORD=$SERVICE_PASSWORD_WORDPRESS
+ - WORDPRESS_DB_NAME=wordpress
depends_on:
- mysql
@@ -22,7 +22,7 @@ services:
volumes:
- mysql-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD: $SERVICE_PASSWORD_ROOT
- MYSQL_DATABASE: wordpress
- MYSQL_USER: $SERVICE_USER_WORDPRESS
- MYSQL_PASSWORD: $SERVICE_PASSWORD_WORDPRESS
+ - MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_ROOT
+ - MYSQL_DATABASE=wordpress
+ - MYSQL_USER=$SERVICE_USER_WORDPRESS
+ - MYSQL_PASSWORD=$SERVICE_PASSWORD_WORDPRESS
diff --git a/templates/compose/wordpress-without-database.yaml b/templates/compose/wordpress-without-database.yaml
index 0ce628168..2254dc444 100644
--- a/templates/compose/wordpress-without-database.yaml
+++ b/templates/compose/wordpress-without-database.yaml
@@ -9,8 +9,4 @@ services:
volumes:
- wordpress-files:/var/www/html
environment:
- SERVICE_FQDN:
- WORDPRESS_DB_HOST: $WORDPRESS_DB_HOST
- WORDPRESS_DB_USER: $WORDPRESS_DB_USER
- WORDPRESS_DB_PASSWORD: $WORDPRESS_DB_PASSWORD
- WORDPRESS_DB_NAME: $WORDPRESS_DB_NAME
+ - SERVICE_FQDN
diff --git a/templates/service-templates.json b/templates/service-templates.json
index e23de7d16..d098f1e4a 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -697,19 +697,20 @@
"syncthing": {
"documentation": "https:\/\/syncthing.net\/",
"slogan": "Syncthing synchronizes files between two or more computers in real time.",
- "compose": "c2VydmljZXM6CiAgc3luY3RoaW5nOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3N5bmN0aGluZzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1lOQ1RISU5HCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXRjL1VUQwogICAgdm9sdW1lczoKICAgICAgLSAnc3luY3RoaW5nLWNvbmZpZzovY29uZmlnJwogICAgICAtICdzeW5jdGhpbmctZGF0YTE6L2RhdGExJwogICAgICAtICdzeW5jdGhpbmctZGF0YTI6L2RhdGEyJwogICAgcG9ydHM6CiAgICAgIC0gJzIyMDAwOjIyMDAwL3RjcCcKICAgICAgLSAnMjIwMDA6MjIwMDAvdWRwJwogICAgICAtICcyMTAyNzoyMTAyNy91ZHAnCg==",
+ "compose": "c2VydmljZXM6CiAgc3luY3RoaW5nOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3N5bmN0aGluZzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1lOQ1RISU5HXzgzODQKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdGMvVVRDCiAgICB2b2x1bWVzOgogICAgICAtICdzeW5jdGhpbmctY29uZmlnOi9jb25maWcnCiAgICAgIC0gJ3N5bmN0aGluZy1kYXRhMTovZGF0YTEnCiAgICAgIC0gJ3N5bmN0aGluZy1kYXRhMjovZGF0YTInCiAgICBwb3J0czoKICAgICAgLSAnMjIwMDA6MjIwMDAvdGNwJwogICAgICAtICcyMjAwMDoyMjAwMC91ZHAnCiAgICAgIC0gJzIxMDI3OjIxMDI3L3VkcCcK",
"tags": [
"filestorage",
"data",
"synchronization"
],
"logo": "svgs\/syncthing.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8384"
},
"trigger-with-external-database": {
"documentation": "https:\/\/trigger.dev",
"slogan": "The open source Background Jobs framework for TypeScript",
- "compose": "c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gTE9HSU5fT1JJR0lOPSRTRVJWSUNFX0ZRRE5fVFJJR0dFUgogICAgICAtIEFQUF9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gTUFHSUNfTElOS19TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfTUFHSUMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9QQVNTV09SRF82NF9FTkNSWVBUSU9OCiAgICAgIC0gU0VTU0lPTl9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfU0VTU0lPTgogICAgICAtICdEQVRBQkFTRV9VUkw9JHtEQVRBQkFTRV9VUkx9JwogICAgICAtICdESVJFQ1RfVVJMPSR7REFUQUJBU0VfVVJMfScKICAgICAgLSBSVU5USU1FX1BMQVRGT1JNPWRvY2tlci1jb21wb3NlCiAgICAgIC0gTk9ERV9FTlY9cHJvZHVjdGlvbgogICAgICAtICdBVVRIX0dJVEhVQl9DTElFTlRfSUQ9JHtBVVRIX0dJVEhVQl9DTElFTlRfSUR9JwogICAgICAtICdBVVRIX0dJVEhVQl9DTElFTlRfU0VDUkVUPSR7QVVUSF9HSVRIVUJfQ0xJRU5UX1NFQ1JFVH0nCiAgICAgIC0gJ1JFU0VORF9BUElfS0VZPSR7UkVTRU5EX0FQSV9LRVl9JwogICAgICAtICdGUk9NX0VNQUlMPSR7RlJPTV9FTUFJTH0nCiAgICAgIC0gJ1JFUExZX1RPX0VNQUlMPSR7UkVQTFlfVE9fRU1BSUx9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBOT05FCg==",
+ "compose": "c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBMT0dJTl9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gQVBQX09SSUdJTj0kU0VSVklDRV9GUUROX1RSSUdHRVIKICAgICAgLSBNQUdJQ19MSU5LX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9NQUdJQwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT04KICAgICAgLSBTRVNTSU9OX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9TRVNTSU9OCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD0ke0RBVEFCQVNFX1VSTH0nCiAgICAgIC0gJ0RJUkVDVF9VUkw9JHtEQVRBQkFTRV9VUkx9JwogICAgICAtIFJVTlRJTUVfUExBVEZPUk09ZG9ja2VyLWNvbXBvc2UKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9JRD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVQ9JHtBVVRIX0dJVEhVQl9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnUkVTRU5EX0FQSV9LRVk9JHtSRVNFTkRfQVBJX0tFWX0nCiAgICAgIC0gJ0ZST01fRU1BSUw9JHtGUk9NX0VNQUlMfScKICAgICAgLSAnUkVQTFlfVE9fRU1BSUw9JHtSRVBMWV9UT19FTUFJTH0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIE5PTkUK",
"tags": [
"trigger.dev",
"background jobs",
@@ -720,12 +721,13 @@
"scheduler"
],
"logo": "svgs\/trigger.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"trigger": {
"documentation": "https:\/\/trigger.dev",
"slogan": "The open source Background Jobs framework for TypeScript",
- "compose": "c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gTE9HSU5fT1JJR0lOPSRTRVJWSUNFX0ZRRE5fVFJJR0dFUgogICAgICAtIEFQUF9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gTUFHSUNfTElOS19TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfTUFHSUMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0kU0VSVklDRV9QQVNTV09SRF82NF9FTkNSWVBUSU9OCiAgICAgIC0gU0VTU0lPTl9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfU0VTU0lPTgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXRyaWdnZXJ9JwogICAgICAtIFBPU1RHUkVTX0hPU1Q9cG9zdGdyZXMKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlc3FsOjU0MzIvJFBPU1RHUkVTX0RCJwogICAgICAtICdESVJFQ1RfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlc3FsOjU0MzIvJFBPU1RHUkVTX0RCJwogICAgICAtIFJVTlRJTUVfUExBVEZPUk09ZG9ja2VyLWNvbXBvc2UKICAgICAgLSBOT0RFX0VOVj1wcm9kdWN0aW9uCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9JRD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9JRH0nCiAgICAgIC0gJ0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVQ9JHtBVVRIX0dJVEhVQl9DTElFTlRfU0VDUkVUfScKICAgICAgLSAnUkVTRU5EX0FQSV9LRVk9JHtSRVNFTkRfQVBJX0tFWX0nCiAgICAgIC0gJ0ZST01fRU1BSUw9JHtGUk9NX0VNQUlMfScKICAgICAgLSAnUkVQTFlfVE9fRU1BSUw9JHtSRVBMWV9UT19FTUFJTH0nCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIE5PTkUKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdHJpZ2dlcn0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
+ "compose": "c2VydmljZXM6CiAgdHJpZ2dlcjoKICAgIGltYWdlOiAnZ2hjci5pby90cmlnZ2VyZG90ZGV2L3RyaWdnZXIuZGV2OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9UUklHR0VSXzMwMDAKICAgICAgLSBMT0dJTl9PUklHSU49JFNFUlZJQ0VfRlFETl9UUklHR0VSCiAgICAgIC0gQVBQX09SSUdJTj0kU0VSVklDRV9GUUROX1RSSUdHRVIKICAgICAgLSBNQUdJQ19MSU5LX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9NQUdJQwogICAgICAtIEVOQ1JZUFRJT05fS0VZPSRTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT04KICAgICAgLSBTRVNTSU9OX1NFQ1JFVD0kU0VSVklDRV9QQVNTV09SRF82NF9TRVNTSU9OCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdHJpZ2dlcn0nCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gJ0RJUkVDVF9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gUlVOVElNRV9QTEFURk9STT1kb2NrZXItY29tcG9zZQogICAgICAtIE5PREVfRU5WPXByb2R1Y3Rpb24KICAgICAgLSAnQVVUSF9HSVRIVUJfQ0xJRU5UX0lEPSR7QVVUSF9HSVRIVUJfQ0xJRU5UX0lEfScKICAgICAgLSAnQVVUSF9HSVRIVUJfQ0xJRU5UX1NFQ1JFVD0ke0FVVEhfR0lUSFVCX0NMSUVOVF9TRUNSRVR9JwogICAgICAtICdSRVNFTkRfQVBJX0tFWT0ke1JFU0VORF9BUElfS0VZfScKICAgICAgLSAnRlJPTV9FTUFJTD0ke0ZST01fRU1BSUx9JwogICAgICAtICdSRVBMWV9UT19FTUFJTD0ke1JFUExZX1RPX0VNQUlMfScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gTk9ORQogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi10cmlnZ2VyfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
"tags": [
"trigger.dev",
"background jobs",
@@ -736,24 +738,26 @@
"scheduler"
],
"logo": "svgs\/trigger.png",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"umami": {
"documentation": "https:\/\/umami.is",
"slogan": "Umami is web analytics platform which provides insights into visitor behavior without compromising user privacy.",
- "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6cG9zdGdyZXNxbC1sYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUkKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlc3FsOjU0MzIvJFBPU1RHUkVTX0RCJwogICAgICAtIERBVEFCQVNFX1RZUEU9cG9zdGdyZXMKICAgICAgLSBBUFBfU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEXzY0X1VNQU1JCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXVtYW1pfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
+ "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6cG9zdGdyZXNxbC1sYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUlfMzAwMAogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly8kU0VSVklDRV9VU0VSX1BPU1RHUkVTOiRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTQHBvc3RncmVzcWw6NTQzMi8kUE9TVEdSRVNfREInCiAgICAgIC0gREFUQUJBU0VfVFlQRT1wb3N0Z3JlcwogICAgICAtIEFQUF9TRUNSRVQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfVU1BTUkKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotdW1hbWl9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICQke1BPU1RHUkVTX1VTRVJ9IC1kICQke1BPU1RHUkVTX0RCfScKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAo=",
"tags": [
"analytics",
"insights",
"privacy"
],
"logo": "svgs\/umami.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3000"
},
"uptime-kuma": {
"documentation": "https:\/\/github.com\/louislam\/uptime-kuma?tab=readme-ov-file",
"slogan": "Uptime Kuma is a monitoring tool for tracking the status and performance of your applications in real-time.",
- "compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwdGltZS1rdW1hOi9hcHAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSBleHRyYS9oZWFsdGhjaGVjawogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fMzAwMQogICAgdm9sdW1lczoKICAgICAgLSAndXB0aW1lLWt1bWE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtIGV4dHJhL2hlYWx0aGNoZWNrCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"monitoring",
"status",
@@ -764,7 +768,8 @@
"real-time"
],
"logo": "svgs\/uptime-kuma.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "3001"
},
"vaultwarden": {
"documentation": "https:\/\/github.com\/dani-garcia\/vaultwarden",
@@ -780,7 +785,7 @@
"weblate": {
"documentation": "https:\/\/weblate.org",
"slogan": "Weblate is a libre software web-based continuous localization system.",
- "compose": "c2VydmljZXM6CiAgd2VibGF0ZToKICAgIGltYWdlOiAnd2VibGF0ZS93ZWJsYXRlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9XRUJMQVRFCiAgICAgIC0gV0VCTEFURV9TSVRFX0RPTUFJTj0kU0VSVklDRV9VUkxfV0VCTEFURQogICAgICAtICdXRUJMQVRFX0FETUlOX05BTUU9JHtXRUJMQVRFX0FETUlOX05BTUU6LUFkbWlufScKICAgICAgLSAnV0VCTEFURV9BRE1JTl9FTUFJTD0ke1dFQkxBVEVfQURNSU5fRU1BSUw6LWFkbWluQGV4YW1wbGUuY29tfScKICAgICAgLSBXRUJMQVRFX0FETUlOX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dFQkxBVEUKICAgICAgLSAnREVGQVVMVF9GUk9NX0VNQUlMPSR7V0VCTEFURV9BRE1JTl9FTUFJTDotYWRtaW5AZXhhbXBsZS5jb219JwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LXdlYmxhdGV9JwogICAgICAtIFBPU1RHUkVTX0hPU1Q9cG9zdGdyZXNxbAogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFJFRElTX0hPU1Q9cmVkaXMKICAgICAgLSBSRURJU19QT1JUPTYzNzkKICAgICAgLSBSRURJU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgdm9sdW1lczoKICAgICAgLSAnd2VibGF0ZS1kYXRhOi9hcHAvZGF0YScKICAgICAgLSAnd2VibGF0ZS1jYWNoZTovYXBwL2NhY2hlJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMzAKICBwb3N0Z3Jlc3FsOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNi1hbHBpbmUnCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlc3FsLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotd2VibGF0ZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgcmVkaXM6CiAgICBpbWFnZTogJ3JlZGlzOjctYWxwaW5lJwogICAgY29tbWFuZDogIi0tYXBwZW5kb25seSB5ZXMgLS1yZXF1aXJlcGFzcyAke1NFUlZJQ0VfUEFTU1dPUkRfUkVESVN9XG4iCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBSRURJU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9SRURJUwogICAgdm9sdW1lczoKICAgICAgLSAnd2VibGF0ZS1yZWRpcy1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHJlZGlzLWNsaQogICAgICAgIC0gcGluZwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
+ "compose": "c2VydmljZXM6CiAgd2VibGF0ZToKICAgIGltYWdlOiAnd2VibGF0ZS93ZWJsYXRlOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9XRUJMQVRFXzgwODAKICAgICAgLSBXRUJMQVRFX1NJVEVfRE9NQUlOPSRTRVJWSUNFX1VSTF9XRUJMQVRFCiAgICAgIC0gJ1dFQkxBVEVfQURNSU5fTkFNRT0ke1dFQkxBVEVfQURNSU5fTkFNRTotQWRtaW59JwogICAgICAtICdXRUJMQVRFX0FETUlOX0VNQUlMPSR7V0VCTEFURV9BRE1JTl9FTUFJTDotYWRtaW5AZXhhbXBsZS5jb219JwogICAgICAtIFdFQkxBVEVfQURNSU5fUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfV0VCTEFURQogICAgICAtICdERUZBVUxUX0ZST01fRU1BSUw9JHtXRUJMQVRFX0FETUlOX0VNQUlMOi1hZG1pbkBleGFtcGxlLmNvbX0nCiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSAnUE9TVEdSRVNfREFUQUJBU0U9JHtQT1NUR1JFU19EQjotd2VibGF0ZX0nCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3Jlc3FsCiAgICAgIC0gUE9TVEdSRVNfUE9SVD01NDMyCiAgICAgIC0gUkVESVNfSE9TVD1yZWRpcwogICAgICAtIFJFRElTX1BPUlQ9NjM3OQogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICB2b2x1bWVzOgogICAgICAtICd3ZWJsYXRlLWRhdGE6L2FwcC9kYXRhJwogICAgICAtICd3ZWJsYXRlLWNhY2hlOi9hcHAvY2FjaGUnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzMAogIHBvc3RncmVzcWw6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE2LWFscGluZScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19VU0VSPSRTRVJWSUNFX1VTRVJfUE9TVEdSRVMKICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi13ZWJsYXRlfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny1hbHBpbmUnCiAgICBjb21tYW5kOiAiLS1hcHBlbmRvbmx5IHllcyAtLXJlcXVpcmVwYXNzICR7U0VSVklDRV9QQVNTV09SRF9SRURJU31cbiIKICAgIGVudmlyb25tZW50OgogICAgICAtIFJFRElTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1JFRElTCiAgICB2b2x1bWVzOgogICAgICAtICd3ZWJsYXRlLXJlZGlzLWRhdGE6L2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gcmVkaXMtY2xpCiAgICAgICAgLSBwaW5nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
"tags": [
"localization",
"translation",
@@ -791,23 +796,25 @@
"software"
],
"logo": "svgs\/weblate.webp",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "8080"
},
"whoogle": {
"documentation": "https:\/\/github.com\/benbusby\/whoogle-search?tab=readme-ov-file",
"slogan": "Whoogle is a self-hosted, privacy-focused search engine front-end for accessing Google search results without tracking and data collection.",
- "compose": "c2VydmljZXM6CiAgd2hvb2dsZToKICAgIGltYWdlOiAnYmVuYnVzYnkvd2hvb2dsZS1zZWFyY2g6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1dIT09HTEUKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo1MDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "compose": "c2VydmljZXM6CiAgd2hvb2dsZToKICAgIGltYWdlOiAnYmVuYnVzYnkvd2hvb2dsZS1zZWFyY2g6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1dIT09HTEVfNTAwMAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjUwMDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"privacy",
"search engine"
],
"logo": "svgs\/unknown.svg",
- "minversion": "0.0.0"
+ "minversion": "0.0.0",
+ "port": "5000"
},
"wordpress-with-mariadb": {
"documentation": "https:\/\/wordpress.org",
"slogan": "WordPress with MariaDB. Wordpress is open source software you can use to create a beautiful website, blog, or app.",
- "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBtYXJpYWRiCiAgICAgIFdPUkRQUkVTU19EQl9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfTkFNRTogd29yZHByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==",
+ "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBXT1JEUFJFU1NfREJfSE9TVD1tYXJpYWRiCiAgICAgIC0gV09SRFBSRVNTX0RCX1VTRVI9JFNFUlZJQ0VfVVNFUl9XT1JEUFJFU1MKICAgICAgLSBXT1JEUFJFU1NfREJfUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX05BTUU9d29yZHByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9d29yZHByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwo=",
"tags": [
"cms",
"blog",
@@ -821,7 +828,7 @@
"wordpress-with-mysql": {
"documentation": "https:\/\/wordpress.org",
"slogan": "WordPress with MySQL. Wordpress is open source software you can use to create a beautiful website, blog, or app.",
- "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBteXNxbAogICAgICBXT1JEUFJFU1NfREJfVVNFUjogJFNFUlZJQ0VfVVNFUl9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX1BBU1NXT1JEOiAkU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX05BTUU6IHdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBteXNxbAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==",
+ "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBXT1JEUFJFU1NfREJfSE9TVD1teXNxbAogICAgICAtIFdPUkRQUkVTU19EQl9VU0VSPSRTRVJWSUNFX1VTRVJfV09SRFBSRVNTCiAgICAgIC0gV09SRFBSRVNTX0RCX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICAtIFdPUkRQUkVTU19EQl9OQU1FPXdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBteXNxbAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfUk9PVF9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9ST09UCiAgICAgIC0gTVlTUUxfREFUQUJBU0U9d29yZHByZXNzCiAgICAgIC0gTVlTUUxfVVNFUj0kU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICAtIE1ZU1FMX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwo=",
"tags": [
"cms",
"blog",
@@ -835,7 +842,7 @@
"wordpress-without-database": {
"documentation": "https:\/\/wordpress.org",
"slogan": "WordPress with external database. Wordpress is open source software you can use to create a beautiful website, blog, or app.",
- "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiAkV09SRFBSRVNTX0RCX0hPU1QKICAgICAgV09SRFBSRVNTX0RCX1VTRVI6ICRXT1JEUFJFU1NfREJfVVNFUgogICAgICBXT1JEUFJFU1NfREJfUEFTU1dPUkQ6ICRXT1JEUFJFU1NfREJfUEFTU1dPUkQKICAgICAgV09SRFBSRVNTX0RCX05BTUU6ICRXT1JEUFJFU1NfREJfTkFNRQo=",
+ "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4K",
"tags": [
"cms",
"blog",
From 65e0eb520534e8d1f9d02bf684de0d169148ef67 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 13 Mar 2024 09:32:42 +0100
Subject: [PATCH 58/97] Refactor dynamic configurations and update textarea
rows
---
app/Livewire/Server/Proxy/DynamicConfigurations.php | 4 ----
.../livewire/server/proxy/dynamic-configurations.blade.php | 4 ++--
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/app/Livewire/Server/Proxy/DynamicConfigurations.php b/app/Livewire/Server/Proxy/DynamicConfigurations.php
index 36219dc7e..ae84ce949 100644
--- a/app/Livewire/Server/Proxy/DynamicConfigurations.php
+++ b/app/Livewire/Server/Proxy/DynamicConfigurations.php
@@ -30,10 +30,6 @@ class DynamicConfigurations extends Component
$files = collect(explode("\n", $files))->filter(fn ($file) => !empty($file));
$files = $files->map(fn ($file) => trim($file));
$files = $files->sort();
- if ($files->contains('coolify.yaml')) {
- $files = $files->filter(fn ($file) => $file !== 'coolify.yaml')->prepend('coolify.yaml');
- $files = $files->filter(fn ($file) => $file !== 'Caddyfile')->prepend('Caddyfile');
- }
$contents = collect([]);
foreach ($files as $file) {
$without_extension = str_replace('.', '|', $file);
diff --git a/resources/views/livewire/server/proxy/dynamic-configurations.blade.php b/resources/views/livewire/server/proxy/dynamic-configurations.blade.php
index 0fd92f41e..bc768f392 100644
--- a/resources/views/livewire/server/proxy/dynamic-configurations.blade.php
+++ b/resources/views/livewire/server/proxy/dynamic-configurations.blade.php
@@ -29,12 +29,12 @@
@if ($contents?->isNotEmpty())
@foreach ($contents as $fileName => $value)
- @if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||str_replace('|', '.', $fileName) === 'Caddyfile' )
+ @if (str_replace('|', '.', $fileName) === 'coolify.yaml' || str_replace('|', '.', $fileName) === 'Caddyfile' || str_replace('|', '.', $fileName) === 'coolify.caddy' || str_replace('|', '.', $fileName) === 'default_redirect_404.caddy')
File: {{ str_replace('|', '.', $fileName) }}
+ wire:model="contents.{{ $fileName }}" rows="5" />
@else
Date: Wed, 13 Mar 2024 10:10:53 +0100
Subject: [PATCH 59/97] feat: reset password
---
app/Livewire/Profile/Index.php | 34 +++++++++++++++++--
.../livewire/force-password-reset.blade.php | 4 +--
.../views/livewire/profile/index.blade.php | 15 ++++++--
3 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/app/Livewire/Profile/Index.php b/app/Livewire/Profile/Index.php
index 3499d4ff9..abfc0b972 100644
--- a/app/Livewire/Profile/Index.php
+++ b/app/Livewire/Profile/Index.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Profile;
+use Illuminate\Support\Facades\Hash;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -10,6 +11,13 @@ class Index extends Component
public int $userId;
public string $email;
+ #[Validate('required')]
+ public string $current_password;
+ #[Validate('required|min:8')]
+ public string $new_password;
+ #[Validate('required|min:8|same:new_password')]
+ public string $new_password_confirmation;
+
#[Validate('required')]
public string $name;
public function mount()
@@ -19,7 +27,6 @@ class Index extends Component
$this->email = auth()->user()->email;
}
public function submit()
-
{
try {
$this->validate();
@@ -27,7 +34,30 @@ class Index extends Component
'name' => $this->name,
]);
- $this->dispatch('success', 'Profile updated');
+ $this->dispatch('success', 'Profile updated.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+ public function resetPassword()
+ {
+ try {
+ $this->validate();
+ if (!Hash::check($this->current_password, auth()->user()->password)) {
+ $this->dispatch('error', 'Current password is incorrect.');
+ return;
+ }
+ if ($this->new_password !== $this->new_password_confirmation) {
+ $this->dispatch('error', 'The two new passwords does not match.');
+ return;
+ }
+ auth()->user()->update([
+ 'password' => Hash::make($this->new_password),
+ ]);
+ $this->dispatch('success', 'Password updated.');
+ $this->current_password = '';
+ $this->new_password = '';
+ $this->new_password_confirmation = '';
} catch (\Throwable $e) {
return handleError($e, $this);
}
diff --git a/resources/views/livewire/force-password-reset.blade.php b/resources/views/livewire/force-password-reset.blade.php
index d6951d0ad..d77651675 100644
--- a/resources/views/livewire/force-password-reset.blade.php
+++ b/resources/views/livewire/force-password-reset.blade.php
@@ -5,9 +5,7 @@
Coolify
-
- Set your initial password
-
+
From 1f03499fc52d02353873ae14673573a4241e4e46 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 13 Mar 2024 14:55:44 +0100
Subject: [PATCH 67/97] feat: show resources on source page
---
app/Livewire/Source/Github/Change.php | 8 +++
.../views/livewire/server/resources.blade.php | 5 +-
.../livewire/source/github/change.blade.php | 53 ++++++++++++++++++-
3 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php
index 61c4ffcda..b7acb30a7 100644
--- a/app/Livewire/Source/Github/Change.php
+++ b/app/Livewire/Source/Github/Change.php
@@ -24,6 +24,8 @@ class Change extends Component
public string $name;
public bool $is_system_wide;
+ public $applications;
+
protected $rules = [
'github_app.name' => 'required|string',
'github_app.organization' => 'nullable|string',
@@ -90,6 +92,7 @@ class Change extends Component
if (!$this->github_app) {
return redirect()->route('source.all');
}
+ $this->applications = $this->github_app->applications;
$settings = InstanceSettings::get();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
@@ -170,6 +173,11 @@ class Change extends Component
public function delete()
{
try {
+ if ($this->github_app->applications->isNotEmpty()) {
+ $this->dispatch('error', 'This source is being used by an application. Please delete all applications first.');
+ $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
+ return;
+ }
$this->github_app->delete();
return redirect()->route('source.all');
} catch (\Throwable $e) {
diff --git a/resources/views/livewire/server/resources.blade.php b/resources/views/livewire/server/resources.blade.php
index 807b3172b..0b9f9d854 100644
--- a/resources/views/livewire/server/resources.blade.php
+++ b/resources/views/livewire/server/resources.blade.php
@@ -45,7 +45,8 @@
{{ data_get($resource, 'environment.name') }}
{{ $resource->name }}
+ href="{{ $resource->link() }}">{{ $resource->name }}
+
{{ str($resource->type())->headline() }}
@@ -138,6 +139,4 @@
-
-
diff --git a/resources/views/livewire/source/github/change.blade.php b/resources/views/livewire/source/github/change.blade.php
index 2eae0a8bd..c369d2f42 100644
--- a/resources/views/livewire/source/github/change.blade.php
+++ b/resources/views/livewire/source/github/change.blade.php
@@ -69,7 +69,7 @@
@endif
+
+
+
+
+
Resources
+
+
Here you can find all resources that are used by this source.
+
+
+
+
+
+
+
+
+
+ Project
+
+
+ Environment
+ Name
+ Type
+
+
+
+ @forelse ($applications->sortBy('name',SORT_NATURAL) as $resource)
+
+
+ {{ data_get($resource->project(), 'name') }}
+
+
+ {{ data_get($resource, 'environment.name') }}
+
+ {{ $resource->name }}
+
+
+
+ {{ str($resource->type())->headline() }}
+
+ @empty
+ @endforelse
+
+
+
+
+
+
+
+
+
@else
GitHub App
From 31989997463c6a54efcef5ab9d543c17f5183a25 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 13 Mar 2024 15:00:29 +0100
Subject: [PATCH 68/97] Fix null check for memory swappiness and CPU shares
---
app/Livewire/Project/Shared/ResourceLimits.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/Livewire/Project/Shared/ResourceLimits.php b/app/Livewire/Project/Shared/ResourceLimits.php
index a326a9a0b..767175313 100644
--- a/app/Livewire/Project/Shared/ResourceLimits.php
+++ b/app/Livewire/Project/Shared/ResourceLimits.php
@@ -35,7 +35,7 @@ class ResourceLimits extends Component
if (!$this->resource->limits_memory_swap) {
$this->resource->limits_memory_swap = "0";
}
- if (!$this->resource->limits_memory_swappiness) {
+ if (is_null($this->resource->limits_memory_swappiness)) {
$this->resource->limits_memory_swappiness = "60";
}
if (!$this->resource->limits_memory_reservation) {
@@ -47,7 +47,7 @@ class ResourceLimits extends Component
if ($this->resource->limits_cpuset === "") {
$this->resource->limits_cpuset = null;
}
- if (!$this->resource->limits_cpu_shares) {
+ if (is_null($this->resource->limits_cpu_shares)) {
$this->resource->limits_cpu_shares = 1024;
}
$this->validate();
From 379733938cc9951668949fa79cc693e9eccba1bd Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 13 Mar 2024 15:07:43 +0100
Subject: [PATCH 69/97] Update environment variable description
---
.../livewire/project/shared/environment-variable/all.blade.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 1b3483254..74e8f792d 100644
--- a/resources/views/livewire/project/shared/environment-variable/all.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php
@@ -17,7 +17,7 @@
Environment variables (secrets) for this resource.
@if ($resource->type() === 'service')
- If you cannot find a variable here, or need a new one, define it in the Docker Compose file.
+ Hardcoded variables are not shown here.
@endif
@if ($view === 'normal')
From 965625ad0195e881e0453dddce6175cfc42ea5ab Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 13 Mar 2024 18:26:30 +0100
Subject: [PATCH 70/97] fix: create initial files async
---
app/Jobs/ServerFilesFromServerJob.php | 26 ++++++++++++++++++++++++++
app/Jobs/ServerStorageSaveJob.php | 26 ++++++++++++++++++++++++++
app/Models/LocalFileVolume.php | 2 +-
bootstrap/helpers/shared.php | 3 ++-
4 files changed, 55 insertions(+), 2 deletions(-)
create mode 100644 app/Jobs/ServerFilesFromServerJob.php
create mode 100644 app/Jobs/ServerStorageSaveJob.php
diff --git a/app/Jobs/ServerFilesFromServerJob.php b/app/Jobs/ServerFilesFromServerJob.php
new file mode 100644
index 000000000..b195e2b6d
--- /dev/null
+++ b/app/Jobs/ServerFilesFromServerJob.php
@@ -0,0 +1,26 @@
+service->getFilesFromServer(isInit: true);
+ }
+}
diff --git a/app/Jobs/ServerStorageSaveJob.php b/app/Jobs/ServerStorageSaveJob.php
new file mode 100644
index 000000000..7ed55cf5a
--- /dev/null
+++ b/app/Jobs/ServerStorageSaveJob.php
@@ -0,0 +1,26 @@
+localFileVolume->saveStorageOnServer();
+ }
+
+}
diff --git a/app/Models/LocalFileVolume.php b/app/Models/LocalFileVolume.php
index a0447b581..b097aa300 100644
--- a/app/Models/LocalFileVolume.php
+++ b/app/Models/LocalFileVolume.php
@@ -13,7 +13,7 @@ class LocalFileVolume extends BaseModel
{
static::created(function (LocalFileVolume $fileVolume) {
$fileVolume->load(['service']);
- $fileVolume->saveStorageOnServer();
+ dispatch(new \App\Jobs\ServerStorageSaveJob($fileVolume));
});
}
public function service()
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 1a99ff414..88bbb5cd5 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -1,5 +1,6 @@
getFilesFromServer(isInit: true);
+ dispatch(new ServerFilesFromServerJob($savedService));
return $volume;
});
data_set($service, 'volumes', $serviceVolumes->toArray());
From 695d3b82b53d1e45a2bdcbadaec570098849695d Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 14 Mar 2024 09:10:32 +0100
Subject: [PATCH 71/97] Add toast timeout functionality
---
resources/views/components/toast.blade.php | 32 +++++++++++++++++-----
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/resources/views/components/toast.blade.php b/resources/views/components/toast.blade.php
index 64f07b8b4..7f07929b7 100644
--- a/resources/views/components/toast.blade.php
+++ b/resources/views/components/toast.blade.php
@@ -27,6 +27,7 @@
-
+
+ class="mt-1.5 text-xs leading-2 opacity-90 whitespace-pre-wrap"
+ x-html="toast.description">
From c8d48ccbff3a5abeb3dffc2fa65116c67152e63b Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 14 Mar 2024 09:21:48 +0100
Subject: [PATCH 72/97] fix: docker compose validation
---
app/Livewire/Project/New/DockerCompose.php | 9 +++++---
.../Project/Service/Configuration.php | 10 ++++++---
app/Livewire/Project/Service/StackForm.php | 5 ++++-
bootstrap/helpers/docker.php | 21 +++++++++++++++++++
4 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/app/Livewire/Project/New/DockerCompose.php b/app/Livewire/Project/New/DockerCompose.php
index dea5eca3e..79394d310 100644
--- a/app/Livewire/Project/New/DockerCompose.php
+++ b/app/Livewire/Project/New/DockerCompose.php
@@ -17,7 +17,6 @@ class DockerCompose extends Component
public array $query;
public function mount()
{
-
$this->parameters = get_route_parameters();
$this->query = request()->query();
if (isDev()) {
@@ -40,12 +39,17 @@ class DockerCompose extends Component
}
public function submit()
{
+ $server_id = $this->query['server_id'];
try {
$this->validate([
'dockerComposeRaw' => 'required'
]);
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
- $server_id = $this->query['server_id'];
+
+ $isValid = validateComposeFile($this->dockerComposeRaw, $server_id);
+ if ($isValid !== 'OK') {
+ return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
+ }
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
@@ -74,7 +78,6 @@ class DockerCompose extends Component
'environment_name' => $environment->name,
'project_uuid' => $project->uuid,
]);
-
} catch (\Throwable $e) {
return handleError($e, $this);
}
diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php
index 69d158c04..eb72c803c 100644
--- a/app/Livewire/Project/Service/Configuration.php
+++ b/app/Livewire/Project/Service/Configuration.php
@@ -38,8 +38,12 @@ class Configuration extends Component
}
public function check_status()
{
- dispatch_sync(new ContainerStatusJob($this->service->server));
- $this->dispatch('refresh')->self();
- $this->dispatch('serviceStatusChanged');
+ try {
+ dispatch_sync(new ContainerStatusJob($this->service->server));
+ $this->dispatch('refresh')->self();
+ $this->dispatch('serviceStatusChanged');
+ } catch (\Exception $e) {
+ return handleError($e, $this);
+ }
}
}
diff --git a/app/Livewire/Project/Service/StackForm.php b/app/Livewire/Project/Service/StackForm.php
index 1ec63a761..650dde792 100644
--- a/app/Livewire/Project/Service/StackForm.php
+++ b/app/Livewire/Project/Service/StackForm.php
@@ -41,7 +41,6 @@ class StackForm extends Component
}
public function saveCompose($raw)
{
-
$this->service->docker_compose_raw = $raw;
$this->submit();
}
@@ -55,6 +54,10 @@ class StackForm extends Component
{
try {
$this->validate();
+ $isValid = validateComposeFile($this->service->docker_compose_raw, $this->service->server->id);
+ if ($isValid !== 'OK') {
+ throw new \Exception("Invalid docker-compose file.\n$isValid");
+ }
$this->service->save();
$this->service->saveExtraFields($this->fields);
$this->service->parse();
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 754e65a97..9c56b627d 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -556,3 +556,24 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
}
return $compose_options->toArray();
}
+
+function validateComposeFile(string $compose, int $server_id): string|Throwable {
+ try {
+ $uuid = Str::random(10);
+ $server = Server::findOrFail($server_id);
+ $base64_compose = base64_encode($compose);
+ $output = instant_remote_process([
+ "echo {$base64_compose} | base64 -d > /tmp/{$uuid}.yml",
+ "docker compose -f /tmp/{$uuid}.yml config",
+ ], $server);
+ ray($output);
+ return 'OK';
+ } catch (\Throwable $e) {
+ ray($e);
+ return $e->getMessage();
+ } finally {
+ instant_remote_process([
+ "rm /tmp/{$uuid}.yml",
+ ], $server);
+ }
+}
From de4d0961da16a024f684537ae43c01778caadff7 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 14 Mar 2024 10:10:03 +0100
Subject: [PATCH 73/97] Add Sleep class and refactor container name in
generate_compose_file()
---
app/Jobs/ApplicationDeploymentJob.php | 40 +++++++++++++--------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 6ef04ced8..129df7814 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -24,6 +24,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
+use Illuminate\Support\Sleep;
use Illuminate\Support\Str;
use RuntimeException;
use Spatie\Url\Url;
@@ -811,7 +812,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
break;
}
$counter++;
- sleep($this->application->health_check_interval);
+ Sleep::for($this->application->health_check_interval)->seconds();
}
}
}
@@ -1146,7 +1147,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
'networks' => [
$this->destination->network,
],
-
'mem_limit' => $this->application->limits_memory,
'memswap_limit' => $this->application->limits_memory_swap,
'mem_swappiness' => $this->application->limits_memory_swappiness,
@@ -1275,24 +1275,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($this->pull_request_id === 0) {
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
- $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
- if (count($custom_compose) > 0) {
- $ipv4 = data_get($custom_compose, 'ip.0');
- $ipv6 = data_get($custom_compose, 'ip6.0');
- data_forget($custom_compose, 'ip');
- data_forget($custom_compose, 'ip6');
- if ($ipv4 || $ipv6) {
- data_forget($docker_compose['services'][$this->container_name], 'networks');
- }
- if ($ipv4) {
- $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
- }
- if ($ipv6) {
- $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
- }
- $docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
- }
- } else {
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
data_forget($docker_compose, 'services.' . $this->container_name);
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
@@ -1312,6 +1294,24 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
}
+ } else {
+ $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
+ if (count($custom_compose) > 0) {
+ $ipv4 = data_get($custom_compose, 'ip.0');
+ $ipv6 = data_get($custom_compose, 'ip6.0');
+ data_forget($custom_compose, 'ip');
+ data_forget($custom_compose, 'ip6');
+ if ($ipv4 || $ipv6) {
+ data_forget($docker_compose['services'][$this->container_name], 'networks');
+ }
+ if ($ipv4) {
+ $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
+ }
+ if ($ipv6) {
+ $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
+ }
+ $docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
+ }
}
}
From f994f83ce1cd90a4a471b2d678eabb23c815e9e5 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 14 Mar 2024 19:15:30 +0100
Subject: [PATCH 74/97] revert validateCompose until further investigation
---
bootstrap/helpers/docker.php | 1 +
bootstrap/helpers/shared.php | 10 ++++++++--
config/sentry.php | 2 +-
config/version.php | 2 +-
versions.json | 2 +-
5 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 9c56b627d..898789adf 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -558,6 +558,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
}
function validateComposeFile(string $compose, int $server_id): string|Throwable {
+ return 'OK';
try {
$uuid = Str::random(10);
$server = Server::findOrFail($server_id);
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 88bbb5cd5..3b9f5df53 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -914,8 +914,14 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// SERVICE_FQDN_UMAMI_1000
$port = $key->afterLast('_');
} else {
- // SERVICE_FQDN_UMAMI
- $port = null;
+ $last = $key->afterLast('_');
+ if (is_numeric($last->value())) {
+ // SERVICE_FQDN_3001
+ $port = $last;
+ } else {
+ // SERVICE_FQDN_UMAMI
+ $port = null;
+ }
}
if ($port) {
$fqdn = "$fqdn:$port";
diff --git a/config/sentry.php b/config/sentry.php
index 2d2d440c3..150b055c3 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.237',
+ 'release' => '4.0.0-beta.238',
// 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 73a72d6ad..6a881ff74 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Thu, 14 Mar 2024 19:18:07 +0100
Subject: [PATCH 75/97] fix: supabase edge functions fix: GOTRUE_SITE_URL
should be SERVICE_SITE_URL for correct redirects fix: DB_AFTER_CONNECT_QUERY
value will be incorrecly resolved if it's inside quotes chore: update image
versions
---
templates/compose/supabase.yaml | 29 ++++++++++++++++++++---------
templates/service-templates.json | 4 ++--
2 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/templates/compose/supabase.yaml b/templates/compose/supabase.yaml
index 39d9f5f24..222cbe8dd 100644
--- a/templates/compose/supabase.yaml
+++ b/templates/compose/supabase.yaml
@@ -202,8 +202,8 @@ services:
## Edge Functions routes
- name: functions-v1
- _comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*'
- url: http://functions:9000/
+ _comment: 'Edge Functions: /functions/v1/* -> http://supabase-edge-functions:9000/*'
+ url: http://supabase-edge-functions:9000/
routes:
- name: functions-v1-all
strip_path: true
@@ -256,7 +256,7 @@ services:
config:
hide_credentials: true
supabase-studio:
- image: supabase/studio:20240205-b145c86
+ image: supabase/studio:20240301-0942bfe
healthcheck:
test:
[
@@ -885,7 +885,7 @@ services:
- PGRST_APP_SETTINGS_JWT_EXP=${JWT_EXPIRY:-3600}
command: "postgrest"
supabase-auth:
- image: supabase/gotrue:v2.132.3
+ image: supabase/gotrue:v2.143.0
depends_on:
supabase-db:
# Disable this if you are using an external Postgres database
@@ -913,7 +913,7 @@ services:
- GOTRUE_DB_DRIVER=postgres
- GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOST:-supabase-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-supabase}
- - GOTRUE_SITE_URL=${SERVICE_FQDN_SUPABASE}
+ - GOTRUE_SITE_URL=${SERVICE_SITE_URL}
- GOTRUE_URI_ALLOW_LIST=${ADDITIONAL_REDIRECT_URLS}
- GOTRUE_DISABLE_SIGNUP=${DISABLE_SIGNUP:-false}
@@ -937,12 +937,23 @@ services:
- GOTRUE_MAILER_URLPATHS_CONFIRMATION=${MAILER_URLPATHS_CONFIRMATION:-/auth/v1/verify}
- GOTRUE_MAILER_URLPATHS_RECOVERY=${MAILER_URLPATHS_RECOVERY:-/auth/v1/verify}
- GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE=${MAILER_URLPATHS_EMAIL_CHANGE:-/auth/v1/verify}
+ - GOTRUE_MAILER_TEMPLATES_INVITE=${MAILER_TEMPLATES_INVITE}
+ - GOTRUE_MAILER_TEMPLATES_CONFIRMATION=${MAILER_TEMPLATES_CONFIRMATION}
+ - GOTRUE_MAILER_TEMPLATES_RECOVERY=${MAILER_TEMPLATES_RECOVERY}
+ - GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=${MAILER_TEMPLATES_MAGIC_LINK}
+ - GOTRUE_MAILER_TEMPLATES_EMAIL_CHANGE=${MAILER_TEMPLATES_EMAIL_CHANGE}
+
+ - GOTRUE_MAILER_SUBJECTS_CONFIRMATION=${MAILER_SUBJECTS_CONFIRMATION}
+ - GOTRUE_MAILER_SUBJECTS_RECOVERY=${MAILER_SUBJECTS_RECOVERY}
+ - GOTRUE_MAILER_SUBJECTS_MAGIC_LINK=${MAILER_SUBJECTS_MAGIC_LINK}
+ - GOTRUE_MAILER_SUBJECTS_EMAIL_CHANGE=${MAILER_SUBJECTS_EMAIL_CHANGE}
+ - GOTRUE_MAILER_SUBJECTS_INVITE=${MAILER_SUBJECTS_INVITE}
- GOTRUE_EXTERNAL_PHONE_ENABLED=${ENABLE_PHONE_SIGNUP:-true}
- GOTRUE_SMS_AUTOCONFIRM=${ENABLE_PHONE_AUTOCONFIRM:-true}
supabase-realtime:
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
- image: supabase/realtime:v2.25.50
+ image: supabase/realtime:v2.25.66
depends_on:
supabase-db:
# Disable this if you are using an external Postgres database
@@ -961,7 +972,7 @@ services:
- DB_USER=supabase_admin
- DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- DB_NAME=${POSTGRES_DB:-supabase}
- - DB_AFTER_CONNECT_QUERY='SET search_path TO _realtime'
+ - DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime
- DB_ENC_KEY=supabaserealtime
- API_JWT_SECRET=${SERVICE_PASSWORD_JWT}
- FLY_ALLOC_ID=fly123
@@ -1091,7 +1102,7 @@ services:
- ./volumes/storage:/var/lib/storage
supabase-meta:
- image: supabase/postgres-meta:v0.77.2
+ image: supabase/postgres-meta:v0.79.0
depends_on:
supabase-db:
# Disable this if you are using an external Postgres database
@@ -1107,7 +1118,7 @@ services:
- PG_META_DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
supabase-edge-functions:
- image: supabase/edge-runtime:v1.36.1
+ image: supabase/edge-runtime:v1.38.0
depends_on:
supabase-analytics:
condition: service_healthy
diff --git a/templates/service-templates.json b/templates/service-templates.json
index b2cc462e9..6068a125b 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -642,7 +642,7 @@
"supabase": {
"documentation": "https:\/\/supabase.io",
"slogan": "The open source Firebase alternative.",
- "compose": "c2VydmljZXM6CiAgc3VwYWJhc2Uta29uZzoKICAgIGltYWdlOiAna29uZzoyLjguMScKICAgIGVudHJ5cG9pbnQ6ICdiYXNoIC1jICcnZXZhbCAiZWNobyBcIiQkKGNhdCB+L3RlbXAueW1sKVwiIiA+IH4va29uZy55bWwgJiYgL2RvY2tlci1lbnRyeXBvaW50LnNoIGtvbmcgZG9ja2VyLXN0YXJ0JycnCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9TVVBBQkFTRQogICAgICAtICdKV1RfU0VSQ0VUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtIEtPTkdfREFUQUJBU0U9b2ZmCiAgICAgIC0gS09OR19ERUNMQVJBVElWRV9DT05GSUc9L2hvbWUva29uZy9rb25nLnltbAogICAgICAtICdLT05HX0ROU19PUkRFUj1MQVNULEEsQ05BTUUnCiAgICAgIC0gJ0tPTkdfUExVR0lOUz1yZXF1ZXN0LXRyYW5zZm9ybWVyLGNvcnMsa2V5LWF1dGgsYWNsLGJhc2ljLWF1dGgnCiAgICAgIC0gS09OR19OR0lOWF9QUk9YWV9QUk9YWV9CVUZGRVJfU0laRT0xNjBrCiAgICAgIC0gJ0tPTkdfTkdJTlhfUFJPWFlfUFJPWFlfQlVGRkVSUz02NCAxNjBrJwogICAgICAtICdTVVBBQkFTRV9BTk9OX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VBTk9OX0tFWX0nCiAgICAgIC0gJ1NVUEFCQVNFX1NFUlZJQ0VfS0VZPSR7U0VSVklDRV9TVVBBQkFTRVNFUlZJQ0VfS0VZfScKICAgICAgLSAnREFTSEJPQVJEX1VTRVJOQU1FPSR7U0VSVklDRV9VU0VSX0FETUlOfScKICAgICAgLSAnREFTSEJPQVJEX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9BRE1JTn0nCiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2FwaS9rb25nLnltbAogICAgICAgIHRhcmdldDogL2hvbWUva29uZy90ZW1wLnltbAogICAgICAgIGNvbnRlbnQ6ICJfZm9ybWF0X3ZlcnNpb246ICcyLjEnXG5fdHJhbnNmb3JtOiB0cnVlXG5cbiMjI1xuIyMjIENvbnN1bWVycyAvIFVzZXJzXG4jIyNcbmNvbnN1bWVyczpcbiAgLSB1c2VybmFtZTogREFTSEJPQVJEXG4gIC0gdXNlcm5hbWU6IGFub25cbiAgICBrZXlhdXRoX2NyZWRlbnRpYWxzOlxuICAgICAgLSBrZXk6ICRTVVBBQkFTRV9BTk9OX0tFWVxuICAtIHVzZXJuYW1lOiBzZXJ2aWNlX3JvbGVcbiAgICBrZXlhdXRoX2NyZWRlbnRpYWxzOlxuICAgICAgLSBrZXk6ICRTVVBBQkFTRV9TRVJWSUNFX0tFWVxuXG4jIyNcbiMjIyBBY2Nlc3MgQ29udHJvbCBMaXN0XG4jIyNcbmFjbHM6XG4gIC0gY29uc3VtZXI6IGFub25cbiAgICBncm91cDogYW5vblxuICAtIGNvbnN1bWVyOiBzZXJ2aWNlX3JvbGVcbiAgICBncm91cDogYWRtaW5cblxuIyMjXG4jIyMgRGFzaGJvYXJkIGNyZWRlbnRpYWxzXG4jIyNcbmJhc2ljYXV0aF9jcmVkZW50aWFsczpcbi0gY29uc3VtZXI6IERBU0hCT0FSRFxuICB1c2VybmFtZTogJERBU0hCT0FSRF9VU0VSTkFNRVxuICBwYXNzd29yZDogJERBU0hCT0FSRF9QQVNTV09SRFxuXG5cbiMjI1xuIyMjIEFQSSBSb3V0ZXNcbiMjI1xuc2VydmljZXM6XG5cbiAgIyMgT3BlbiBBdXRoIHJvdXRlc1xuICAtIG5hbWU6IGF1dGgtdjEtb3BlblxuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS92ZXJpZnlcbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGF1dGgtdjEtb3BlblxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2F1dGgvdjEvdmVyaWZ5XG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1jYWxsYmFja1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS9jYWxsYmFja1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogYXV0aC12MS1vcGVuLWNhbGxiYWNrXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvYXV0aC92MS9jYWxsYmFja1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgLSBuYW1lOiBhdXRoLXYxLW9wZW4tYXV0aG9yaXplXG4gICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtYXV0aDo5OTk5L2F1dGhvcml6ZVxuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogYXV0aC12MS1vcGVuLWF1dGhvcml6ZVxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2F1dGgvdjEvYXV0aG9yaXplXG4gICAgcGx1Z2luczpcbiAgICAgIC0gbmFtZTogY29yc1xuXG4gICMjIFNlY3VyZSBBdXRoIHJvdXRlc1xuICAtIG5hbWU6IGF1dGgtdjFcbiAgICBfY29tbWVudDogJ0dvVHJ1ZTogL2F1dGgvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2UtYXV0aDo5OTk5LyonXG4gICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtYXV0aDo5OTk5L1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogYXV0aC12MS1hbGxcbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9hdXRoL3YxL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgICAgIC0gbmFtZToga2V5LWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IGZhbHNlXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG4gICAgICAgICAgICAtIGFub25cblxuICAjIyBTZWN1cmUgUkVTVCByb3V0ZXNcbiAgLSBuYW1lOiByZXN0LXYxXG4gICAgX2NvbW1lbnQ6ICdQb3N0Z1JFU1Q6IC9yZXN0L3YxLyogLT4gaHR0cDovL3N1cGFiYXNlLXJlc3Q6MzAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLXJlc3Q6MzAwMC9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IHJlc3QtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvcmVzdC92MS9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiB0cnVlXG4gICAgICAtIG5hbWU6IGFjbFxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlXG4gICAgICAgICAgYWxsb3c6XG4gICAgICAgICAgICAtIGFkbWluXG4gICAgICAgICAgICAtIGFub25cblxuICAjIyBTZWN1cmUgR3JhcGhRTCByb3V0ZXNcbiAgLSBuYW1lOiBncmFwaHFsLXYxXG4gICAgX2NvbW1lbnQ6ICdQb3N0Z1JFU1Q6IC9ncmFwaHFsL3YxLyogLT4gaHR0cDovL3N1cGFiYXNlLXJlc3Q6MzAwMC9ycGMvZ3JhcGhxbCdcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvcnBjL2dyYXBocWxcbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGdyYXBocWwtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvZ3JhcGhxbC92MVxuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGNvcnNcbiAgICAgIC0gbmFtZToga2V5LWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IHRydWVcbiAgICAgIC0gbmFtZTogcmVxdWVzdC10cmFuc2Zvcm1lclxuICAgICAgICBjb25maWc6XG4gICAgICAgICAgYWRkOlxuICAgICAgICAgICAgaGVhZGVyczpcbiAgICAgICAgICAgICAgLSBDb250ZW50LVByb2ZpbGU6Z3JhcGhxbF9wdWJsaWNcbiAgICAgIC0gbmFtZTogYWNsXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2dyb3Vwc19oZWFkZXI6IHRydWVcbiAgICAgICAgICBhbGxvdzpcbiAgICAgICAgICAgIC0gYWRtaW5cbiAgICAgICAgICAgIC0gYW5vblxuXG4gICMjIFNlY3VyZSBSZWFsdGltZSByb3V0ZXNcbiAgLSBuYW1lOiByZWFsdGltZS12MVxuICAgIF9jb21tZW50OiAnUmVhbHRpbWU6IC9yZWFsdGltZS92MS8qIC0+IHdzOi8vcmVhbHRpbWU6NDAwMC9zb2NrZXQvKidcbiAgICB1cmw6IGh0dHA6Ly9yZWFsdGltZS1kZXYuc3VwYWJhc2UtcmVhbHRpbWU6NDAwMC9zb2NrZXQvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiByZWFsdGltZS12MS1hbGxcbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9yZWFsdGltZS92MS9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiBmYWxzZVxuICAgICAgLSBuYW1lOiBhY2xcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZVxuICAgICAgICAgIGFsbG93OlxuICAgICAgICAgICAgLSBhZG1pblxuICAgICAgICAgICAgLSBhbm9uXG5cbiAgIyMgU3RvcmFnZSByb3V0ZXM6IHRoZSBzdG9yYWdlIHNlcnZlciBtYW5hZ2VzIGl0cyBvd24gYXV0aFxuICAtIG5hbWU6IHN0b3JhZ2UtdjFcbiAgICBfY29tbWVudDogJ1N0b3JhZ2U6IC9zdG9yYWdlL3YxLyogLT4gaHR0cDovL3N1cGFiYXNlLXN0b3JhZ2U6NTAwMC8qJ1xuICAgIHVybDogaHR0cDovL3N1cGFiYXNlLXN0b3JhZ2U6NTAwMC9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IHN0b3JhZ2UtdjEtYWxsXG4gICAgICAgIHN0cmlwX3BhdGg6IHRydWVcbiAgICAgICAgcGF0aHM6XG4gICAgICAgICAgLSAvc3RvcmFnZS92MS9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG5cbiAgIyMgRWRnZSBGdW5jdGlvbnMgcm91dGVzXG4gIC0gbmFtZTogZnVuY3Rpb25zLXYxXG4gICAgX2NvbW1lbnQ6ICdFZGdlIEZ1bmN0aW9uczogL2Z1bmN0aW9ucy92MS8qIC0+IGh0dHA6Ly9mdW5jdGlvbnM6OTAwMC8qJ1xuICAgIHVybDogaHR0cDovL2Z1bmN0aW9uczo5MDAwL1xuICAgIHJvdXRlczpcbiAgICAgIC0gbmFtZTogZnVuY3Rpb25zLXYxLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL2Z1bmN0aW9ucy92MS9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG5cbiAgIyMgQW5hbHl0aWNzIHJvdXRlc1xuICAtIG5hbWU6IGFuYWx5dGljcy12MVxuICAgIF9jb21tZW50OiAnQW5hbHl0aWNzOiAvYW5hbHl0aWNzL3YxLyogLT4gaHR0cDovL2xvZ2ZsYXJlOjQwMDAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGFuYWx5dGljcy12MS1hbGxcbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9hbmFseXRpY3MvdjEvXG5cbiAgIyMgU2VjdXJlIERhdGFiYXNlIHJvdXRlc1xuICAtIG5hbWU6IG1ldGFcbiAgICBfY29tbWVudDogJ3BnLW1ldGE6IC9wZy8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1tZXRhOjgwODAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1tZXRhOjgwODAvXG4gICAgcm91dGVzOlxuICAgICAgLSBuYW1lOiBtZXRhLWFsbFxuICAgICAgICBzdHJpcF9wYXRoOiB0cnVlXG4gICAgICAgIHBhdGhzOlxuICAgICAgICAgIC0gL3BnL1xuICAgIHBsdWdpbnM6XG4gICAgICAtIG5hbWU6IGtleS1hdXRoXG4gICAgICAgIGNvbmZpZzpcbiAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiBmYWxzZVxuICAgICAgLSBuYW1lOiBhY2xcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZVxuICAgICAgICAgIGFsbG93OlxuICAgICAgICAgICAgLSBhZG1pblxuXG4gICMjIFByb3RlY3RlZCBEYXNoYm9hcmQgLSBjYXRjaCBhbGwgcmVtYWluaW5nIHJvdXRlc1xuICAtIG5hbWU6IGRhc2hib2FyZFxuICAgIF9jb21tZW50OiAnU3R1ZGlvOiAvKiAtPiBodHRwOi8vc3R1ZGlvOjMwMDAvKidcbiAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1zdHVkaW86MzAwMC9cbiAgICByb3V0ZXM6XG4gICAgICAtIG5hbWU6IGRhc2hib2FyZC1hbGxcbiAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZVxuICAgICAgICBwYXRoczpcbiAgICAgICAgICAtIC9cbiAgICBwbHVnaW5zOlxuICAgICAgLSBuYW1lOiBjb3JzXG4gICAgICAtIG5hbWU6IGJhc2ljLWF1dGhcbiAgICAgICAgY29uZmlnOlxuICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IHRydWVcbiIKICBzdXBhYmFzZS1zdHVkaW86CiAgICBpbWFnZTogJ3N1cGFiYXNlL3N0dWRpbzoyMDI0MDIwNS1iMTQ1Yzg2JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG5vZGUKICAgICAgICAtICctZScKICAgICAgICAtICJyZXF1aXJlKCdodHRwJykuZ2V0KCdodHRwOi8vbG9jYWxob3N0OjMwMDAvYXBpL3Byb2ZpbGUnLCAocikgPT4ge2lmIChyLnN0YXR1c0NvZGUgIT09IDIwMCkgdGhyb3cgbmV3IEVycm9yKHIuc3RhdHVzQ29kZSl9KSIKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gSE9TVE5BTUU9MC4wLjAuMAogICAgICAtICdTVFVESU9fUEdfTUVUQV9VUkw9aHR0cDovL3N1cGFiYXNlLW1ldGE6ODA4MCcKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnREVGQVVMVF9PUkdBTklaQVRJT05fTkFNRT0ke1NUVURJT19ERUZBVUxUX09SR0FOSVpBVElPTjotRGVmYXVsdCBPcmdhbml6YXRpb259JwogICAgICAtICdERUZBVUxUX1BST0pFQ1RfTkFNRT0ke1NUVURJT19ERUZBVUxUX1BST0pFQ1Q6LURlZmF1bHQgUHJvamVjdH0nCiAgICAgIC0gJ1NVUEFCQVNFX1VSTD1odHRwOi8vc3VwYWJhc2Uta29uZzo4MDAwJwogICAgICAtICdTVVBBQkFTRV9QVUJMSUNfVVJMPSR7U0VSVklDRV9GUUROX1NVUEFCQVNFfScKICAgICAgLSAnU1VQQUJBU0VfQU5PTl9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9JwogICAgICAtICdTVVBBQkFTRV9TRVJWSUNFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0nCiAgICAgIC0gJ0xPR0ZMQVJFX0FQSV9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX0xPR0ZMQVJFfScKICAgICAgLSAnTE9HRkxBUkVfVVJMPWh0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMCcKICAgICAgLSBORVhUX1BVQkxJQ19FTkFCTEVfTE9HUz10cnVlCiAgICAgIC0gTkVYVF9BTkFMWVRJQ1NfQkFDS0VORF9QUk9WSURFUj1wb3N0Z3JlcwogIHN1cGFiYXNlLWRiOgogICAgaW1hZ2U6ICdzdXBhYmFzZS9wb3N0Z3JlczoxNS4xLjAuMTQ3JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICdwZ19pc3JlYWR5IC1VIHBvc3RncmVzIC1oIGxvY2FsaG9zdCcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS12ZWN0b3I6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGNvbW1hbmQ6CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSAnLWMnCiAgICAgIC0gY29uZmlnX2ZpbGU9L2V0Yy9wb3N0Z3Jlc3FsL3Bvc3RncmVzcWwuY29uZgogICAgICAtICctYycKICAgICAgLSBsb2dfbWluX21lc3NhZ2VzPWZhdGFsCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfSE9TVD0vdmFyL3J1bi9wb3N0Z3Jlc3FsCiAgICAgIC0gJ1BHUE9SVD0ke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9JwogICAgICAtICdQT1NUR1JFU19QT1JUPSR7UE9TVEdSRVNfUE9SVDotNTQzMn0nCiAgICAgIC0gJ1BHUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUEdEQVRBQkFTRT0ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXN1cGFiYXNlfScKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnSldUX0VYUD0ke0pXVF9FWFBJUlk6LTM2MDB9JwogICAgdm9sdW1lczoKICAgICAgLSAnc3VwYWJhc2UtZGItZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvcmVhbHRpbWUuc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvbWlncmF0aW9ucy85OS1yZWFsdGltZS5zcWwKICAgICAgICBjb250ZW50OiAiXFxzZXQgcGd1c2VyIGBlY2hvIFwic3VwYWJhc2VfYWRtaW5cImBcblxuY3JlYXRlIHNjaGVtYSBpZiBub3QgZXhpc3RzIF9yZWFsdGltZTtcbmFsdGVyIHNjaGVtYSBfcmVhbHRpbWUgb3duZXIgdG8gOnBndXNlcjtcbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi93ZWJob29rcy5zcWwKICAgICAgICB0YXJnZXQ6IC9kb2NrZXItZW50cnlwb2ludC1pbml0ZGIuZC9pbml0LXNjcmlwdHMvOTgtd2ViaG9va3Muc3FsCiAgICAgICAgY29udGVudDogIkJFR0lOO1xuLS0gQ3JlYXRlIHBnX25ldCBleHRlbnNpb25cbkNSRUFURSBFWFRFTlNJT04gSUYgTk9UIEVYSVNUUyBwZ19uZXQgU0NIRU1BIGV4dGVuc2lvbnM7XG4tLSBDcmVhdGUgc3VwYWJhc2VfZnVuY3Rpb25zIHNjaGVtYVxuQ1JFQVRFIFNDSEVNQSBzdXBhYmFzZV9mdW5jdGlvbnMgQVVUSE9SSVpBVElPTiBzdXBhYmFzZV9hZG1pbjtcbkdSQU5UIFVTQUdFIE9OIFNDSEVNQSBzdXBhYmFzZV9mdW5jdGlvbnMgVE8gcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbkFMVEVSIERFRkFVTFQgUFJJVklMRUdFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIEdSQU5UIEFMTCBPTiBUQUJMRVMgVE8gcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbkFMVEVSIERFRkFVTFQgUFJJVklMRUdFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIEdSQU5UIEFMTCBPTiBGVU5DVElPTlMgVE8gcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbkFMVEVSIERFRkFVTFQgUFJJVklMRUdFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIEdSQU5UIEFMTCBPTiBTRVFVRU5DRVMgVE8gcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbi0tIHN1cGFiYXNlX2Z1bmN0aW9ucy5taWdyYXRpb25zIGRlZmluaXRpb25cbkNSRUFURSBUQUJMRSBzdXBhYmFzZV9mdW5jdGlvbnMubWlncmF0aW9ucyAoXG4gIHZlcnNpb24gdGV4dCBQUklNQVJZIEtFWSxcbiAgaW5zZXJ0ZWRfYXQgdGltZXN0YW1wdHogTk9UIE5VTEwgREVGQVVMVCBOT1coKVxuKTtcbi0tIEluaXRpYWwgc3VwYWJhc2VfZnVuY3Rpb25zIG1pZ3JhdGlvblxuSU5TRVJUIElOVE8gc3VwYWJhc2VfZnVuY3Rpb25zLm1pZ3JhdGlvbnMgKHZlcnNpb24pIFZBTFVFUyAoJ2luaXRpYWwnKTtcbi0tIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyBkZWZpbml0aW9uXG5DUkVBVEUgVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIChcbiAgaWQgYmlnc2VyaWFsIFBSSU1BUlkgS0VZLFxuICBob29rX3RhYmxlX2lkIGludGVnZXIgTk9UIE5VTEwsXG4gIGhvb2tfbmFtZSB0ZXh0IE5PVCBOVUxMLFxuICBjcmVhdGVkX2F0IHRpbWVzdGFtcHR6IE5PVCBOVUxMIERFRkFVTFQgTk9XKCksXG4gIHJlcXVlc3RfaWQgYmlnaW50XG4pO1xuQ1JFQVRFIElOREVYIHN1cGFiYXNlX2Z1bmN0aW9uc19ob29rc19yZXF1ZXN0X2lkX2lkeCBPTiBzdXBhYmFzZV9mdW5jdGlvbnMuaG9va3MgVVNJTkcgYnRyZWUgKHJlcXVlc3RfaWQpO1xuQ1JFQVRFIElOREVYIHN1cGFiYXNlX2Z1bmN0aW9uc19ob29rc19oX3RhYmxlX2lkX2hfbmFtZV9pZHggT04gc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIFVTSU5HIGJ0cmVlIChob29rX3RhYmxlX2lkLCBob29rX25hbWUpO1xuQ09NTUVOVCBPTiBUQUJMRSBzdXBhYmFzZV9mdW5jdGlvbnMuaG9va3MgSVMgJ1N1cGFiYXNlIEZ1bmN0aW9ucyBIb29rczogQXVkaXQgdHJhaWwgZm9yIHRyaWdnZXJlZCBob29rcy4nO1xuQ1JFQVRFIEZVTkNUSU9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKVxuICBSRVRVUk5TIHRyaWdnZXJcbiAgTEFOR1VBR0UgcGxwZ3NxbFxuICBBUyAkZnVuY3Rpb24kXG4gIERFQ0xBUkVcbiAgICByZXF1ZXN0X2lkIGJpZ2ludDtcbiAgICBwYXlsb2FkIGpzb25iO1xuICAgIHVybCB0ZXh0IDo9IFRHX0FSR1ZbMF06OnRleHQ7XG4gICAgbWV0aG9kIHRleHQgOj0gVEdfQVJHVlsxXTo6dGV4dDtcbiAgICBoZWFkZXJzIGpzb25iIERFRkFVTFQgJ3t9Jzo6anNvbmI7XG4gICAgcGFyYW1zIGpzb25iIERFRkFVTFQgJ3t9Jzo6anNvbmI7XG4gICAgdGltZW91dF9tcyBpbnRlZ2VyIERFRkFVTFQgMTAwMDtcbiAgQkVHSU5cbiAgICBJRiB1cmwgSVMgTlVMTCBPUiB1cmwgPSAnbnVsbCcgVEhFTlxuICAgICAgUkFJU0UgRVhDRVBUSU9OICd1cmwgYXJndW1lbnQgaXMgbWlzc2luZyc7XG4gICAgRU5EIElGO1xuXG4gICAgSUYgbWV0aG9kIElTIE5VTEwgT1IgbWV0aG9kID0gJ251bGwnIFRIRU5cbiAgICAgIFJBSVNFIEVYQ0VQVElPTiAnbWV0aG9kIGFyZ3VtZW50IGlzIG1pc3NpbmcnO1xuICAgIEVORCBJRjtcblxuICAgIElGIFRHX0FSR1ZbMl0gSVMgTlVMTCBPUiBUR19BUkdWWzJdID0gJ251bGwnIFRIRU5cbiAgICAgIGhlYWRlcnMgPSAne1wiQ29udGVudC1UeXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwifSc6Ompzb25iO1xuICAgIEVMU0VcbiAgICAgIGhlYWRlcnMgPSBUR19BUkdWWzJdOjpqc29uYjtcbiAgICBFTkQgSUY7XG5cbiAgICBJRiBUR19BUkdWWzNdIElTIE5VTEwgT1IgVEdfQVJHVlszXSA9ICdudWxsJyBUSEVOXG4gICAgICBwYXJhbXMgPSAne30nOjpqc29uYjtcbiAgICBFTFNFXG4gICAgICBwYXJhbXMgPSBUR19BUkdWWzNdOjpqc29uYjtcbiAgICBFTkQgSUY7XG5cbiAgICBJRiBUR19BUkdWWzRdIElTIE5VTEwgT1IgVEdfQVJHVls0XSA9ICdudWxsJyBUSEVOXG4gICAgICB0aW1lb3V0X21zID0gMTAwMDtcbiAgICBFTFNFXG4gICAgICB0aW1lb3V0X21zID0gVEdfQVJHVls0XTo6aW50ZWdlcjtcbiAgICBFTkQgSUY7XG5cbiAgICBDQVNFXG4gICAgICBXSEVOIG1ldGhvZCA9ICdHRVQnIFRIRU5cbiAgICAgICAgU0VMRUNUIGh0dHBfZ2V0IElOVE8gcmVxdWVzdF9pZCBGUk9NIG5ldC5odHRwX2dldChcbiAgICAgICAgICB1cmwsXG4gICAgICAgICAgcGFyYW1zLFxuICAgICAgICAgIGhlYWRlcnMsXG4gICAgICAgICAgdGltZW91dF9tc1xuICAgICAgICApO1xuICAgICAgV0hFTiBtZXRob2QgPSAnUE9TVCcgVEhFTlxuICAgICAgICBwYXlsb2FkID0ganNvbmJfYnVpbGRfb2JqZWN0KFxuICAgICAgICAgICdvbGRfcmVjb3JkJywgT0xELFxuICAgICAgICAgICdyZWNvcmQnLCBORVcsXG4gICAgICAgICAgJ3R5cGUnLCBUR19PUCxcbiAgICAgICAgICAndGFibGUnLCBUR19UQUJMRV9OQU1FLFxuICAgICAgICAgICdzY2hlbWEnLCBUR19UQUJMRV9TQ0hFTUFcbiAgICAgICAgKTtcblxuICAgICAgICBTRUxFQ1QgaHR0cF9wb3N0IElOVE8gcmVxdWVzdF9pZCBGUk9NIG5ldC5odHRwX3Bvc3QoXG4gICAgICAgICAgdXJsLFxuICAgICAgICAgIHBheWxvYWQsXG4gICAgICAgICAgcGFyYW1zLFxuICAgICAgICAgIGhlYWRlcnMsXG4gICAgICAgICAgdGltZW91dF9tc1xuICAgICAgICApO1xuICAgICAgRUxTRVxuICAgICAgICBSQUlTRSBFWENFUFRJT04gJ21ldGhvZCBhcmd1bWVudCAlIGlzIGludmFsaWQnLCBtZXRob2Q7XG4gICAgRU5EIENBU0U7XG5cbiAgICBJTlNFUlQgSU5UTyBzdXBhYmFzZV9mdW5jdGlvbnMuaG9va3NcbiAgICAgIChob29rX3RhYmxlX2lkLCBob29rX25hbWUsIHJlcXVlc3RfaWQpXG4gICAgVkFMVUVTXG4gICAgICAoVEdfUkVMSUQsIFRHX05BTUUsIHJlcXVlc3RfaWQpO1xuXG4gICAgUkVUVVJOIE5FVztcbiAgRU5EXG4kZnVuY3Rpb24kO1xuLS0gU3VwYWJhc2Ugc3VwZXIgYWRtaW5cbkRPXG4kJFxuQkVHSU5cbiAgSUYgTk9UIEVYSVNUUyAoXG4gICAgU0VMRUNUIDFcbiAgICBGUk9NIHBnX3JvbGVzXG4gICAgV0hFUkUgcm9sbmFtZSA9ICdzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4nXG4gIClcbiAgVEhFTlxuICAgIENSRUFURSBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBOT0lOSEVSSVQgQ1JFQVRFUk9MRSBMT0dJTiBOT1JFUExJQ0FUSU9OO1xuICBFTkQgSUY7XG5FTkRcbiQkO1xuR1JBTlQgQUxMIFBSSVZJTEVHRVMgT04gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47XG5HUkFOVCBBTEwgUFJJVklMRUdFUyBPTiBBTEwgVEFCTEVTIElOIFNDSEVNQSBzdXBhYmFzZV9mdW5jdGlvbnMgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluO1xuR1JBTlQgQUxMIFBSSVZJTEVHRVMgT04gQUxMIFNFUVVFTkNFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjtcbkFMVEVSIFVTRVIgc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluIFNFVCBzZWFyY2hfcGF0aCA9IFwic3VwYWJhc2VfZnVuY3Rpb25zXCI7XG5BTFRFUiB0YWJsZSBcInN1cGFiYXNlX2Z1bmN0aW9uc1wiLm1pZ3JhdGlvbnMgT1dORVIgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluO1xuQUxURVIgdGFibGUgXCJzdXBhYmFzZV9mdW5jdGlvbnNcIi5ob29rcyBPV05FUiBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47XG5BTFRFUiBmdW5jdGlvbiBcInN1cGFiYXNlX2Z1bmN0aW9uc1wiLmh0dHBfcmVxdWVzdCgpIE9XTkVSIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjtcbkdSQU5UIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBUTyBwb3N0Z3Jlcztcbi0tIFJlbW92ZSB1bnVzZWQgc3VwYWJhc2VfcGdfbmV0X2FkbWluIHJvbGVcbkRPXG4kJFxuQkVHSU5cbiAgSUYgRVhJU1RTIChcbiAgICBTRUxFQ1QgMVxuICAgIEZST00gcGdfcm9sZXNcbiAgICBXSEVSRSByb2xuYW1lID0gJ3N1cGFiYXNlX3BnX25ldF9hZG1pbidcbiAgKVxuICBUSEVOXG4gICAgUkVBU1NJR04gT1dORUQgQlkgc3VwYWJhc2VfcGdfbmV0X2FkbWluIFRPIHN1cGFiYXNlX2FkbWluO1xuICAgIERST1AgT1dORUQgQlkgc3VwYWJhc2VfcGdfbmV0X2FkbWluO1xuICAgIERST1AgUk9MRSBzdXBhYmFzZV9wZ19uZXRfYWRtaW47XG4gIEVORCBJRjtcbkVORFxuJCQ7XG4tLSBwZ19uZXQgZ3JhbnRzIHdoZW4gZXh0ZW5zaW9uIGlzIGFscmVhZHkgZW5hYmxlZFxuRE9cbiQkXG5CRUdJTlxuICBJRiBFWElTVFMgKFxuICAgIFNFTEVDVCAxXG4gICAgRlJPTSBwZ19leHRlbnNpb25cbiAgICBXSEVSRSBleHRuYW1lID0gJ3BnX25ldCdcbiAgKVxuICBUSEVOXG4gICAgR1JBTlQgVVNBR0UgT04gU0NIRU1BIG5ldCBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4gICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFQ1VSSVRZIERFRklORVI7XG4gICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRUNVUklUWSBERUZJTkVSO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRVQgc2VhcmNoX3BhdGggPSBuZXQ7XG4gICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRVQgc2VhcmNoX3BhdGggPSBuZXQ7XG4gICAgUkVWT0tFIEFMTCBPTiBGVU5DVElPTiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgRlJPTSBQVUJMSUM7XG4gICAgUkVWT0tFIEFMTCBPTiBGVU5DVElPTiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIEZST00gUFVCTElDO1xuICAgIEdSQU5UIEVYRUNVVEUgT04gRlVOQ1RJT04gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbiAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlO1xuICBFTkQgSUY7XG5FTkRcbiQkO1xuLS0gRXZlbnQgdHJpZ2dlciBmb3IgcGdfbmV0XG5DUkVBVEUgT1IgUkVQTEFDRSBGVU5DVElPTiBleHRlbnNpb25zLmdyYW50X3BnX25ldF9hY2Nlc3MoKVxuUkVUVVJOUyBldmVudF90cmlnZ2VyXG5MQU5HVUFHRSBwbHBnc3FsXG5BUyAkJFxuQkVHSU5cbiAgSUYgRVhJU1RTIChcbiAgICBTRUxFQ1QgMVxuICAgIEZST00gcGdfZXZlbnRfdHJpZ2dlcl9kZGxfY29tbWFuZHMoKSBBUyBldlxuICAgIEpPSU4gcGdfZXh0ZW5zaW9uIEFTIGV4dFxuICAgIE9OIGV2Lm9iamlkID0gZXh0Lm9pZFxuICAgIFdIRVJFIGV4dC5leHRuYW1lID0gJ3BnX25ldCdcbiAgKVxuICBUSEVOXG4gICAgR1JBTlQgVVNBR0UgT04gU0NIRU1BIG5ldCBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7XG4gICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFQ1VSSVRZIERFRklORVI7XG4gICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRUNVUklUWSBERUZJTkVSO1xuICAgIEFMVEVSIGZ1bmN0aW9uIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRVQgc2VhcmNoX3BhdGggPSBuZXQ7XG4gICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBTRVQgc2VhcmNoX3BhdGggPSBuZXQ7XG4gICAgUkVWT0tFIEFMTCBPTiBGVU5DVElPTiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgRlJPTSBQVUJMSUM7XG4gICAgUkVWT0tFIEFMTCBPTiBGVU5DVElPTiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIEZST00gUFVCTElDO1xuICAgIEdSQU5UIEVYRUNVVEUgT04gRlVOQ1RJT04gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbiAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlO1xuICBFTkQgSUY7XG5FTkQ7XG4kJDtcbkNPTU1FTlQgT04gRlVOQ1RJT04gZXh0ZW5zaW9ucy5ncmFudF9wZ19uZXRfYWNjZXNzIElTICdHcmFudHMgYWNjZXNzIHRvIHBnX25ldCc7XG5ET1xuJCRcbkJFR0lOXG4gIElGIE5PVCBFWElTVFMgKFxuICAgIFNFTEVDVCAxXG4gICAgRlJPTSBwZ19ldmVudF90cmlnZ2VyXG4gICAgV0hFUkUgZXZ0bmFtZSA9ICdpc3N1ZV9wZ19uZXRfYWNjZXNzJ1xuICApIFRIRU5cbiAgICBDUkVBVEUgRVZFTlQgVFJJR0dFUiBpc3N1ZV9wZ19uZXRfYWNjZXNzIE9OIGRkbF9jb21tYW5kX2VuZCBXSEVOIFRBRyBJTiAoJ0NSRUFURSBFWFRFTlNJT04nKVxuICAgIEVYRUNVVEUgUFJPQ0VEVVJFIGV4dGVuc2lvbnMuZ3JhbnRfcGdfbmV0X2FjY2VzcygpO1xuICBFTkQgSUY7XG5FTkRcbiQkO1xuSU5TRVJUIElOVE8gc3VwYWJhc2VfZnVuY3Rpb25zLm1pZ3JhdGlvbnMgKHZlcnNpb24pIFZBTFVFUyAoJzIwMjEwODA5MTgzNDIzX3VwZGF0ZV9ncmFudHMnKTtcbkFMVEVSIGZ1bmN0aW9uIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKSBTRUNVUklUWSBERUZJTkVSO1xuQUxURVIgZnVuY3Rpb24gc3VwYWJhc2VfZnVuY3Rpb25zLmh0dHBfcmVxdWVzdCgpIFNFVCBzZWFyY2hfcGF0aCA9IHN1cGFiYXNlX2Z1bmN0aW9ucztcblJFVk9LRSBBTEwgT04gRlVOQ1RJT04gc3VwYWJhc2VfZnVuY3Rpb25zLmh0dHBfcmVxdWVzdCgpIEZST00gUFVCTElDO1xuR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KCkgVE8gcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTtcbkNPTU1JVDtcbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi9yb2xlcy5zcWwKICAgICAgICB0YXJnZXQ6IC9kb2NrZXItZW50cnlwb2ludC1pbml0ZGIuZC9pbml0LXNjcmlwdHMvOTktcm9sZXMuc3FsCiAgICAgICAgY29udGVudDogIi0tIE5PVEU6IGNoYW5nZSB0byB5b3VyIG93biBwYXNzd29yZHMgZm9yIHByb2R1Y3Rpb24gZW52aXJvbm1lbnRzXG4gXFxzZXQgcGdwYXNzIGBlY2hvIFwiJFBPU1RHUkVTX1BBU1NXT1JEXCJgXG5cbiBBTFRFUiBVU0VSIGF1dGhlbnRpY2F0b3IgV0lUSCBQQVNTV09SRCA6J3BncGFzcyc7XG4gQUxURVIgVVNFUiBwZ2JvdW5jZXIgV0lUSCBQQVNTV09SRCA6J3BncGFzcyc7XG4gQUxURVIgVVNFUiBzdXBhYmFzZV9hdXRoX2FkbWluIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnO1xuIEFMVEVSIFVTRVIgc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnO1xuIEFMVEVSIFVTRVIgc3VwYWJhc2Vfc3RvcmFnZV9hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJztcbiIKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi9qd3Quc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvaW5pdC1zY3JpcHRzLzk5LWp3dC5zcWwKICAgICAgICBjb250ZW50OiAiXFxzZXQgand0X3NlY3JldCBgZWNobyBcIiRKV1RfU0VDUkVUXCJgXG5cXHNldCBqd3RfZXhwIGBlY2hvIFwiJEpXVF9FWFBcImBcblxuQUxURVIgREFUQUJBU0UgcG9zdGdyZXMgU0VUIFwiYXBwLnNldHRpbmdzLmp3dF9zZWNyZXRcIiBUTyA6J2p3dF9zZWNyZXQnO1xuQUxURVIgREFUQUJBU0UgcG9zdGdyZXMgU0VUIFwiYXBwLnNldHRpbmdzLmp3dF9leHBcIiBUTyA6J2p3dF9leHAnO1xuIgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2RiL2xvZ3Muc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvbWlncmF0aW9ucy85OS1sb2dzLnNxbAogICAgICAgIGNvbnRlbnQ6ICJcXHNldCBwZ3VzZXIgYGVjaG8gXCJzdXBhYmFzZV9hZG1pblwiYFxuXG5jcmVhdGUgc2NoZW1hIGlmIG5vdCBleGlzdHMgX2FuYWx5dGljcztcbmFsdGVyIHNjaGVtYSBfYW5hbHl0aWNzIG93bmVyIHRvIDpwZ3VzZXI7XG4iCiAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgaW1hZ2U6ICdzdXBhYmFzZS9sb2dmbGFyZToxLjQuMCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo0MDAwL2hlYWx0aCcKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIExPR0ZMQVJFX05PREVfSE9TVD0xMjcuMC4wLjEKICAgICAgLSBEQl9VU0VSTkFNRT1zdXBhYmFzZV9hZG1pbgogICAgICAtICdEQl9EQVRBQkFTRT0ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0nCiAgICAgIC0gJ0RCX0hPU1ROQU1FPSR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9JwogICAgICAtICdEQl9QT1JUPSR7UE9TVEdSRVNfUE9SVDotNTQzMn0nCiAgICAgIC0gJ0RCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gREJfU0NIRU1BPV9hbmFseXRpY3MKICAgICAgLSAnTE9HRkxBUkVfQVBJX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTE9HRkxBUkV9JwogICAgICAtIExPR0ZMQVJFX1NJTkdMRV9URU5BTlQ9dHJ1ZQogICAgICAtIExPR0ZMQVJFX1NJTkdMRV9URU5BTlRfTU9ERT10cnVlCiAgICAgIC0gTE9HRkxBUkVfU1VQQUJBU0VfTU9ERT10cnVlCiAgICAgIC0gJ1BPU1RHUkVTX0JBQ0tFTkRfVVJMPXBvc3RncmVzcWw6Ly9zdXBhYmFzZV9hZG1pbjoke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QCR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9OiR7UE9TVEdSRVNfUE9SVDotNTQzMn0vJHtQT1NUR1JFU19EQjotc3VwYWJhc2V9JwogICAgICAtIFBPU1RHUkVTX0JBQ0tFTkRfU0NIRU1BPV9hbmFseXRpY3MKICAgICAgLSBMT0dGTEFSRV9GRUFUVVJFX0ZMQUdfT1ZFUlJJREU9bXVsdGliYWNrZW5kPXRydWUKICBzdXBhYmFzZS12ZWN0b3I6CiAgICBpbWFnZTogJ3RpbWJlcmlvL3ZlY3RvcjowLjI4LjEtYWxwaW5lJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIHdnZXQKICAgICAgICAtICctLW5vLXZlcmJvc2UnCiAgICAgICAgLSAnLS10cmllcz0xJwogICAgICAgIC0gJy0tc3BpZGVyJwogICAgICAgIC0gJ2h0dHA6Ly9zdXBhYmFzZS12ZWN0b3I6OTAwMS9oZWFsdGgnCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICB2b2x1bWVzOgogICAgICAtCiAgICAgICAgdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2xvZ3MvdmVjdG9yLnltbAogICAgICAgIHRhcmdldDogL2V0Yy92ZWN0b3IvdmVjdG9yLnltbAogICAgICAgIHJlYWRfb25seTogdHJ1ZQogICAgICAgIGNvbnRlbnQ6ICJhcGk6XG4gIGVuYWJsZWQ6IHRydWVcbiAgYWRkcmVzczogMC4wLjAuMDo5MDAxXG5cbnNvdXJjZXM6XG4gIGRvY2tlcl9ob3N0OlxuICAgIHR5cGU6IGRvY2tlcl9sb2dzXG4gICAgZXhjbHVkZV9jb250YWluZXJzOlxuICAgICAgLSBzdXBhYmFzZS12ZWN0b3JcblxudHJhbnNmb3JtczpcbiAgcHJvamVjdF9sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSBkb2NrZXJfaG9zdFxuICAgIHNvdXJjZTogfC1cbiAgICAgIC5wcm9qZWN0ID0gXCJkZWZhdWx0XCJcbiAgICAgIC5ldmVudF9tZXNzYWdlID0gZGVsKC5tZXNzYWdlKVxuICAgICAgLmFwcG5hbWUgPSBkZWwoLmNvbnRhaW5lcl9uYW1lKVxuICAgICAgZGVsKC5jb250YWluZXJfY3JlYXRlZF9hdClcbiAgICAgIGRlbCguY29udGFpbmVyX2lkKVxuICAgICAgZGVsKC5zb3VyY2VfdHlwZSlcbiAgICAgIGRlbCguc3RyZWFtKVxuICAgICAgZGVsKC5sYWJlbClcbiAgICAgIGRlbCguaW1hZ2UpXG4gICAgICBkZWwoLmhvc3QpXG4gICAgICBkZWwoLnN0cmVhbSlcbiAgcm91dGVyOlxuICAgIHR5cGU6IHJvdXRlXG4gICAgaW5wdXRzOlxuICAgICAgLSBwcm9qZWN0X2xvZ3NcbiAgICByb3V0ZTpcbiAgICAgIGtvbmc6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgXCJzdXBhYmFzZS1rb25nXCIpJ1xuICAgICAgYXV0aDogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCBcInN1cGFiYXNlLWF1dGhcIiknXG4gICAgICByZXN0OiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtcmVzdFwiKSdcbiAgICAgIHJlYWx0aW1lOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtcmVhbHRpbWVcIiknXG4gICAgICBzdG9yYWdlOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2Utc3RvcmFnZVwiKSdcbiAgICAgIGZ1bmN0aW9uczogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCBcInN1cGFiYXNlLWZ1bmN0aW9uc1wiKSdcbiAgICAgIGRiOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksIFwic3VwYWJhc2UtZGJcIiknXG4gICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXBcbiAga29uZ19sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSByb3V0ZXIua29uZ1xuICAgIHNvdXJjZTogfC1cbiAgICAgIHJlcSwgZXJyID0gcGFyc2VfbmdpbnhfbG9nKC5ldmVudF9tZXNzYWdlLCBcImNvbWJpbmVkXCIpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLnRpbWVzdGFtcCA9IHJlcS50aW1lc3RhbXBcbiAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5oZWFkZXJzLnJlZmVyZXIgPSByZXEucmVmZXJlclxuICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LmhlYWRlcnMudXNlcl9hZ2VudCA9IHJlcS5hZ2VudFxuICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LmhlYWRlcnMuY2ZfY29ubmVjdGluZ19pcCA9IHJlcS5jbGllbnRcbiAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5tZXRob2QgPSByZXEubWV0aG9kXG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucGF0aCA9IHJlcS5wYXRoXG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucHJvdG9jb2wgPSByZXEucHJvdG9jb2xcbiAgICAgICAgICAubWV0YWRhdGEucmVzcG9uc2Uuc3RhdHVzX2NvZGUgPSByZXEuc3RhdHVzXG4gICAgICB9XG4gICAgICBpZiBlcnIgIT0gbnVsbCB7XG4gICAgICAgIGFib3J0XG4gICAgICB9XG4gICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXBcbiAga29uZ19lcnI6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5rb25nXG4gICAgc291cmNlOiB8LVxuICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gXCJHRVRcIlxuICAgICAgLm1ldGFkYXRhLnJlc3BvbnNlLnN0YXR1c19jb2RlID0gMjAwXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX25naW54X2xvZyguZXZlbnRfbWVzc2FnZSwgXCJlcnJvclwiKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC50aW1lc3RhbXAgPSBwYXJzZWQudGltZXN0YW1wXG4gICAgICAgICAgLnNldmVyaXR5ID0gcGFyc2VkLnNldmVyaXR5XG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaG9zdCA9IHBhcnNlZC5ob3N0XG4gICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaGVhZGVycy5jZl9jb25uZWN0aW5nX2lwID0gcGFyc2VkLmNsaWVudFxuICAgICAgICAgIHVybCwgZXJyID0gc3BsaXQocGFyc2VkLnJlcXVlc3QsIFwiIFwiKVxuICAgICAgICAgIGlmIGVyciA9PSBudWxsIHtcbiAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gdXJsWzBdXG4gICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LnBhdGggPSB1cmxbMV1cbiAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QucHJvdG9jb2wgPSB1cmxbMl1cbiAgICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiBlcnIgIT0gbnVsbCB7XG4gICAgICAgIGFib3J0XG4gICAgICB9XG4gICMgR290cnVlIGxvZ3MgYXJlIHN0cnVjdHVyZWQganNvbiBzdHJpbmdzIHdoaWNoIGZyb250ZW5kIHBhcnNlcyBkaXJlY3RseS4gQnV0IHdlIGtlZXAgbWV0YWRhdGEgZm9yIGNvbnNpc3RlbmN5LlxuICBhdXRoX2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5hdXRoXG4gICAgc291cmNlOiB8LVxuICAgICAgcGFyc2VkLCBlcnIgPSBwYXJzZV9qc29uKC5ldmVudF9tZXNzYWdlKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC5tZXRhZGF0YS50aW1lc3RhbXAgPSBwYXJzZWQudGltZVxuICAgICAgICAgIC5tZXRhZGF0YSA9IG1lcmdlISgubWV0YWRhdGEsIHBhcnNlZClcbiAgICAgIH1cbiAgIyBQb3N0Z1JFU1QgbG9ncyBhcmUgc3RydWN0dXJlZCBzbyB3ZSBzZXBhcmF0ZSB0aW1lc3RhbXAgZnJvbSBtZXNzYWdlIHVzaW5nIHJlZ2V4XG4gIHJlc3RfbG9nczpcbiAgICB0eXBlOiByZW1hcFxuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLnJlc3RcbiAgICBzb3VyY2U6IHwtXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJ14oP1A8dGltZT4uKik6ICg\/UDxtc2c+LiopJCcpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnXG4gICAgICAgICAgLnRpbWVzdGFtcCA9IHRvX3RpbWVzdGFtcCEocGFyc2VkLnRpbWUpXG4gICAgICAgICAgLm1ldGFkYXRhLmhvc3QgPSAucHJvamVjdFxuICAgICAgfVxuICAjIFJlYWx0aW1lIGxvZ3MgYXJlIHN0cnVjdHVyZWQgc28gd2UgcGFyc2UgdGhlIHNldmVyaXR5IGxldmVsIHVzaW5nIHJlZ2V4IChpZ25vcmUgdGltZSBiZWNhdXNlIGl0IGhhcyBubyBkYXRlKVxuICByZWFsdGltZV9sb2dzOlxuICAgIHR5cGU6IHJlbWFwXG4gICAgaW5wdXRzOlxuICAgICAgLSByb3V0ZXIucmVhbHRpbWVcbiAgICBzb3VyY2U6IHwtXG4gICAgICAubWV0YWRhdGEucHJvamVjdCA9IGRlbCgucHJvamVjdClcbiAgICAgIC5tZXRhZGF0YS5leHRlcm5hbF9pZCA9IC5tZXRhZGF0YS5wcm9qZWN0XG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJ14oP1A8dGltZT5cXGQrOlxcZCs6XFxkK1xcLlxcZCspIFxcWyg\/UDxsZXZlbD5cXHcrKVxcXSAoP1A8bXNnPi4qKSQnKVxuICAgICAgaWYgZXJyID09IG51bGwge1xuICAgICAgICAgIC5ldmVudF9tZXNzYWdlID0gcGFyc2VkLm1zZ1xuICAgICAgICAgIC5tZXRhZGF0YS5sZXZlbCA9IHBhcnNlZC5sZXZlbFxuICAgICAgfVxuICAjIFN0b3JhZ2UgbG9ncyBtYXkgY29udGFpbiBqc29uIG9iamVjdHMgc28gd2UgcGFyc2UgdGhlbSBmb3IgY29tcGxldGVuZXNzXG4gIHN0b3JhZ2VfbG9nczpcbiAgICB0eXBlOiByZW1hcFxuICAgIGlucHV0czpcbiAgICAgIC0gcm91dGVyLnN0b3JhZ2VcbiAgICBzb3VyY2U6IHwtXG4gICAgICAubWV0YWRhdGEucHJvamVjdCA9IGRlbCgucHJvamVjdClcbiAgICAgIC5tZXRhZGF0YS50ZW5hbnRJZCA9IC5tZXRhZGF0YS5wcm9qZWN0XG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX2pzb24oLmV2ZW50X21lc3NhZ2UpXG4gICAgICBpZiBlcnIgPT0gbnVsbCB7XG4gICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnXG4gICAgICAgICAgLm1ldGFkYXRhLmxldmVsID0gcGFyc2VkLmxldmVsXG4gICAgICAgICAgLm1ldGFkYXRhLnRpbWVzdGFtcCA9IHBhcnNlZC50aW1lXG4gICAgICAgICAgLm1ldGFkYXRhLmNvbnRleHRbMF0uaG9zdCA9IHBhcnNlZC5ob3N0bmFtZVxuICAgICAgICAgIC5tZXRhZGF0YS5jb250ZXh0WzBdLnBpZCA9IHBhcnNlZC5waWRcbiAgICAgIH1cbiAgIyBQb3N0Z3JlcyBsb2dzIHNvbWUgbWVzc2FnZXMgdG8gc3RkZXJyIHdoaWNoIHdlIG1hcCB0byB3YXJuaW5nIHNldmVyaXR5IGxldmVsXG4gIGRiX2xvZ3M6XG4gICAgdHlwZTogcmVtYXBcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5kYlxuICAgIHNvdXJjZTogfC1cbiAgICAgIC5tZXRhZGF0YS5ob3N0ID0gXCJkYi1kZWZhdWx0XCJcbiAgICAgIC5tZXRhZGF0YS5wYXJzZWQudGltZXN0YW1wID0gLnRpbWVzdGFtcFxuXG4gICAgICBwYXJzZWQsIGVyciA9IHBhcnNlX3JlZ2V4KC5ldmVudF9tZXNzYWdlLCByJy4qKD9QPGxldmVsPklORk98Tk9USUNFfFdBUk5JTkd8RVJST1J8TE9HfEZBVEFMfFBBTklDPyk6LionLCBudW1lcmljX2dyb3VwczogdHJ1ZSlcblxuICAgICAgaWYgZXJyICE9IG51bGwgfHwgcGFyc2VkID09IG51bGwge1xuICAgICAgICAubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5ID0gXCJpbmZvXCJcbiAgICAgIH1cbiAgICAgIGlmIHBhcnNlZCAhPSBudWxsIHtcbiAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSBwYXJzZWQubGV2ZWxcbiAgICAgIH1cbiAgICAgIGlmIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPT0gXCJpbmZvXCIge1xuICAgICAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSBcImxvZ1wiXG4gICAgICB9XG4gICAgICAubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5ID0gdXBjYXNlISgubWV0YWRhdGEucGFyc2VkLmVycm9yX3NldmVyaXR5KVxuXG5zaW5rczpcbiAgbG9nZmxhcmVfYXV0aDpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIGF1dGhfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1nb3RydWUubG9ncy5wcm9kJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZfSdcbiAgbG9nZmxhcmVfcmVhbHRpbWU6XG4gICAgdHlwZTogJ2h0dHAnXG4gICAgaW5wdXRzOlxuICAgICAgLSByZWFsdGltZV9sb2dzXG4gICAgZW5jb2Rpbmc6XG4gICAgICBjb2RlYzogJ2pzb24nXG4gICAgbWV0aG9kOiAncG9zdCdcbiAgICByZXF1ZXN0OlxuICAgICAgcmV0cnlfbWF4X2R1cmF0aW9uX3NlY3M6IDEwXG4gICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwL2FwaS9sb2dzP3NvdXJjZV9uYW1lPXJlYWx0aW1lLmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nXG4gIGxvZ2ZsYXJlX3Jlc3Q6XG4gICAgdHlwZTogJ2h0dHAnXG4gICAgaW5wdXRzOlxuICAgICAgLSByZXN0X2xvZ3NcbiAgICBlbmNvZGluZzpcbiAgICAgIGNvZGVjOiAnanNvbidcbiAgICBtZXRob2Q6ICdwb3N0J1xuICAgIHJlcXVlc3Q6XG4gICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTBcbiAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M\/c291cmNlX25hbWU9cG9zdGdSRVNULmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nXG4gIGxvZ2ZsYXJlX2RiOlxuICAgIHR5cGU6ICdodHRwJ1xuICAgIGlucHV0czpcbiAgICAgIC0gZGJfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgICMgV2UgbXVzdCByb3V0ZSB0aGUgc2luayB0aHJvdWdoIGtvbmcgYmVjYXVzZSBpbmdlc3RpbmcgbG9ncyBiZWZvcmUgbG9nZmxhcmUgaXMgZnVsbHkgaW5pdGlhbGlzZWQgd2lsbFxuICAgICMgbGVhZCB0byBicm9rZW4gcXVlcmllcyBmcm9tIHN0dWRpby4gVGhpcyB3b3JrcyBieSB0aGUgYXNzdW1wdGlvbiB0aGF0IGNvbnRhaW5lcnMgYXJlIHN0YXJ0ZWQgaW4gdGhlXG4gICAgIyBmb2xsb3dpbmcgb3JkZXI6IHZlY3RvciA+IGRiID4gbG9nZmxhcmUgPiBrb25nXG4gICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWtvbmc6ODAwMC9hbmFseXRpY3MvdjEvYXBpL2xvZ3M\/c291cmNlX25hbWU9cG9zdGdyZXMubG9ncyZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nXG4gIGxvZ2ZsYXJlX2Z1bmN0aW9uczpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIHJvdXRlci5mdW5jdGlvbnNcbiAgICBlbmNvZGluZzpcbiAgICAgIGNvZGVjOiAnanNvbidcbiAgICBtZXRob2Q6ICdwb3N0J1xuICAgIHJlcXVlc3Q6XG4gICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTBcbiAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M\/c291cmNlX25hbWU9ZGVuby1yZWxheS1sb2dzJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZfSdcbiAgbG9nZmxhcmVfc3RvcmFnZTpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIHN0b3JhZ2VfbG9nc1xuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1zdG9yYWdlLmxvZ3MucHJvZC4yJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZfSdcbiAgbG9nZmxhcmVfa29uZzpcbiAgICB0eXBlOiAnaHR0cCdcbiAgICBpbnB1dHM6XG4gICAgICAtIGtvbmdfbG9nc1xuICAgICAgLSBrb25nX2VyclxuICAgIGVuY29kaW5nOlxuICAgICAgY29kZWM6ICdqc29uJ1xuICAgIG1ldGhvZDogJ3Bvc3QnXG4gICAgcmVxdWVzdDpcbiAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMFxuICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1jbG91ZGZsYXJlLmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nXG4iCiAgICAgIC0gJy92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrOnJvJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0xPR0ZMQVJFX0FQSV9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX0xPR0ZMQVJFfScKICAgIGNvbW1hbmQ6CiAgICAgIC0gJy0tY29uZmlnJwogICAgICAtIGV0Yy92ZWN0b3IvdmVjdG9yLnltbAogIHN1cGFiYXNlLXJlc3Q6CiAgICBpbWFnZTogJ3Bvc3RncmVzdC9wb3N0Z3Jlc3Q6djEyLjAuMScKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGVudmlyb25tZW50OgogICAgICAtICdQR1JTVF9EQl9VUkk9cG9zdGdyZXM6Ly9hdXRoZW50aWNhdG9yOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0nCiAgICAgIC0gJ1BHUlNUX0RCX1NDSEVNQVM9JHtQR1JTVF9EQl9TQ0hFTUFTOi1wdWJsaWN9JwogICAgICAtIFBHUlNUX0RCX0FOT05fUk9MRT1hbm9uCiAgICAgIC0gJ1BHUlNUX0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gUEdSU1RfREJfVVNFX0xFR0FDWV9HVUNTPWZhbHNlCiAgICAgIC0gJ1BHUlNUX0FQUF9TRVRUSU5HU19KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtICdQR1JTVF9BUFBfU0VUVElOR1NfSldUX0VYUD0ke0pXVF9FWFBJUlk6LTM2MDB9JwogICAgY29tbWFuZDogcG9zdGdyZXN0CiAgc3VwYWJhc2UtYXV0aDoKICAgIGltYWdlOiAnc3VwYWJhc2UvZ290cnVlOnYyLjEzMi4zJwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tbm8tdmVyYm9zZScKICAgICAgICAtICctLXRyaWVzPTEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo5OTk5L2hlYWx0aCcKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPVFJVRV9BUElfSE9TVD0wLjAuMC4wCiAgICAgIC0gR09UUlVFX0FQSV9QT1JUPTk5OTkKICAgICAgLSAnQVBJX0VYVEVSTkFMX1VSTD0ke0FQSV9FWFRFUk5BTF9VUkw6LWh0dHA6Ly9zdXBhYmFzZS1rb25nOjgwMDB9JwogICAgICAtIEdPVFJVRV9EQl9EUklWRVI9cG9zdGdyZXMKICAgICAgLSAnR09UUlVFX0RCX0RBVEFCQVNFX1VSTD1wb3N0Z3JlczovL3N1cGFiYXNlX2F1dGhfYWRtaW46JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfUAke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifToke1BPU1RHUkVTX1BPUlQ6LTU0MzJ9LyR7UE9TVEdSRVNfREI6LXN1cGFiYXNlfScKICAgICAgLSAnR09UUlVFX1NJVEVfVVJMPSR7U0VSVklDRV9GUUROX1NVUEFCQVNFfScKICAgICAgLSAnR09UUlVFX1VSSV9BTExPV19MSVNUPSR7QURESVRJT05BTF9SRURJUkVDVF9VUkxTfScKICAgICAgLSAnR09UUlVFX0RJU0FCTEVfU0lHTlVQPSR7RElTQUJMRV9TSUdOVVA6LWZhbHNlfScKICAgICAgLSBHT1RSVUVfSldUX0FETUlOX1JPTEVTPXNlcnZpY2Vfcm9sZQogICAgICAtIEdPVFJVRV9KV1RfQVVEPWF1dGhlbnRpY2F0ZWQKICAgICAgLSBHT1RSVUVfSldUX0RFRkFVTFRfR1JPVVBfTkFNRT1hdXRoZW50aWNhdGVkCiAgICAgIC0gJ0dPVFJVRV9KV1RfRVhQPSR7SldUX0VYUElSWTotMzYwMH0nCiAgICAgIC0gJ0dPVFJVRV9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtICdHT1RSVUVfRVhURVJOQUxfRU1BSUxfRU5BQkxFRD0ke0VOQUJMRV9FTUFJTF9TSUdOVVA6LXRydWV9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX0FVVE9DT05GSVJNPSR7RU5BQkxFX0VNQUlMX0FVVE9DT05GSVJNOi1mYWxzZX0nCiAgICAgIC0gJ0dPVFJVRV9TTVRQX0FETUlOX0VNQUlMPSR7U01UUF9BRE1JTl9FTUFJTH0nCiAgICAgIC0gJ0dPVFJVRV9TTVRQX0hPU1Q9JHtTTVRQX0hPU1R9JwogICAgICAtICdHT1RSVUVfU01UUF9QT1JUPSR7U01UUF9QT1JUOi01ODd9JwogICAgICAtICdHT1RSVUVfU01UUF9VU0VSPSR7U01UUF9VU0VSfScKICAgICAgLSAnR09UUlVFX1NNVFBfUEFTUz0ke1NNVFBfUEFTU30nCiAgICAgIC0gJ0dPVFJVRV9TTVRQX1NFTkRFUl9OQU1FPSR7U01UUF9TRU5ERVJfTkFNRX0nCiAgICAgIC0gJ0dPVFJVRV9NQUlMRVJfVVJMUEFUSFNfSU5WSVRFPSR7TUFJTEVSX1VSTFBBVEhTX0lOVklURTotL2F1dGgvdjEvdmVyaWZ5fScKICAgICAgLSAnR09UUlVFX01BSUxFUl9VUkxQQVRIU19DT05GSVJNQVRJT049JHtNQUlMRVJfVVJMUEFUSFNfQ09ORklSTUFUSU9OOi0vYXV0aC92MS92ZXJpZnl9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX1JFQ09WRVJZPSR7TUFJTEVSX1VSTFBBVEhTX1JFQ09WRVJZOi0vYXV0aC92MS92ZXJpZnl9JwogICAgICAtICdHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX0VNQUlMX0NIQU5HRT0ke01BSUxFUl9VUkxQQVRIU19FTUFJTF9DSEFOR0U6LS9hdXRoL3YxL3ZlcmlmeX0nCiAgICAgIC0gJ0dPVFJVRV9FWFRFUk5BTF9QSE9ORV9FTkFCTEVEPSR7RU5BQkxFX1BIT05FX1NJR05VUDotdHJ1ZX0nCiAgICAgIC0gJ0dPVFJVRV9TTVNfQVVUT0NPTkZJUk09JHtFTkFCTEVfUEhPTkVfQVVUT0NPTkZJUk06LXRydWV9JwogIHN1cGFiYXNlLXJlYWx0aW1lOgogICAgaW1hZ2U6ICdzdXBhYmFzZS9yZWFsdGltZTp2Mi4yNS41MCcKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGJhc2gKICAgICAgICAtICctYycKICAgICAgICAtICdwcmludGYgXDAgPiAvZGV2L3RjcC9sb2NhbGhvc3QvNDAwMCcKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPUlQ9NDAwMAogICAgICAtICdEQl9IT1NUPSR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9JwogICAgICAtICdEQl9QT1JUPSR7UE9TVEdSRVNfUE9SVDotNTQzMn0nCiAgICAgIC0gREJfVVNFUj1zdXBhYmFzZV9hZG1pbgogICAgICAtICdEQl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdEQl9OQU1FPSR7UE9TVEdSRVNfREI6LXN1cGFiYXNlfScKICAgICAgLSAiREJfQUZURVJfQ09OTkVDVF9RVUVSWT0nU0VUIHNlYXJjaF9wYXRoIFRPIF9yZWFsdGltZSciCiAgICAgIC0gREJfRU5DX0tFWT1zdXBhYmFzZXJlYWx0aW1lCiAgICAgIC0gJ0FQSV9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtIEZMWV9BTExPQ19JRD1mbHkxMjMKICAgICAgLSBGTFlfQVBQX05BTUU9cmVhbHRpbWUKICAgICAgLSAnU0VDUkVUX0tFWV9CQVNFPSR7U0VDUkVUX1BBU1NXT1JEX1JFQUxUSU1FfScKICAgICAgLSAnRVJMX0FGTEFHUz0tcHJvdG9fZGlzdCBpbmV0X3RjcCcKICAgICAgLSBFTkFCTEVfVEFJTFNDQUxFPWZhbHNlCiAgICAgIC0gIkROU19OT0RFUz0nJyIKICAgIGNvbW1hbmQ6ICJzaCAtYyBcIi9hcHAvYmluL21pZ3JhdGUgJiYgL2FwcC9iaW4vcmVhbHRpbWUgZXZhbCAnUmVhbHRpbWUuUmVsZWFzZS5zZWVkcyhSZWFsdGltZS5SZXBvKScgJiYgL2FwcC9iaW4vc2VydmVyXCJcbiIKICBzdXBhYmFzZS1taW5pbzoKICAgIGltYWdlOiBtaW5pby9taW5pbwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIGNvbW1hbmQ6ICdzZXJ2ZXIgLS1jb25zb2xlLWFkZHJlc3MgIjo5MDAxIiAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiAnc2xlZXAgNSAmJiBleGl0IDAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogICAgdm9sdW1lczoKICAgICAgLSAnLi92b2x1bWVzL3N0b3JhZ2U6L2RhdGEnCiAgbWluaW8tY3JlYXRlYnVja2V0OgogICAgaW1hZ2U6IG1pbmlvL21jCiAgICByZXN0YXJ0OiAnbm8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudHJ5cG9pbnQ6CiAgICAgIC0gL2VudHJ5cG9pbnQuc2gKICAgIHZvbHVtZXM6CiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2VudHJ5cG9pbnQuc2gKICAgICAgICB0YXJnZXQ6IC9lbnRyeXBvaW50LnNoCiAgICAgICAgY29udGVudDogIiMhL2Jpbi9zaFxuL3Vzci9iaW4vbWMgYWxpYXMgc2V0IHN1cGFiYXNlLW1pbmlvIGh0dHA6Ly9zdXBhYmFzZS1taW5pbzo5MDAwICR7TUlOSU9fUk9PVF9VU0VSfSAke01JTklPX1JPT1RfUEFTU1dPUkR9O1xuL3Vzci9iaW4vbWMgbWIgc3VwYWJhc2UtbWluaW8vc3R1YjtcbmV4aXQgMFxuIgogIHN1cGFiYXNlLXN0b3JhZ2U6CiAgICBpbWFnZTogJ3N1cGFiYXNlL3N0b3JhZ2UtYXBpOnYwLjQ2LjQnCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1kYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBzdXBhYmFzZS1yZXN0OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICAgIGltZ3Byb3h5OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gd2dldAogICAgICAgIC0gJy0tbm8tdmVyYm9zZScKICAgICAgICAtICctLXRyaWVzPTEnCiAgICAgICAgLSAnLS1zcGlkZXInCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo1MDAwL3N0YXR1cycKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZFUl9QT1JUPTUwMDAKICAgICAgLSBTRVJWRVJfUkVHSU9OPWxvY2FsCiAgICAgIC0gTVVMVElfVEVOQU5UPWZhbHNlCiAgICAgIC0gJ0FVVEhfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vc3VwYWJhc2Vfc3RvcmFnZV9hZG1pbjoke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QCR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9OiR7UE9TVEdSRVNfUE9SVDotNTQzMn0vJHtQT1NUR1JFU19EQjotc3VwYWJhc2V9JwogICAgICAtIERCX0lOU1RBTExfUk9MRVM9ZmFsc2UKICAgICAgLSBTVE9SQUdFX0JBQ0tFTkQ9czMKICAgICAgLSBTVE9SQUdFX1MzX0JVQ0tFVD1zdHViCiAgICAgIC0gJ1NUT1JBR0VfUzNfRU5EUE9JTlQ9aHR0cDovL3N1cGFiYXNlLW1pbmlvOjkwMDAnCiAgICAgIC0gU1RPUkFHRV9TM19GT1JDRV9QQVRIX1NUWUxFPXRydWUKICAgICAgLSBTVE9SQUdFX1MzX1JFR0lPTj11cy1lYXN0LTEKICAgICAgLSAnQVdTX0FDQ0VTU19LRVlfSUQ9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSBVUExPQURfRklMRV9TSVpFX0xJTUlUPTUyNDI4ODAwMAogICAgICAtIFVQTE9BRF9GSUxFX1NJWkVfTElNSVRfU1RBTkRBUkQ9NTI0Mjg4MDAwCiAgICAgIC0gVVBMT0FEX1NJR05FRF9VUkxfRVhQSVJBVElPTl9USU1FPTEyMAogICAgICAtIFRVU19VUkxfUEFUSD0vdXBsb2FkL3Jlc3VtYWJsZQogICAgICAtIFRVU19NQVhfU0laRT0zNjAwMDAwCiAgICAgIC0gSU1BR0VfVFJBTlNGT1JNQVRJT05fRU5BQkxFRD10cnVlCiAgICAgIC0gJ0lNR1BST1hZX1VSTD1odHRwOi8vaW1ncHJveHk6ODA4MCcKICAgICAgLSBJTUdQUk9YWV9SRVFVRVNUX1RJTUVPVVQ9MTUKICAgICAgLSBEQVRBQkFTRV9TRUFSQ0hfUEFUSD1zdG9yYWdlCiAgICB2b2x1bWVzOgogICAgICAtICcuL3ZvbHVtZXMvc3RvcmFnZTovdmFyL2xpYi9zdG9yYWdlJwogIGltZ3Byb3h5OgogICAgaW1hZ2U6ICdkYXJ0aHNpbS9pbWdwcm94eTp2My44LjAnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gaW1ncHJveHkKICAgICAgICAtIGhlYWx0aAogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gSU1HUFJPWFlfTE9DQUxfRklMRVNZU1RFTV9ST09UPS8KICAgICAgLSBJTUdQUk9YWV9VU0VfRVRBRz10cnVlCiAgICAgIC0gJ0lNR1BST1hZX0VOQUJMRV9XRUJQX0RFVEVDVElPTj0ke0lNR1BST1hZX0VOQUJMRV9XRUJQX0RFVEVDVElPTjotdHJ1ZX0nCiAgICB2b2x1bWVzOgogICAgICAtICcuL3ZvbHVtZXMvc3RvcmFnZTovdmFyL2xpYi9zdG9yYWdlJwogIHN1cGFiYXNlLW1ldGE6CiAgICBpbWFnZTogJ3N1cGFiYXNlL3Bvc3RncmVzLW1ldGE6djAuNzcuMicKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUEdfTUVUQV9QT1JUPTgwODAKICAgICAgLSAnUEdfTUVUQV9EQl9IT1NUPSR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9JwogICAgICAtICdQR19NRVRBX0RCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfScKICAgICAgLSAnUEdfTUVUQV9EQl9OQU1FPSR7UE9TVEdSRVNfREI6LXN1cGFiYXNlfScKICAgICAgLSBQR19NRVRBX0RCX1VTRVI9c3VwYWJhc2VfYWRtaW4KICAgICAgLSAnUEdfTUVUQV9EQl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogIHN1cGFiYXNlLWVkZ2UtZnVuY3Rpb25zOgogICAgaW1hZ2U6ICdzdXBhYmFzZS9lZGdlLXJ1bnRpbWU6djEuMzYuMScKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ1NVUEFCQVNFX1VSTD1odHRwOi8vc3VwYWJhc2Uta29uZzo4MDAwJwogICAgICAtICdTVVBBQkFTRV9BTk9OX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VBTk9OX0tFWX0nCiAgICAgIC0gJ1NVUEFCQVNFX1NFUlZJQ0VfUk9MRV9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFU0VSVklDRV9LRVl9JwogICAgICAtICdTVVBBQkFTRV9EQl9VUkw9cG9zdGdyZXNxbDovL3Bvc3RncmVzOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0nCiAgICAgIC0gJ1ZFUklGWV9KV1Q9JHtGVU5DVElPTlNfVkVSSUZZX0pXVDotZmFsc2V9JwogICAgdm9sdW1lczoKICAgICAgLSAnLi92b2x1bWVzL2Z1bmN0aW9uczovaG9tZS9kZW5vL2Z1bmN0aW9ucycKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9mdW5jdGlvbnMvbWFpbi9pbmRleC50cwogICAgICAgIHRhcmdldDogL2hvbWUvZGVuby9mdW5jdGlvbnMvbWFpbi9pbmRleC50cwogICAgICAgIGNvbnRlbnQ6ICJpbXBvcnQgeyBzZXJ2ZSB9IGZyb20gJ2h0dHBzOi8vZGVuby5sYW5kL3N0ZEAwLjEzMS4wL2h0dHAvc2VydmVyLnRzJ1xuaW1wb3J0ICogYXMgam9zZSBmcm9tICdodHRwczovL2Rlbm8ubGFuZC94L2pvc2VAdjQuMTQuNC9pbmRleC50cydcblxuY29uc29sZS5sb2coJ21haW4gZnVuY3Rpb24gc3RhcnRlZCcpXG5cbmNvbnN0IEpXVF9TRUNSRVQgPSBEZW5vLmVudi5nZXQoJ0pXVF9TRUNSRVQnKVxuY29uc3QgVkVSSUZZX0pXVCA9IERlbm8uZW52LmdldCgnVkVSSUZZX0pXVCcpID09PSAndHJ1ZSdcblxuZnVuY3Rpb24gZ2V0QXV0aFRva2VuKHJlcTogUmVxdWVzdCkge1xuICBjb25zdCBhdXRoSGVhZGVyID0gcmVxLmhlYWRlcnMuZ2V0KCdhdXRob3JpemF0aW9uJylcbiAgaWYgKCFhdXRoSGVhZGVyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdNaXNzaW5nIGF1dGhvcml6YXRpb24gaGVhZGVyJylcbiAgfVxuICBjb25zdCBbYmVhcmVyLCB0b2tlbl0gPSBhdXRoSGVhZGVyLnNwbGl0KCcgJylcbiAgaWYgKGJlYXJlciAhPT0gJ0JlYXJlcicpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEF1dGggaGVhZGVyIGlzIG5vdCAnQmVhcmVyIHt0b2tlbn0nYClcbiAgfVxuICByZXR1cm4gdG9rZW5cbn1cblxuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5SldUKGp3dDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGNvbnN0IGVuY29kZXIgPSBuZXcgVGV4dEVuY29kZXIoKVxuICBjb25zdCBzZWNyZXRLZXkgPSBlbmNvZGVyLmVuY29kZShKV1RfU0VDUkVUKVxuICB0cnkge1xuICAgIGF3YWl0IGpvc2Uuand0VmVyaWZ5KGp3dCwgc2VjcmV0S2V5KVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICBjb25zb2xlLmVycm9yKGVycilcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuICByZXR1cm4gdHJ1ZVxufVxuXG5zZXJ2ZShhc3luYyAocmVxOiBSZXF1ZXN0KSA9PiB7XG4gIGlmIChyZXEubWV0aG9kICE9PSAnT1BUSU9OUycgJiYgVkVSSUZZX0pXVCkge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCB0b2tlbiA9IGdldEF1dGhUb2tlbihyZXEpXG4gICAgICBjb25zdCBpc1ZhbGlkSldUID0gYXdhaXQgdmVyaWZ5SldUKHRva2VuKVxuXG4gICAgICBpZiAoIWlzVmFsaWRKV1QpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeSh7IG1zZzogJ0ludmFsaWQgSldUJyB9KSwge1xuICAgICAgICAgIHN0YXR1czogNDAxLFxuICAgICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoZSlcbiAgICAgIHJldHVybiBuZXcgUmVzcG9uc2UoSlNPTi5zdHJpbmdpZnkoeyBtc2c6IGUudG9TdHJpbmcoKSB9KSwge1xuICAgICAgICBzdGF0dXM6IDQwMSxcbiAgICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHVybCA9IG5ldyBVUkwocmVxLnVybClcbiAgY29uc3QgeyBwYXRobmFtZSB9ID0gdXJsXG4gIGNvbnN0IHBhdGhfcGFydHMgPSBwYXRobmFtZS5zcGxpdCgnLycpXG4gIGNvbnN0IHNlcnZpY2VfbmFtZSA9IHBhdGhfcGFydHNbMV1cblxuICBpZiAoIXNlcnZpY2VfbmFtZSB8fCBzZXJ2aWNlX25hbWUgPT09ICcnKSB7XG4gICAgY29uc3QgZXJyb3IgPSB7IG1zZzogJ21pc3NpbmcgZnVuY3Rpb24gbmFtZSBpbiByZXF1ZXN0JyB9XG4gICAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeShlcnJvciksIHtcbiAgICAgIHN0YXR1czogNDAwLFxuICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gICAgfSlcbiAgfVxuXG4gIGNvbnN0IHNlcnZpY2VQYXRoID0gYC9ob21lL2Rlbm8vZnVuY3Rpb25zLyR7c2VydmljZV9uYW1lfWBcbiAgY29uc29sZS5lcnJvcihgc2VydmluZyB0aGUgcmVxdWVzdCB3aXRoICR7c2VydmljZVBhdGh9YClcblxuICBjb25zdCBtZW1vcnlMaW1pdE1iID0gMTUwXG4gIGNvbnN0IHdvcmtlclRpbWVvdXRNcyA9IDEgKiA2MCAqIDEwMDBcbiAgY29uc3Qgbm9Nb2R1bGVDYWNoZSA9IGZhbHNlXG4gIGNvbnN0IGltcG9ydE1hcFBhdGggPSBudWxsXG4gIGNvbnN0IGVudlZhcnNPYmogPSBEZW5vLmVudi50b09iamVjdCgpXG4gIGNvbnN0IGVudlZhcnMgPSBPYmplY3Qua2V5cyhlbnZWYXJzT2JqKS5tYXAoKGspID0+IFtrLCBlbnZWYXJzT2JqW2tdXSlcblxuICB0cnkge1xuICAgIGNvbnN0IHdvcmtlciA9IGF3YWl0IEVkZ2VSdW50aW1lLnVzZXJXb3JrZXJzLmNyZWF0ZSh7XG4gICAgICBzZXJ2aWNlUGF0aCxcbiAgICAgIG1lbW9yeUxpbWl0TWIsXG4gICAgICB3b3JrZXJUaW1lb3V0TXMsXG4gICAgICBub01vZHVsZUNhY2hlLFxuICAgICAgaW1wb3J0TWFwUGF0aCxcbiAgICAgIGVudlZhcnMsXG4gICAgfSlcbiAgICByZXR1cm4gYXdhaXQgd29ya2VyLmZldGNoKHJlcSlcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IGVycm9yID0geyBtc2c6IGUudG9TdHJpbmcoKSB9XG4gICAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeShlcnJvciksIHtcbiAgICAgIHN0YXR1czogNTAwLFxuICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gICAgfSlcbiAgfVxufSkiCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZnVuY3Rpb25zL2hlbGxvL2luZGV4LnRzCiAgICAgICAgdGFyZ2V0OiAvaG9tZS9kZW5vL2Z1bmN0aW9ucy9oZWxsby9pbmRleC50cwogICAgICAgIGNvbnRlbnQ6ICIvLyBGb2xsb3cgdGhpcyBzZXR1cCBndWlkZSB0byBpbnRlZ3JhdGUgdGhlIERlbm8gbGFuZ3VhZ2Ugc2VydmVyIHdpdGggeW91ciBlZGl0b3I6XG4vLyBodHRwczovL2Rlbm8ubGFuZC9tYW51YWwvZ2V0dGluZ19zdGFydGVkL3NldHVwX3lvdXJfZW52aXJvbm1lbnRcbi8vIFRoaXMgZW5hYmxlcyBhdXRvY29tcGxldGUsIGdvIHRvIGRlZmluaXRpb24sIGV0Yy5cblxuaW1wb3J0IHsgc2VydmUgfSBmcm9tIFwiaHR0cHM6Ly9kZW5vLmxhbmQvc3RkQDAuMTc3LjEvaHR0cC9zZXJ2ZXIudHNcIlxuXG5zZXJ2ZShhc3luYyAoKSA9PiB7XG4gIHJldHVybiBuZXcgUmVzcG9uc2UoXG4gICAgYFwiSGVsbG8gZnJvbSBFZGdlIEZ1bmN0aW9ucyFcImAsXG4gICAgeyBoZWFkZXJzOiB7IFwiQ29udGVudC1UeXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH0gfSxcbiAgKVxufSlcblxuLy8gVG8gaW52b2tlOlxuLy8gY3VybCAnaHR0cDovL2xvY2FsaG9zdDo8S09OR19IVFRQX1BPUlQ+L2Z1bmN0aW9ucy92MS9oZWxsbycgXFxcbi8vICAgLS1oZWFkZXIgJ0F1dGhvcml6YXRpb246IEJlYXJlciA8YW5vbi9zZXJ2aWNlX3JvbGUgQVBJIGtleT4nXG4iCiAgICBjb21tYW5kOgogICAgICAtIHN0YXJ0CiAgICAgIC0gJy0tbWFpbi1zZXJ2aWNlJwogICAgICAtIC9ob21lL2Rlbm8vZnVuY3Rpb25zL21haW4K",
+ "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL3N1cGFiYXNlLmlvCiMgc2xvZ2FuOiBUaGUgb3BlbiBzb3VyY2UgRmlyZWJhc2UgYWx0ZXJuYXRpdmUuCiMgdGFnczogZmlyZWJhc2UsIGFsdGVybmF0aXZlLCBvcGVuLXNvdXJjZQojIG1pbnZlcnNpb246IDQuMC4wLWJldGEuMjI4CiMgbG9nbzogc3Zncy9zdXBhYmFzZS5zdmcKCnNlcnZpY2VzOgogIHN1cGFiYXNlLWtvbmc6CiAgICBpbWFnZToga29uZzoyLjguMQogICAgIyBodHRwczovL3VuaXguc3RhY2tleGNoYW5nZS5jb20vYS8yOTQ4MzcKICAgIGVudHJ5cG9pbnQ6IGJhc2ggLWMgJ2V2YWwgImVjaG8gXCIkJChjYXQgfi90ZW1wLnltbClcIiIgPiB+L2tvbmcueW1sICYmIC9kb2NrZXItZW50cnlwb2ludC5zaCBrb25nIGRvY2tlci1zdGFydCcKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NVUEFCQVNFCiAgICAgIC0gSldUX1NFUkNFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfQogICAgICAtIEtPTkdfREFUQUJBU0U9b2ZmCiAgICAgIC0gS09OR19ERUNMQVJBVElWRV9DT05GSUc9L2hvbWUva29uZy9rb25nLnltbAogICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9zdXBhYmFzZS9jbGkvaXNzdWVzLzE0CiAgICAgIC0gS09OR19ETlNfT1JERVI9TEFTVCxBLENOQU1FCiAgICAgIC0gS09OR19QTFVHSU5TPXJlcXVlc3QtdHJhbnNmb3JtZXIsY29ycyxrZXktYXV0aCxhY2wsYmFzaWMtYXV0aAogICAgICAtIEtPTkdfTkdJTlhfUFJPWFlfUFJPWFlfQlVGRkVSX1NJWkU9MTYwawogICAgICAtIEtPTkdfTkdJTlhfUFJPWFlfUFJPWFlfQlVGRkVSUz02NCAxNjBrCiAgICAgIC0gU1VQQUJBU0VfQU5PTl9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFQU5PTl9LRVl9CiAgICAgIC0gU1VQQUJBU0VfU0VSVklDRV9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFU0VSVklDRV9LRVl9CiAgICAgIC0gREFTSEJPQVJEX1VTRVJOQU1FPSR7U0VSVklDRV9VU0VSX0FETUlOfQogICAgICAtIERBU0hCT0FSRF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQURNSU59CiAgICB2b2x1bWVzOgogICAgICAjIGh0dHBzOi8vZ2l0aHViLmNvbS9zdXBhYmFzZS9zdXBhYmFzZS9pc3N1ZXMvMTI2NjEKICAgICAgLSB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvYXBpL2tvbmcueW1sCiAgICAgICAgdGFyZ2V0OiAvaG9tZS9rb25nL3RlbXAueW1sCiAgICAgICAgY29udGVudDogfAogICAgICAgICAgX2Zvcm1hdF92ZXJzaW9uOiAnMi4xJwogICAgICAgICAgX3RyYW5zZm9ybTogdHJ1ZQoKICAgICAgICAgICMjIwogICAgICAgICAgIyMjIENvbnN1bWVycyAvIFVzZXJzCiAgICAgICAgICAjIyMKICAgICAgICAgIGNvbnN1bWVyczoKICAgICAgICAgICAgLSB1c2VybmFtZTogREFTSEJPQVJECiAgICAgICAgICAgIC0gdXNlcm5hbWU6IGFub24KICAgICAgICAgICAgICBrZXlhdXRoX2NyZWRlbnRpYWxzOgogICAgICAgICAgICAgICAgLSBrZXk6ICRTVVBBQkFTRV9BTk9OX0tFWQogICAgICAgICAgICAtIHVzZXJuYW1lOiBzZXJ2aWNlX3JvbGUKICAgICAgICAgICAgICBrZXlhdXRoX2NyZWRlbnRpYWxzOgogICAgICAgICAgICAgICAgLSBrZXk6ICRTVVBBQkFTRV9TRVJWSUNFX0tFWQoKICAgICAgICAgICMjIwogICAgICAgICAgIyMjIEFjY2VzcyBDb250cm9sIExpc3QKICAgICAgICAgICMjIwogICAgICAgICAgYWNsczoKICAgICAgICAgICAgLSBjb25zdW1lcjogYW5vbgogICAgICAgICAgICAgIGdyb3VwOiBhbm9uCiAgICAgICAgICAgIC0gY29uc3VtZXI6IHNlcnZpY2Vfcm9sZQogICAgICAgICAgICAgIGdyb3VwOiBhZG1pbgoKICAgICAgICAgICMjIwogICAgICAgICAgIyMjIERhc2hib2FyZCBjcmVkZW50aWFscwogICAgICAgICAgIyMjCiAgICAgICAgICBiYXNpY2F1dGhfY3JlZGVudGlhbHM6CiAgICAgICAgICAtIGNvbnN1bWVyOiBEQVNIQk9BUkQKICAgICAgICAgICAgdXNlcm5hbWU6ICREQVNIQk9BUkRfVVNFUk5BTUUKICAgICAgICAgICAgcGFzc3dvcmQ6ICREQVNIQk9BUkRfUEFTU1dPUkQKCgogICAgICAgICAgIyMjCiAgICAgICAgICAjIyMgQVBJIFJvdXRlcwogICAgICAgICAgIyMjCiAgICAgICAgICBzZXJ2aWNlczoKCiAgICAgICAgICAgICMjIE9wZW4gQXV0aCByb3V0ZXMKICAgICAgICAgICAgLSBuYW1lOiBhdXRoLXYxLW9wZW4KICAgICAgICAgICAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hdXRoOjk5OTkvdmVyaWZ5CiAgICAgICAgICAgICAgcm91dGVzOgogICAgICAgICAgICAgICAgLSBuYW1lOiBhdXRoLXYxLW9wZW4KICAgICAgICAgICAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZQogICAgICAgICAgICAgICAgICBwYXRoczoKICAgICAgICAgICAgICAgICAgICAtIC9hdXRoL3YxL3ZlcmlmeQogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKICAgICAgICAgICAgLSBuYW1lOiBhdXRoLXYxLW9wZW4tY2FsbGJhY2sKICAgICAgICAgICAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1hdXRoOjk5OTkvY2FsbGJhY2sKICAgICAgICAgICAgICByb3V0ZXM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1jYWxsYmFjawogICAgICAgICAgICAgICAgICBzdHJpcF9wYXRoOiB0cnVlCiAgICAgICAgICAgICAgICAgIHBhdGhzOgogICAgICAgICAgICAgICAgICAgIC0gL2F1dGgvdjEvY2FsbGJhY2sKICAgICAgICAgICAgICBwbHVnaW5zOgogICAgICAgICAgICAgICAgLSBuYW1lOiBjb3JzCiAgICAgICAgICAgIC0gbmFtZTogYXV0aC12MS1vcGVuLWF1dGhvcml6ZQogICAgICAgICAgICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWF1dGg6OTk5OS9hdXRob3JpemUKICAgICAgICAgICAgICByb3V0ZXM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGF1dGgtdjEtb3Blbi1hdXRob3JpemUKICAgICAgICAgICAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZQogICAgICAgICAgICAgICAgICBwYXRoczoKICAgICAgICAgICAgICAgICAgICAtIC9hdXRoL3YxL2F1dGhvcml6ZQogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKCiAgICAgICAgICAgICMjIFNlY3VyZSBBdXRoIHJvdXRlcwogICAgICAgICAgICAtIG5hbWU6IGF1dGgtdjEKICAgICAgICAgICAgICBfY29tbWVudDogJ0dvVHJ1ZTogL2F1dGgvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2UtYXV0aDo5OTk5LyonCiAgICAgICAgICAgICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtYXV0aDo5OTk5LwogICAgICAgICAgICAgIHJvdXRlczoKICAgICAgICAgICAgICAgIC0gbmFtZTogYXV0aC12MS1hbGwKICAgICAgICAgICAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZQogICAgICAgICAgICAgICAgICBwYXRoczoKICAgICAgICAgICAgICAgICAgICAtIC9hdXRoL3YxLwogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKICAgICAgICAgICAgICAgIC0gbmFtZToga2V5LWF1dGgKICAgICAgICAgICAgICAgICAgY29uZmlnOgogICAgICAgICAgICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IGZhbHNlCiAgICAgICAgICAgICAgICAtIG5hbWU6IGFjbAogICAgICAgICAgICAgICAgICBjb25maWc6CiAgICAgICAgICAgICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgYWxsb3c6CiAgICAgICAgICAgICAgICAgICAgICAtIGFkbWluCiAgICAgICAgICAgICAgICAgICAgICAtIGFub24KCiAgICAgICAgICAgICMjIFNlY3VyZSBSRVNUIHJvdXRlcwogICAgICAgICAgICAtIG5hbWU6IHJlc3QtdjEKICAgICAgICAgICAgICBfY29tbWVudDogJ1Bvc3RnUkVTVDogL3Jlc3QvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2UtcmVzdDozMDAwLyonCiAgICAgICAgICAgICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtcmVzdDozMDAwLwogICAgICAgICAgICAgIHJvdXRlczoKICAgICAgICAgICAgICAgIC0gbmFtZTogcmVzdC12MS1hbGwKICAgICAgICAgICAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZQogICAgICAgICAgICAgICAgICBwYXRoczoKICAgICAgICAgICAgICAgICAgICAtIC9yZXN0L3YxLwogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKICAgICAgICAgICAgICAgIC0gbmFtZToga2V5LWF1dGgKICAgICAgICAgICAgICAgICAgY29uZmlnOgogICAgICAgICAgICAgICAgICAgIGhpZGVfY3JlZGVudGlhbHM6IHRydWUKICAgICAgICAgICAgICAgIC0gbmFtZTogYWNsCiAgICAgICAgICAgICAgICAgIGNvbmZpZzoKICAgICAgICAgICAgICAgICAgICBoaWRlX2dyb3Vwc19oZWFkZXI6IHRydWUKICAgICAgICAgICAgICAgICAgICBhbGxvdzoKICAgICAgICAgICAgICAgICAgICAgIC0gYWRtaW4KICAgICAgICAgICAgICAgICAgICAgIC0gYW5vbgoKICAgICAgICAgICAgIyMgU2VjdXJlIEdyYXBoUUwgcm91dGVzCiAgICAgICAgICAgIC0gbmFtZTogZ3JhcGhxbC12MQogICAgICAgICAgICAgIF9jb21tZW50OiAnUG9zdGdSRVNUOiAvZ3JhcGhxbC92MS8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1yZXN0OjMwMDAvcnBjL2dyYXBocWwnCiAgICAgICAgICAgICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtcmVzdDozMDAwL3JwYy9ncmFwaHFsCiAgICAgICAgICAgICAgcm91dGVzOgogICAgICAgICAgICAgICAgLSBuYW1lOiBncmFwaHFsLXYxLWFsbAogICAgICAgICAgICAgICAgICBzdHJpcF9wYXRoOiB0cnVlCiAgICAgICAgICAgICAgICAgIHBhdGhzOgogICAgICAgICAgICAgICAgICAgIC0gL2dyYXBocWwvdjEKICAgICAgICAgICAgICBwbHVnaW5zOgogICAgICAgICAgICAgICAgLSBuYW1lOiBjb3JzCiAgICAgICAgICAgICAgICAtIG5hbWU6IGtleS1hdXRoCiAgICAgICAgICAgICAgICAgIGNvbmZpZzoKICAgICAgICAgICAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiB0cnVlCiAgICAgICAgICAgICAgICAtIG5hbWU6IHJlcXVlc3QtdHJhbnNmb3JtZXIKICAgICAgICAgICAgICAgICAgY29uZmlnOgogICAgICAgICAgICAgICAgICAgIGFkZDoKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlcnM6CiAgICAgICAgICAgICAgICAgICAgICAgIC0gQ29udGVudC1Qcm9maWxlOmdyYXBocWxfcHVibGljCiAgICAgICAgICAgICAgICAtIG5hbWU6IGFjbAogICAgICAgICAgICAgICAgICBjb25maWc6CiAgICAgICAgICAgICAgICAgICAgaGlkZV9ncm91cHNfaGVhZGVyOiB0cnVlCiAgICAgICAgICAgICAgICAgICAgYWxsb3c6CiAgICAgICAgICAgICAgICAgICAgICAtIGFkbWluCiAgICAgICAgICAgICAgICAgICAgICAtIGFub24KCiAgICAgICAgICAgICMjIFNlY3VyZSBSZWFsdGltZSByb3V0ZXMKICAgICAgICAgICAgLSBuYW1lOiByZWFsdGltZS12MQogICAgICAgICAgICAgIF9jb21tZW50OiAnUmVhbHRpbWU6IC9yZWFsdGltZS92MS8qIC0+IHdzOi8vcmVhbHRpbWU6NDAwMC9zb2NrZXQvKicKICAgICAgICAgICAgICB1cmw6IGh0dHA6Ly9yZWFsdGltZS1kZXYuc3VwYWJhc2UtcmVhbHRpbWU6NDAwMC9zb2NrZXQvCiAgICAgICAgICAgICAgcm91dGVzOgogICAgICAgICAgICAgICAgLSBuYW1lOiByZWFsdGltZS12MS1hbGwKICAgICAgICAgICAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZQogICAgICAgICAgICAgICAgICBwYXRoczoKICAgICAgICAgICAgICAgICAgICAtIC9yZWFsdGltZS92MS8KICAgICAgICAgICAgICBwbHVnaW5zOgogICAgICAgICAgICAgICAgLSBuYW1lOiBjb3JzCiAgICAgICAgICAgICAgICAtIG5hbWU6IGtleS1hdXRoCiAgICAgICAgICAgICAgICAgIGNvbmZpZzoKICAgICAgICAgICAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiBmYWxzZQogICAgICAgICAgICAgICAgLSBuYW1lOiBhY2wKICAgICAgICAgICAgICAgICAgY29uZmlnOgogICAgICAgICAgICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZQogICAgICAgICAgICAgICAgICAgIGFsbG93OgogICAgICAgICAgICAgICAgICAgICAgLSBhZG1pbgogICAgICAgICAgICAgICAgICAgICAgLSBhbm9uCgogICAgICAgICAgICAjIyBTdG9yYWdlIHJvdXRlczogdGhlIHN0b3JhZ2Ugc2VydmVyIG1hbmFnZXMgaXRzIG93biBhdXRoCiAgICAgICAgICAgIC0gbmFtZTogc3RvcmFnZS12MQogICAgICAgICAgICAgIF9jb21tZW50OiAnU3RvcmFnZTogL3N0b3JhZ2UvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2Utc3RvcmFnZTo1MDAwLyonCiAgICAgICAgICAgICAgdXJsOiBodHRwOi8vc3VwYWJhc2Utc3RvcmFnZTo1MDAwLwogICAgICAgICAgICAgIHJvdXRlczoKICAgICAgICAgICAgICAgIC0gbmFtZTogc3RvcmFnZS12MS1hbGwKICAgICAgICAgICAgICAgICAgc3RyaXBfcGF0aDogdHJ1ZQogICAgICAgICAgICAgICAgICBwYXRoczoKICAgICAgICAgICAgICAgICAgICAtIC9zdG9yYWdlL3YxLwogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKCiAgICAgICAgICAgICMjIEVkZ2UgRnVuY3Rpb25zIHJvdXRlcwogICAgICAgICAgICAtIG5hbWU6IGZ1bmN0aW9ucy12MQogICAgICAgICAgICAgIF9jb21tZW50OiAnRWRnZSBGdW5jdGlvbnM6IC9mdW5jdGlvbnMvdjEvKiAtPiBodHRwOi8vc3VwYWJhc2UtZWRnZS1mdW5jdGlvbnM6OTAwMC8qJwogICAgICAgICAgICAgIHVybDogaHR0cDovL3N1cGFiYXNlLWVkZ2UtZnVuY3Rpb25zOjkwMDAvCiAgICAgICAgICAgICAgcm91dGVzOgogICAgICAgICAgICAgICAgLSBuYW1lOiBmdW5jdGlvbnMtdjEtYWxsCiAgICAgICAgICAgICAgICAgIHN0cmlwX3BhdGg6IHRydWUKICAgICAgICAgICAgICAgICAgcGF0aHM6CiAgICAgICAgICAgICAgICAgICAgLSAvZnVuY3Rpb25zL3YxLwogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKCiAgICAgICAgICAgICMjIEFuYWx5dGljcyByb3V0ZXMKICAgICAgICAgICAgLSBuYW1lOiBhbmFseXRpY3MtdjEKICAgICAgICAgICAgICBfY29tbWVudDogJ0FuYWx5dGljczogL2FuYWx5dGljcy92MS8qIC0+IGh0dHA6Ly9sb2dmbGFyZTo0MDAwLyonCiAgICAgICAgICAgICAgdXJsOiBodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvCiAgICAgICAgICAgICAgcm91dGVzOgogICAgICAgICAgICAgICAgLSBuYW1lOiBhbmFseXRpY3MtdjEtYWxsCiAgICAgICAgICAgICAgICAgIHN0cmlwX3BhdGg6IHRydWUKICAgICAgICAgICAgICAgICAgcGF0aHM6CiAgICAgICAgICAgICAgICAgICAgLSAvYW5hbHl0aWNzL3YxLwoKICAgICAgICAgICAgIyMgU2VjdXJlIERhdGFiYXNlIHJvdXRlcwogICAgICAgICAgICAtIG5hbWU6IG1ldGEKICAgICAgICAgICAgICBfY29tbWVudDogJ3BnLW1ldGE6IC9wZy8qIC0+IGh0dHA6Ly9zdXBhYmFzZS1tZXRhOjgwODAvKicKICAgICAgICAgICAgICB1cmw6IGh0dHA6Ly9zdXBhYmFzZS1tZXRhOjgwODAvCiAgICAgICAgICAgICAgcm91dGVzOgogICAgICAgICAgICAgICAgLSBuYW1lOiBtZXRhLWFsbAogICAgICAgICAgICAgICAgICBzdHJpcF9wYXRoOiB0cnVlCiAgICAgICAgICAgICAgICAgIHBhdGhzOgogICAgICAgICAgICAgICAgICAgIC0gL3BnLwogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGtleS1hdXRoCiAgICAgICAgICAgICAgICAgIGNvbmZpZzoKICAgICAgICAgICAgICAgICAgICBoaWRlX2NyZWRlbnRpYWxzOiBmYWxzZQogICAgICAgICAgICAgICAgLSBuYW1lOiBhY2wKICAgICAgICAgICAgICAgICAgY29uZmlnOgogICAgICAgICAgICAgICAgICAgIGhpZGVfZ3JvdXBzX2hlYWRlcjogdHJ1ZQogICAgICAgICAgICAgICAgICAgIGFsbG93OgogICAgICAgICAgICAgICAgICAgICAgLSBhZG1pbgoKICAgICAgICAgICAgIyMgUHJvdGVjdGVkIERhc2hib2FyZCAtIGNhdGNoIGFsbCByZW1haW5pbmcgcm91dGVzCiAgICAgICAgICAgIC0gbmFtZTogZGFzaGJvYXJkCiAgICAgICAgICAgICAgX2NvbW1lbnQ6ICdTdHVkaW86IC8qIC0+IGh0dHA6Ly9zdHVkaW86MzAwMC8qJwogICAgICAgICAgICAgIHVybDogaHR0cDovL3N1cGFiYXNlLXN0dWRpbzozMDAwLwogICAgICAgICAgICAgIHJvdXRlczoKICAgICAgICAgICAgICAgIC0gbmFtZTogZGFzaGJvYXJkLWFsbAogICAgICAgICAgICAgICAgICBzdHJpcF9wYXRoOiB0cnVlCiAgICAgICAgICAgICAgICAgIHBhdGhzOgogICAgICAgICAgICAgICAgICAgIC0gLwogICAgICAgICAgICAgIHBsdWdpbnM6CiAgICAgICAgICAgICAgICAtIG5hbWU6IGNvcnMKICAgICAgICAgICAgICAgIC0gbmFtZTogYmFzaWMtYXV0aAogICAgICAgICAgICAgICAgICBjb25maWc6CiAgICAgICAgICAgICAgICAgICAgaGlkZV9jcmVkZW50aWFsczogdHJ1ZQogIHN1cGFiYXNlLXN0dWRpbzoKICAgIGltYWdlOiBzdXBhYmFzZS9zdHVkaW86MjAyNDAzMDEtMDk0MmJmZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgWwogICAgICAgICAgIkNNRCIsCiAgICAgICAgICAibm9kZSIsCiAgICAgICAgICAiLWUiLAogICAgICAgICAgInJlcXVpcmUoJ2h0dHAnKS5nZXQoJ2h0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hcGkvcHJvZmlsZScsIChyKSA9PiB7aWYgKHIuc3RhdHVzQ29kZSAhPT0gMjAwKSB0aHJvdyBuZXcgRXJyb3Ioci5zdGF0dXNDb2RlKX0pIiwKICAgICAgICBdCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIEhPU1ROQU1FPTAuMC4wLjAKICAgICAgLSBTVFVESU9fUEdfTUVUQV9VUkw9aHR0cDovL3N1cGFiYXNlLW1ldGE6ODA4MAogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30KCiAgICAgIC0gREVGQVVMVF9PUkdBTklaQVRJT05fTkFNRT0ke1NUVURJT19ERUZBVUxUX09SR0FOSVpBVElPTjotRGVmYXVsdCBPcmdhbml6YXRpb259CiAgICAgIC0gREVGQVVMVF9QUk9KRUNUX05BTUU9JHtTVFVESU9fREVGQVVMVF9QUk9KRUNUOi1EZWZhdWx0IFByb2plY3R9CgogICAgICAtIFNVUEFCQVNFX1VSTD1odHRwOi8vc3VwYWJhc2Uta29uZzo4MDAwCiAgICAgIC0gU1VQQUJBU0VfUFVCTElDX1VSTD0ke1NFUlZJQ0VfRlFETl9TVVBBQkFTRX0KICAgICAgLSBTVVBBQkFTRV9BTk9OX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VBTk9OX0tFWX0KICAgICAgLSBTVVBBQkFTRV9TRVJWSUNFX0tFWT0ke1NFUlZJQ0VfU1VQQUJBU0VTRVJWSUNFX0tFWX0KCiAgICAgIC0gTE9HRkxBUkVfQVBJX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTE9HRkxBUkV9CiAgICAgIC0gTE9HRkxBUkVfVVJMPWh0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMAogICAgICAtIE5FWFRfUFVCTElDX0VOQUJMRV9MT0dTPXRydWUKICAgICAgIyBDb21tZW50IHRvIHVzZSBCaWcgUXVlcnkgYmFja2VuZCBmb3IgYW5hbHl0aWNzCiAgICAgIC0gTkVYVF9BTkFMWVRJQ1NfQkFDS0VORF9QUk9WSURFUj1wb3N0Z3JlcwogICAgICAjIFVuY29tbWVudCB0byB1c2UgQmlnIFF1ZXJ5IGJhY2tlbmQgZm9yIGFuYWx5dGljcwogICAgICAjIE5FWFRfQU5BTFlUSUNTX0JBQ0tFTkRfUFJPVklERVI9YmlncXVlcnkKICBzdXBhYmFzZS1kYjoKICAgIGltYWdlOiBzdXBhYmFzZS9wb3N0Z3JlczoxNS4xLjAuMTQ3CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogcGdfaXNyZWFkeSAtVSBwb3N0Z3JlcyAtaCBsb2NhbGhvc3QKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS12ZWN0b3I6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGNvbW1hbmQ6CiAgICAgIC0gcG9zdGdyZXMKICAgICAgLSAtYwogICAgICAtIGNvbmZpZ19maWxlPS9ldGMvcG9zdGdyZXNxbC9wb3N0Z3Jlc3FsLmNvbmYKICAgICAgLSAtYwogICAgICAtIGxvZ19taW5fbWVzc2FnZXM9ZmF0YWwKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19IT1NUPS92YXIvcnVuL3Bvc3RncmVzcWwKICAgICAgLSBQR1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfQogICAgICAtIFBPU1RHUkVTX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfQogICAgICAtIFBHUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30KICAgICAgLSBQR0RBVEFCQVNFPSR7UE9TVEdSRVNfREI6LXN1cGFiYXNlfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXN1cGFiYXNlfQogICAgICAtIEpXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0KICAgICAgLSBKV1RfRVhQPSR7SldUX0VYUElSWTotMzYwMH0KICAgIHZvbHVtZXM6CiAgICAgIC0gc3VwYWJhc2UtZGItZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEKICAgICAgLSB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZGIvcmVhbHRpbWUuc3FsCiAgICAgICAgdGFyZ2V0OiAvZG9ja2VyLWVudHJ5cG9pbnQtaW5pdGRiLmQvbWlncmF0aW9ucy85OS1yZWFsdGltZS5zcWwKICAgICAgICBjb250ZW50OiB8CiAgICAgICAgICBcc2V0IHBndXNlciBgZWNobyAic3VwYWJhc2VfYWRtaW4iYAoKICAgICAgICAgIGNyZWF0ZSBzY2hlbWEgaWYgbm90IGV4aXN0cyBfcmVhbHRpbWU7CiAgICAgICAgICBhbHRlciBzY2hlbWEgX3JlYWx0aW1lIG93bmVyIHRvIDpwZ3VzZXI7CiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2RiL3dlYmhvb2tzLnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL2luaXQtc2NyaXB0cy85OC13ZWJob29rcy5zcWwKICAgICAgICBjb250ZW50OiB8CiAgICAgICAgICBCRUdJTjsKICAgICAgICAgIC0tIENyZWF0ZSBwZ19uZXQgZXh0ZW5zaW9uCiAgICAgICAgICBDUkVBVEUgRVhURU5TSU9OIElGIE5PVCBFWElTVFMgcGdfbmV0IFNDSEVNQSBleHRlbnNpb25zOwogICAgICAgICAgLS0gQ3JlYXRlIHN1cGFiYXNlX2Z1bmN0aW9ucyBzY2hlbWEKICAgICAgICAgIENSRUFURSBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIEFVVEhPUklaQVRJT04gc3VwYWJhc2VfYWRtaW47CiAgICAgICAgICBHUkFOVCBVU0FHRSBPTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7CiAgICAgICAgICBBTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gVEFCTEVTIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7CiAgICAgICAgICBBTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gRlVOQ1RJT05TIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7CiAgICAgICAgICBBTFRFUiBERUZBVUxUIFBSSVZJTEVHRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBHUkFOVCBBTEwgT04gU0VRVUVOQ0VTIFRPIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7CiAgICAgICAgICAtLSBzdXBhYmFzZV9mdW5jdGlvbnMubWlncmF0aW9ucyBkZWZpbml0aW9uCiAgICAgICAgICBDUkVBVEUgVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLm1pZ3JhdGlvbnMgKAogICAgICAgICAgICB2ZXJzaW9uIHRleHQgUFJJTUFSWSBLRVksCiAgICAgICAgICAgIGluc2VydGVkX2F0IHRpbWVzdGFtcHR6IE5PVCBOVUxMIERFRkFVTFQgTk9XKCkKICAgICAgICAgICk7CiAgICAgICAgICAtLSBJbml0aWFsIHN1cGFiYXNlX2Z1bmN0aW9ucyBtaWdyYXRpb24KICAgICAgICAgIElOU0VSVCBJTlRPIHN1cGFiYXNlX2Z1bmN0aW9ucy5taWdyYXRpb25zICh2ZXJzaW9uKSBWQUxVRVMgKCdpbml0aWFsJyk7CiAgICAgICAgICAtLSBzdXBhYmFzZV9mdW5jdGlvbnMuaG9va3MgZGVmaW5pdGlvbgogICAgICAgICAgQ1JFQVRFIFRBQkxFIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyAoCiAgICAgICAgICAgIGlkIGJpZ3NlcmlhbCBQUklNQVJZIEtFWSwKICAgICAgICAgICAgaG9va190YWJsZV9pZCBpbnRlZ2VyIE5PVCBOVUxMLAogICAgICAgICAgICBob29rX25hbWUgdGV4dCBOT1QgTlVMTCwKICAgICAgICAgICAgY3JlYXRlZF9hdCB0aW1lc3RhbXB0eiBOT1QgTlVMTCBERUZBVUxUIE5PVygpLAogICAgICAgICAgICByZXF1ZXN0X2lkIGJpZ2ludAogICAgICAgICAgKTsKICAgICAgICAgIENSRUFURSBJTkRFWCBzdXBhYmFzZV9mdW5jdGlvbnNfaG9va3NfcmVxdWVzdF9pZF9pZHggT04gc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIFVTSU5HIGJ0cmVlIChyZXF1ZXN0X2lkKTsKICAgICAgICAgIENSRUFURSBJTkRFWCBzdXBhYmFzZV9mdW5jdGlvbnNfaG9va3NfaF90YWJsZV9pZF9oX25hbWVfaWR4IE9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcyBVU0lORyBidHJlZSAoaG9va190YWJsZV9pZCwgaG9va19uYW1lKTsKICAgICAgICAgIENPTU1FTlQgT04gVEFCTEUgc3VwYWJhc2VfZnVuY3Rpb25zLmhvb2tzIElTICdTdXBhYmFzZSBGdW5jdGlvbnMgSG9va3M6IEF1ZGl0IHRyYWlsIGZvciB0cmlnZ2VyZWQgaG9va3MuJzsKICAgICAgICAgIENSRUFURSBGVU5DVElPTiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KCkKICAgICAgICAgICAgUkVUVVJOUyB0cmlnZ2VyCiAgICAgICAgICAgIExBTkdVQUdFIHBscGdzcWwKICAgICAgICAgICAgQVMgJGZ1bmN0aW9uJAogICAgICAgICAgICBERUNMQVJFCiAgICAgICAgICAgICAgcmVxdWVzdF9pZCBiaWdpbnQ7CiAgICAgICAgICAgICAgcGF5bG9hZCBqc29uYjsKICAgICAgICAgICAgICB1cmwgdGV4dCA6PSBUR19BUkdWWzBdOjp0ZXh0OwogICAgICAgICAgICAgIG1ldGhvZCB0ZXh0IDo9IFRHX0FSR1ZbMV06OnRleHQ7CiAgICAgICAgICAgICAgaGVhZGVycyBqc29uYiBERUZBVUxUICd7fSc6Ompzb25iOwogICAgICAgICAgICAgIHBhcmFtcyBqc29uYiBERUZBVUxUICd7fSc6Ompzb25iOwogICAgICAgICAgICAgIHRpbWVvdXRfbXMgaW50ZWdlciBERUZBVUxUIDEwMDA7CiAgICAgICAgICAgIEJFR0lOCiAgICAgICAgICAgICAgSUYgdXJsIElTIE5VTEwgT1IgdXJsID0gJ251bGwnIFRIRU4KICAgICAgICAgICAgICAgIFJBSVNFIEVYQ0VQVElPTiAndXJsIGFyZ3VtZW50IGlzIG1pc3NpbmcnOwogICAgICAgICAgICAgIEVORCBJRjsKCiAgICAgICAgICAgICAgSUYgbWV0aG9kIElTIE5VTEwgT1IgbWV0aG9kID0gJ251bGwnIFRIRU4KICAgICAgICAgICAgICAgIFJBSVNFIEVYQ0VQVElPTiAnbWV0aG9kIGFyZ3VtZW50IGlzIG1pc3NpbmcnOwogICAgICAgICAgICAgIEVORCBJRjsKCiAgICAgICAgICAgICAgSUYgVEdfQVJHVlsyXSBJUyBOVUxMIE9SIFRHX0FSR1ZbMl0gPSAnbnVsbCcgVEhFTgogICAgICAgICAgICAgICAgaGVhZGVycyA9ICd7IkNvbnRlbnQtVHlwZSI6ICJhcHBsaWNhdGlvbi9qc29uIn0nOjpqc29uYjsKICAgICAgICAgICAgICBFTFNFCiAgICAgICAgICAgICAgICBoZWFkZXJzID0gVEdfQVJHVlsyXTo6anNvbmI7CiAgICAgICAgICAgICAgRU5EIElGOwoKICAgICAgICAgICAgICBJRiBUR19BUkdWWzNdIElTIE5VTEwgT1IgVEdfQVJHVlszXSA9ICdudWxsJyBUSEVOCiAgICAgICAgICAgICAgICBwYXJhbXMgPSAne30nOjpqc29uYjsKICAgICAgICAgICAgICBFTFNFCiAgICAgICAgICAgICAgICBwYXJhbXMgPSBUR19BUkdWWzNdOjpqc29uYjsKICAgICAgICAgICAgICBFTkQgSUY7CgogICAgICAgICAgICAgIElGIFRHX0FSR1ZbNF0gSVMgTlVMTCBPUiBUR19BUkdWWzRdID0gJ251bGwnIFRIRU4KICAgICAgICAgICAgICAgIHRpbWVvdXRfbXMgPSAxMDAwOwogICAgICAgICAgICAgIEVMU0UKICAgICAgICAgICAgICAgIHRpbWVvdXRfbXMgPSBUR19BUkdWWzRdOjppbnRlZ2VyOwogICAgICAgICAgICAgIEVORCBJRjsKCiAgICAgICAgICAgICAgQ0FTRQogICAgICAgICAgICAgICAgV0hFTiBtZXRob2QgPSAnR0VUJyBUSEVOCiAgICAgICAgICAgICAgICAgIFNFTEVDVCBodHRwX2dldCBJTlRPIHJlcXVlc3RfaWQgRlJPTSBuZXQuaHR0cF9nZXQoCiAgICAgICAgICAgICAgICAgICAgdXJsLAogICAgICAgICAgICAgICAgICAgIHBhcmFtcywKICAgICAgICAgICAgICAgICAgICBoZWFkZXJzLAogICAgICAgICAgICAgICAgICAgIHRpbWVvdXRfbXMKICAgICAgICAgICAgICAgICAgKTsKICAgICAgICAgICAgICAgIFdIRU4gbWV0aG9kID0gJ1BPU1QnIFRIRU4KICAgICAgICAgICAgICAgICAgcGF5bG9hZCA9IGpzb25iX2J1aWxkX29iamVjdCgKICAgICAgICAgICAgICAgICAgICAnb2xkX3JlY29yZCcsIE9MRCwKICAgICAgICAgICAgICAgICAgICAncmVjb3JkJywgTkVXLAogICAgICAgICAgICAgICAgICAgICd0eXBlJywgVEdfT1AsCiAgICAgICAgICAgICAgICAgICAgJ3RhYmxlJywgVEdfVEFCTEVfTkFNRSwKICAgICAgICAgICAgICAgICAgICAnc2NoZW1hJywgVEdfVEFCTEVfU0NIRU1BCiAgICAgICAgICAgICAgICAgICk7CgogICAgICAgICAgICAgICAgICBTRUxFQ1QgaHR0cF9wb3N0IElOVE8gcmVxdWVzdF9pZCBGUk9NIG5ldC5odHRwX3Bvc3QoCiAgICAgICAgICAgICAgICAgICAgdXJsLAogICAgICAgICAgICAgICAgICAgIHBheWxvYWQsCiAgICAgICAgICAgICAgICAgICAgcGFyYW1zLAogICAgICAgICAgICAgICAgICAgIGhlYWRlcnMsCiAgICAgICAgICAgICAgICAgICAgdGltZW91dF9tcwogICAgICAgICAgICAgICAgICApOwogICAgICAgICAgICAgICAgRUxTRQogICAgICAgICAgICAgICAgICBSQUlTRSBFWENFUFRJT04gJ21ldGhvZCBhcmd1bWVudCAlIGlzIGludmFsaWQnLCBtZXRob2Q7CiAgICAgICAgICAgICAgRU5EIENBU0U7CgogICAgICAgICAgICAgIElOU0VSVCBJTlRPIHN1cGFiYXNlX2Z1bmN0aW9ucy5ob29rcwogICAgICAgICAgICAgICAgKGhvb2tfdGFibGVfaWQsIGhvb2tfbmFtZSwgcmVxdWVzdF9pZCkKICAgICAgICAgICAgICBWQUxVRVMKICAgICAgICAgICAgICAgIChUR19SRUxJRCwgVEdfTkFNRSwgcmVxdWVzdF9pZCk7CgogICAgICAgICAgICAgIFJFVFVSTiBORVc7CiAgICAgICAgICAgIEVORAogICAgICAgICAgJGZ1bmN0aW9uJDsKICAgICAgICAgIC0tIFN1cGFiYXNlIHN1cGVyIGFkbWluCiAgICAgICAgICBETwogICAgICAgICAgJCQKICAgICAgICAgIEJFR0lOCiAgICAgICAgICAgIElGIE5PVCBFWElTVFMgKAogICAgICAgICAgICAgIFNFTEVDVCAxCiAgICAgICAgICAgICAgRlJPTSBwZ19yb2xlcwogICAgICAgICAgICAgIFdIRVJFIHJvbG5hbWUgPSAnc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluJwogICAgICAgICAgICApCiAgICAgICAgICAgIFRIRU4KICAgICAgICAgICAgICBDUkVBVEUgVVNFUiBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4gTk9JTkhFUklUIENSRUFURVJPTEUgTE9HSU4gTk9SRVBMSUNBVElPTjsKICAgICAgICAgICAgRU5EIElGOwogICAgICAgICAgRU5ECiAgICAgICAgICAkJDsKICAgICAgICAgIEdSQU5UIEFMTCBQUklWSUxFR0VTIE9OIFNDSEVNQSBzdXBhYmFzZV9mdW5jdGlvbnMgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluOwogICAgICAgICAgR1JBTlQgQUxMIFBSSVZJTEVHRVMgT04gQUxMIFRBQkxFUyBJTiBTQ0hFTUEgc3VwYWJhc2VfZnVuY3Rpb25zIFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbjsKICAgICAgICAgIEdSQU5UIEFMTCBQUklWSUxFR0VTIE9OIEFMTCBTRVFVRU5DRVMgSU4gU0NIRU1BIHN1cGFiYXNlX2Z1bmN0aW9ucyBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW47CiAgICAgICAgICBBTFRFUiBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBTRVQgc2VhcmNoX3BhdGggPSAic3VwYWJhc2VfZnVuY3Rpb25zIjsKICAgICAgICAgIEFMVEVSIHRhYmxlICJzdXBhYmFzZV9mdW5jdGlvbnMiLm1pZ3JhdGlvbnMgT1dORVIgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluOwogICAgICAgICAgQUxURVIgdGFibGUgInN1cGFiYXNlX2Z1bmN0aW9ucyIuaG9va3MgT1dORVIgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluOwogICAgICAgICAgQUxURVIgZnVuY3Rpb24gInN1cGFiYXNlX2Z1bmN0aW9ucyIuaHR0cF9yZXF1ZXN0KCkgT1dORVIgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluOwogICAgICAgICAgR1JBTlQgc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluIFRPIHBvc3RncmVzOwogICAgICAgICAgLS0gUmVtb3ZlIHVudXNlZCBzdXBhYmFzZV9wZ19uZXRfYWRtaW4gcm9sZQogICAgICAgICAgRE8KICAgICAgICAgICQkCiAgICAgICAgICBCRUdJTgogICAgICAgICAgICBJRiBFWElTVFMgKAogICAgICAgICAgICAgIFNFTEVDVCAxCiAgICAgICAgICAgICAgRlJPTSBwZ19yb2xlcwogICAgICAgICAgICAgIFdIRVJFIHJvbG5hbWUgPSAnc3VwYWJhc2VfcGdfbmV0X2FkbWluJwogICAgICAgICAgICApCiAgICAgICAgICAgIFRIRU4KICAgICAgICAgICAgICBSRUFTU0lHTiBPV05FRCBCWSBzdXBhYmFzZV9wZ19uZXRfYWRtaW4gVE8gc3VwYWJhc2VfYWRtaW47CiAgICAgICAgICAgICAgRFJPUCBPV05FRCBCWSBzdXBhYmFzZV9wZ19uZXRfYWRtaW47CiAgICAgICAgICAgICAgRFJPUCBST0xFIHN1cGFiYXNlX3BnX25ldF9hZG1pbjsKICAgICAgICAgICAgRU5EIElGOwogICAgICAgICAgRU5ECiAgICAgICAgICAkJDsKICAgICAgICAgIC0tIHBnX25ldCBncmFudHMgd2hlbiBleHRlbnNpb24gaXMgYWxyZWFkeSBlbmFibGVkCiAgICAgICAgICBETwogICAgICAgICAgJCQKICAgICAgICAgIEJFR0lOCiAgICAgICAgICAgIElGIEVYSVNUUyAoCiAgICAgICAgICAgICAgU0VMRUNUIDEKICAgICAgICAgICAgICBGUk9NIHBnX2V4dGVuc2lvbgogICAgICAgICAgICAgIFdIRVJFIGV4dG5hbWUgPSAncGdfbmV0JwogICAgICAgICAgICApCiAgICAgICAgICAgIFRIRU4KICAgICAgICAgICAgICBHUkFOVCBVU0FHRSBPTiBTQ0hFTUEgbmV0IFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTsKICAgICAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VDVVJJVFkgREVGSU5FUjsKICAgICAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFQ1VSSVRZIERFRklORVI7CiAgICAgICAgICAgICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFVCBzZWFyY2hfcGF0aCA9IG5ldDsKICAgICAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFVCBzZWFyY2hfcGF0aCA9IG5ldDsKICAgICAgICAgICAgICBSRVZPS0UgQUxMIE9OIEZVTkNUSU9OIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBGUk9NIFBVQkxJQzsKICAgICAgICAgICAgICBSRVZPS0UgQUxMIE9OIEZVTkNUSU9OIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgRlJPTSBQVUJMSUM7CiAgICAgICAgICAgICAgR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlOwogICAgICAgICAgICAgIEdSQU5UIEVYRUNVVEUgT04gRlVOQ1RJT04gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7CiAgICAgICAgICAgIEVORCBJRjsKICAgICAgICAgIEVORAogICAgICAgICAgJCQ7CiAgICAgICAgICAtLSBFdmVudCB0cmlnZ2VyIGZvciBwZ19uZXQKICAgICAgICAgIENSRUFURSBPUiBSRVBMQUNFIEZVTkNUSU9OIGV4dGVuc2lvbnMuZ3JhbnRfcGdfbmV0X2FjY2VzcygpCiAgICAgICAgICBSRVRVUk5TIGV2ZW50X3RyaWdnZXIKICAgICAgICAgIExBTkdVQUdFIHBscGdzcWwKICAgICAgICAgIEFTICQkCiAgICAgICAgICBCRUdJTgogICAgICAgICAgICBJRiBFWElTVFMgKAogICAgICAgICAgICAgIFNFTEVDVCAxCiAgICAgICAgICAgICAgRlJPTSBwZ19ldmVudF90cmlnZ2VyX2RkbF9jb21tYW5kcygpIEFTIGV2CiAgICAgICAgICAgICAgSk9JTiBwZ19leHRlbnNpb24gQVMgZXh0CiAgICAgICAgICAgICAgT04gZXYub2JqaWQgPSBleHQub2lkCiAgICAgICAgICAgICAgV0hFUkUgZXh0LmV4dG5hbWUgPSAncGdfbmV0JwogICAgICAgICAgICApCiAgICAgICAgICAgIFRIRU4KICAgICAgICAgICAgICBHUkFOVCBVU0FHRSBPTiBTQ0hFTUEgbmV0IFRPIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiwgcG9zdGdyZXMsIGFub24sIGF1dGhlbnRpY2F0ZWQsIHNlcnZpY2Vfcm9sZTsKICAgICAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgU0VDVVJJVFkgREVGSU5FUjsKICAgICAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFQ1VSSVRZIERFRklORVI7CiAgICAgICAgICAgICAgQUxURVIgZnVuY3Rpb24gbmV0Lmh0dHBfZ2V0KHVybCB0ZXh0LCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFVCBzZWFyY2hfcGF0aCA9IG5ldDsKICAgICAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBuZXQuaHR0cF9wb3N0KHVybCB0ZXh0LCBib2R5IGpzb25iLCBwYXJhbXMganNvbmIsIGhlYWRlcnMganNvbmIsIHRpbWVvdXRfbWlsbGlzZWNvbmRzIGludGVnZXIpIFNFVCBzZWFyY2hfcGF0aCA9IG5ldDsKICAgICAgICAgICAgICBSRVZPS0UgQUxMIE9OIEZVTkNUSU9OIG5ldC5odHRwX2dldCh1cmwgdGV4dCwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBGUk9NIFBVQkxJQzsKICAgICAgICAgICAgICBSRVZPS0UgQUxMIE9OIEZVTkNUSU9OIG5ldC5odHRwX3Bvc3QodXJsIHRleHQsIGJvZHkganNvbmIsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgRlJPTSBQVUJMSUM7CiAgICAgICAgICAgICAgR1JBTlQgRVhFQ1VURSBPTiBGVU5DVElPTiBuZXQuaHR0cF9nZXQodXJsIHRleHQsIHBhcmFtcyBqc29uYiwgaGVhZGVycyBqc29uYiwgdGltZW91dF9taWxsaXNlY29uZHMgaW50ZWdlcikgVE8gc3VwYWJhc2VfZnVuY3Rpb25zX2FkbWluLCBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlOwogICAgICAgICAgICAgIEdSQU5UIEVYRUNVVEUgT04gRlVOQ1RJT04gbmV0Lmh0dHBfcG9zdCh1cmwgdGV4dCwgYm9keSBqc29uYiwgcGFyYW1zIGpzb25iLCBoZWFkZXJzIGpzb25iLCB0aW1lb3V0X21pbGxpc2Vjb25kcyBpbnRlZ2VyKSBUTyBzdXBhYmFzZV9mdW5jdGlvbnNfYWRtaW4sIHBvc3RncmVzLCBhbm9uLCBhdXRoZW50aWNhdGVkLCBzZXJ2aWNlX3JvbGU7CiAgICAgICAgICAgIEVORCBJRjsKICAgICAgICAgIEVORDsKICAgICAgICAgICQkOwogICAgICAgICAgQ09NTUVOVCBPTiBGVU5DVElPTiBleHRlbnNpb25zLmdyYW50X3BnX25ldF9hY2Nlc3MgSVMgJ0dyYW50cyBhY2Nlc3MgdG8gcGdfbmV0JzsKICAgICAgICAgIERPCiAgICAgICAgICAkJAogICAgICAgICAgQkVHSU4KICAgICAgICAgICAgSUYgTk9UIEVYSVNUUyAoCiAgICAgICAgICAgICAgU0VMRUNUIDEKICAgICAgICAgICAgICBGUk9NIHBnX2V2ZW50X3RyaWdnZXIKICAgICAgICAgICAgICBXSEVSRSBldnRuYW1lID0gJ2lzc3VlX3BnX25ldF9hY2Nlc3MnCiAgICAgICAgICAgICkgVEhFTgogICAgICAgICAgICAgIENSRUFURSBFVkVOVCBUUklHR0VSIGlzc3VlX3BnX25ldF9hY2Nlc3MgT04gZGRsX2NvbW1hbmRfZW5kIFdIRU4gVEFHIElOICgnQ1JFQVRFIEVYVEVOU0lPTicpCiAgICAgICAgICAgICAgRVhFQ1VURSBQUk9DRURVUkUgZXh0ZW5zaW9ucy5ncmFudF9wZ19uZXRfYWNjZXNzKCk7CiAgICAgICAgICAgIEVORCBJRjsKICAgICAgICAgIEVORAogICAgICAgICAgJCQ7CiAgICAgICAgICBJTlNFUlQgSU5UTyBzdXBhYmFzZV9mdW5jdGlvbnMubWlncmF0aW9ucyAodmVyc2lvbikgVkFMVUVTICgnMjAyMTA4MDkxODM0MjNfdXBkYXRlX2dyYW50cycpOwogICAgICAgICAgQUxURVIgZnVuY3Rpb24gc3VwYWJhc2VfZnVuY3Rpb25zLmh0dHBfcmVxdWVzdCgpIFNFQ1VSSVRZIERFRklORVI7CiAgICAgICAgICBBTFRFUiBmdW5jdGlvbiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KCkgU0VUIHNlYXJjaF9wYXRoID0gc3VwYWJhc2VfZnVuY3Rpb25zOwogICAgICAgICAgUkVWT0tFIEFMTCBPTiBGVU5DVElPTiBzdXBhYmFzZV9mdW5jdGlvbnMuaHR0cF9yZXF1ZXN0KCkgRlJPTSBQVUJMSUM7CiAgICAgICAgICBHUkFOVCBFWEVDVVRFIE9OIEZVTkNUSU9OIHN1cGFiYXNlX2Z1bmN0aW9ucy5odHRwX3JlcXVlc3QoKSBUTyBwb3N0Z3JlcywgYW5vbiwgYXV0aGVudGljYXRlZCwgc2VydmljZV9yb2xlOwogICAgICAgICAgQ09NTUlUOwogICAgICAtIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi9yb2xlcy5zcWwKICAgICAgICB0YXJnZXQ6IC9kb2NrZXItZW50cnlwb2ludC1pbml0ZGIuZC9pbml0LXNjcmlwdHMvOTktcm9sZXMuc3FsCiAgICAgICAgY29udGVudDogfAogICAgICAgICAgLS0gTk9URTogY2hhbmdlIHRvIHlvdXIgb3duIHBhc3N3b3JkcyBmb3IgcHJvZHVjdGlvbiBlbnZpcm9ubWVudHMKICAgICAgICAgICBcc2V0IHBncGFzcyBgZWNobyAiJFBPU1RHUkVTX1BBU1NXT1JEImAKCiAgICAgICAgICAgQUxURVIgVVNFUiBhdXRoZW50aWNhdG9yIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnOwogICAgICAgICAgIEFMVEVSIFVTRVIgcGdib3VuY2VyIFdJVEggUEFTU1dPUkQgOidwZ3Bhc3MnOwogICAgICAgICAgIEFMVEVSIFVTRVIgc3VwYWJhc2VfYXV0aF9hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJzsKICAgICAgICAgICBBTFRFUiBVU0VSIHN1cGFiYXNlX2Z1bmN0aW9uc19hZG1pbiBXSVRIIFBBU1NXT1JEIDoncGdwYXNzJzsKICAgICAgICAgICBBTFRFUiBVU0VSIHN1cGFiYXNlX3N0b3JhZ2VfYWRtaW4gV0lUSCBQQVNTV09SRCA6J3BncGFzcyc7CiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2RiL2p3dC5zcWwKICAgICAgICB0YXJnZXQ6IC9kb2NrZXItZW50cnlwb2ludC1pbml0ZGIuZC9pbml0LXNjcmlwdHMvOTktand0LnNxbAogICAgICAgIGNvbnRlbnQ6IHwKICAgICAgICAgIFxzZXQgand0X3NlY3JldCBgZWNobyAiJEpXVF9TRUNSRVQiYAogICAgICAgICAgXHNldCBqd3RfZXhwIGBlY2hvICIkSldUX0VYUCJgCgogICAgICAgICAgQUxURVIgREFUQUJBU0UgcG9zdGdyZXMgU0VUICJhcHAuc2V0dGluZ3Muand0X3NlY3JldCIgVE8gOidqd3Rfc2VjcmV0JzsKICAgICAgICAgIEFMVEVSIERBVEFCQVNFIHBvc3RncmVzIFNFVCAiYXBwLnNldHRpbmdzLmp3dF9leHAiIFRPIDonand0X2V4cCc7CgogICAgICAtIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9kYi9sb2dzLnNxbAogICAgICAgIHRhcmdldDogL2RvY2tlci1lbnRyeXBvaW50LWluaXRkYi5kL21pZ3JhdGlvbnMvOTktbG9ncy5zcWwKICAgICAgICBjb250ZW50OiB8CiAgICAgICAgICBcc2V0IHBndXNlciBgZWNobyAic3VwYWJhc2VfYWRtaW4iYAoKICAgICAgICAgIGNyZWF0ZSBzY2hlbWEgaWYgbm90IGV4aXN0cyBfYW5hbHl0aWNzOwogICAgICAgICAgYWx0ZXIgc2NoZW1hIF9hbmFseXRpY3Mgb3duZXIgdG8gOnBndXNlcjsKICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICBpbWFnZTogc3VwYWJhc2UvbG9nZmxhcmU6MS40LjAKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgImh0dHA6Ly9sb2NhbGhvc3Q6NDAwMC9oZWFsdGgiXQogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMTAKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1kYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgIyB2b2x1bWVzOgogICAgIyAgIC0gdHlwZTogYmluZAogICAgIyAgICAgc291cmNlOiAuL3ZvbHVtZXMvZ2Nsb3VkLmpzb24KICAgICMgICAgIHRhcmdldDogL29wdC9hcHAvcmVsL2xvZ2ZsYXJlL2Jpbi9nY2xvdWQuanNvbgogICAgIyAgICAgcmVhZF9vbmx5OiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBMT0dGTEFSRV9OT0RFX0hPU1Q9MTI3LjAuMC4xCiAgICAgIC0gREJfVVNFUk5BTUU9c3VwYWJhc2VfYWRtaW4KICAgICAgLSBEQl9EQVRBQkFTRT0ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0KICAgICAgLSBEQl9IT1NUTkFNRT0ke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifQogICAgICAtIERCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfQogICAgICAtIERCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30KICAgICAgLSBEQl9TQ0hFTUE9X2FuYWx5dGljcwogICAgICAtIExPR0ZMQVJFX0FQSV9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX0xPR0ZMQVJFfQogICAgICAtIExPR0ZMQVJFX1NJTkdMRV9URU5BTlQ9dHJ1ZQogICAgICAtIExPR0ZMQVJFX1NJTkdMRV9URU5BTlRfTU9ERT10cnVlCiAgICAgIC0gTE9HRkxBUkVfU1VQQUJBU0VfTU9ERT10cnVlCgogICAgICAjIENvbW1lbnQgdmFyaWFibGVzIHRvIHVzZSBCaWcgUXVlcnkgYmFja2VuZCBmb3IgYW5hbHl0aWNzCiAgICAgIC0gUE9TVEdSRVNfQkFDS0VORF9VUkw9cG9zdGdyZXNxbDovL3N1cGFiYXNlX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0KICAgICAgLSBQT1NUR1JFU19CQUNLRU5EX1NDSEVNQT1fYW5hbHl0aWNzCiAgICAgIC0gTE9HRkxBUkVfRkVBVFVSRV9GTEFHX09WRVJSSURFPW11bHRpYmFja2VuZD10cnVlCgogICAgICAjIFVuY29tbWVudCB0byB1c2UgQmlnIFF1ZXJ5IGJhY2tlbmQgZm9yIGFuYWx5dGljcwogICAgICAjIEdPT0dMRV9QUk9KRUNUX0lEPSR7R09PR0xFX1BST0pFQ1RfSUR9CiAgICAgICMgR09PR0xFX1BST0pFQ1RfTlVNQkVSPSR7R09PR0xFX1BST0pFQ1RfTlVNQkVSfQogIHN1cGFiYXNlLXZlY3RvcjoKICAgIGltYWdlOiB0aW1iZXJpby92ZWN0b3I6MC4yOC4xLWFscGluZQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgWwogICAgICAgICAgIkNNRCIsCiAgICAgICAgICAid2dldCIsCiAgICAgICAgICAiLS1uby12ZXJib3NlIiwKICAgICAgICAgICItLXRyaWVzPTEiLAogICAgICAgICAgIi0tc3BpZGVyIiwKICAgICAgICAgICJodHRwOi8vc3VwYWJhc2UtdmVjdG9yOjkwMDEvaGVhbHRoIiwKICAgICAgICBdCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICB2b2x1bWVzOgogICAgICAtIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vdm9sdW1lcy9sb2dzL3ZlY3Rvci55bWwKICAgICAgICB0YXJnZXQ6IC9ldGMvdmVjdG9yL3ZlY3Rvci55bWwKICAgICAgICByZWFkX29ubHk6IHRydWUKICAgICAgICBjb250ZW50OiB8CiAgICAgICAgICBhcGk6CiAgICAgICAgICAgIGVuYWJsZWQ6IHRydWUKICAgICAgICAgICAgYWRkcmVzczogMC4wLjAuMDo5MDAxCgogICAgICAgICAgc291cmNlczoKICAgICAgICAgICAgZG9ja2VyX2hvc3Q6CiAgICAgICAgICAgICAgdHlwZTogZG9ja2VyX2xvZ3MKICAgICAgICAgICAgICBleGNsdWRlX2NvbnRhaW5lcnM6CiAgICAgICAgICAgICAgICAtIHN1cGFiYXNlLXZlY3RvcgoKICAgICAgICAgIHRyYW5zZm9ybXM6CiAgICAgICAgICAgIHByb2plY3RfbG9nczoKICAgICAgICAgICAgICB0eXBlOiByZW1hcAogICAgICAgICAgICAgIGlucHV0czoKICAgICAgICAgICAgICAgIC0gZG9ja2VyX2hvc3QKICAgICAgICAgICAgICBzb3VyY2U6IHwtCiAgICAgICAgICAgICAgICAucHJvamVjdCA9ICJkZWZhdWx0IgogICAgICAgICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBkZWwoLm1lc3NhZ2UpCiAgICAgICAgICAgICAgICAuYXBwbmFtZSA9IGRlbCguY29udGFpbmVyX25hbWUpCiAgICAgICAgICAgICAgICBkZWwoLmNvbnRhaW5lcl9jcmVhdGVkX2F0KQogICAgICAgICAgICAgICAgZGVsKC5jb250YWluZXJfaWQpCiAgICAgICAgICAgICAgICBkZWwoLnNvdXJjZV90eXBlKQogICAgICAgICAgICAgICAgZGVsKC5zdHJlYW0pCiAgICAgICAgICAgICAgICBkZWwoLmxhYmVsKQogICAgICAgICAgICAgICAgZGVsKC5pbWFnZSkKICAgICAgICAgICAgICAgIGRlbCguaG9zdCkKICAgICAgICAgICAgICAgIGRlbCguc3RyZWFtKQogICAgICAgICAgICByb3V0ZXI6CiAgICAgICAgICAgICAgdHlwZTogcm91dGUKICAgICAgICAgICAgICBpbnB1dHM6CiAgICAgICAgICAgICAgICAtIHByb2plY3RfbG9ncwogICAgICAgICAgICAgIHJvdXRlOgogICAgICAgICAgICAgICAga29uZzogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCAic3VwYWJhc2Uta29uZyIpJwogICAgICAgICAgICAgICAgYXV0aDogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCAic3VwYWJhc2UtYXV0aCIpJwogICAgICAgICAgICAgICAgcmVzdDogJ3N0YXJ0c193aXRoKHN0cmluZyEoLmFwcG5hbWUpLCAic3VwYWJhc2UtcmVzdCIpJwogICAgICAgICAgICAgICAgcmVhbHRpbWU6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgInN1cGFiYXNlLXJlYWx0aW1lIiknCiAgICAgICAgICAgICAgICBzdG9yYWdlOiAnc3RhcnRzX3dpdGgoc3RyaW5nISguYXBwbmFtZSksICJzdXBhYmFzZS1zdG9yYWdlIiknCiAgICAgICAgICAgICAgICBmdW5jdGlvbnM6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgInN1cGFiYXNlLWZ1bmN0aW9ucyIpJwogICAgICAgICAgICAgICAgZGI6ICdzdGFydHNfd2l0aChzdHJpbmchKC5hcHBuYW1lKSwgInN1cGFiYXNlLWRiIiknCiAgICAgICAgICAgICMgSWdub3JlcyBub24gbmdpbnggZXJyb3JzIHNpbmNlIHRoZXkgYXJlIHJlbGF0ZWQgd2l0aCBrb25nIGJvb3RpbmcgdXAKICAgICAgICAgICAga29uZ19sb2dzOgogICAgICAgICAgICAgIHR5cGU6IHJlbWFwCiAgICAgICAgICAgICAgaW5wdXRzOgogICAgICAgICAgICAgICAgLSByb3V0ZXIua29uZwogICAgICAgICAgICAgIHNvdXJjZTogfC0KICAgICAgICAgICAgICAgIHJlcSwgZXJyID0gcGFyc2VfbmdpbnhfbG9nKC5ldmVudF9tZXNzYWdlLCAiY29tYmluZWQiKQogICAgICAgICAgICAgICAgaWYgZXJyID09IG51bGwgewogICAgICAgICAgICAgICAgICAgIC50aW1lc3RhbXAgPSByZXEudGltZXN0YW1wCiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaGVhZGVycy5yZWZlcmVyID0gcmVxLnJlZmVyZXIKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5oZWFkZXJzLnVzZXJfYWdlbnQgPSByZXEuYWdlbnQKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5oZWFkZXJzLmNmX2Nvbm5lY3RpbmdfaXAgPSByZXEuY2xpZW50CiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QubWV0aG9kID0gcmVxLm1ldGhvZAogICAgICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LnBhdGggPSByZXEucGF0aAogICAgICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LnByb3RvY29sID0gcmVxLnByb3RvY29sCiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlc3BvbnNlLnN0YXR1c19jb2RlID0gcmVxLnN0YXR1cwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgZXJyICE9IG51bGwgewogICAgICAgICAgICAgICAgICBhYm9ydAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAjIElnbm9yZXMgbm9uIG5naW54IGVycm9ycyBzaW5jZSB0aGV5IGFyZSByZWxhdGVkIHdpdGgga29uZyBib290aW5nIHVwCiAgICAgICAgICAgIGtvbmdfZXJyOgogICAgICAgICAgICAgIHR5cGU6IHJlbWFwCiAgICAgICAgICAgICAgaW5wdXRzOgogICAgICAgICAgICAgICAgLSByb3V0ZXIua29uZwogICAgICAgICAgICAgIHNvdXJjZTogfC0KICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0Lm1ldGhvZCA9ICJHRVQiCiAgICAgICAgICAgICAgICAubWV0YWRhdGEucmVzcG9uc2Uuc3RhdHVzX2NvZGUgPSAyMDAKICAgICAgICAgICAgICAgIHBhcnNlZCwgZXJyID0gcGFyc2VfbmdpbnhfbG9nKC5ldmVudF9tZXNzYWdlLCAiZXJyb3IiKQogICAgICAgICAgICAgICAgaWYgZXJyID09IG51bGwgewogICAgICAgICAgICAgICAgICAgIC50aW1lc3RhbXAgPSBwYXJzZWQudGltZXN0YW1wCiAgICAgICAgICAgICAgICAgICAgLnNldmVyaXR5ID0gcGFyc2VkLnNldmVyaXR5CiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaG9zdCA9IHBhcnNlZC5ob3N0CiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLnJlcXVlc3QuaGVhZGVycy5jZl9jb25uZWN0aW5nX2lwID0gcGFyc2VkLmNsaWVudAogICAgICAgICAgICAgICAgICAgIHVybCwgZXJyID0gc3BsaXQocGFyc2VkLnJlcXVlc3QsICIgIikKICAgICAgICAgICAgICAgICAgICBpZiBlcnIgPT0gbnVsbCB7CiAgICAgICAgICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0Lm1ldGhvZCA9IHVybFswXQogICAgICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEucmVxdWVzdC5wYXRoID0gdXJsWzFdCiAgICAgICAgICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5yZXF1ZXN0LnByb3RvY29sID0gdXJsWzJdCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgZXJyICE9IG51bGwgewogICAgICAgICAgICAgICAgICBhYm9ydAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAjIEdvdHJ1ZSBsb2dzIGFyZSBzdHJ1Y3R1cmVkIGpzb24gc3RyaW5ncyB3aGljaCBmcm9udGVuZCBwYXJzZXMgZGlyZWN0bHkuIEJ1dCB3ZSBrZWVwIG1ldGFkYXRhIGZvciBjb25zaXN0ZW5jeS4KICAgICAgICAgICAgYXV0aF9sb2dzOgogICAgICAgICAgICAgIHR5cGU6IHJlbWFwCiAgICAgICAgICAgICAgaW5wdXRzOgogICAgICAgICAgICAgICAgLSByb3V0ZXIuYXV0aAogICAgICAgICAgICAgIHNvdXJjZTogfC0KICAgICAgICAgICAgICAgIHBhcnNlZCwgZXJyID0gcGFyc2VfanNvbiguZXZlbnRfbWVzc2FnZSkKICAgICAgICAgICAgICAgIGlmIGVyciA9PSBudWxsIHsKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEudGltZXN0YW1wID0gcGFyc2VkLnRpbWUKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEgPSBtZXJnZSEoLm1ldGFkYXRhLCBwYXJzZWQpCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICMgUG9zdGdSRVNUIGxvZ3MgYXJlIHN0cnVjdHVyZWQgc28gd2Ugc2VwYXJhdGUgdGltZXN0YW1wIGZyb20gbWVzc2FnZSB1c2luZyByZWdleAogICAgICAgICAgICByZXN0X2xvZ3M6CiAgICAgICAgICAgICAgdHlwZTogcmVtYXAKICAgICAgICAgICAgICBpbnB1dHM6CiAgICAgICAgICAgICAgICAtIHJvdXRlci5yZXN0CiAgICAgICAgICAgICAgc291cmNlOiB8LQogICAgICAgICAgICAgICAgcGFyc2VkLCBlcnIgPSBwYXJzZV9yZWdleCguZXZlbnRfbWVzc2FnZSwgcideKD9QPHRpbWU+LiopOiAoP1A8bXNnPi4qKSQnKQogICAgICAgICAgICAgICAgaWYgZXJyID09IG51bGwgewogICAgICAgICAgICAgICAgICAgIC5ldmVudF9tZXNzYWdlID0gcGFyc2VkLm1zZwogICAgICAgICAgICAgICAgICAgIC50aW1lc3RhbXAgPSB0b190aW1lc3RhbXAhKHBhcnNlZC50aW1lKQogICAgICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5ob3N0ID0gLnByb2plY3QKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgIyBSZWFsdGltZSBsb2dzIGFyZSBzdHJ1Y3R1cmVkIHNvIHdlIHBhcnNlIHRoZSBzZXZlcml0eSBsZXZlbCB1c2luZyByZWdleCAoaWdub3JlIHRpbWUgYmVjYXVzZSBpdCBoYXMgbm8gZGF0ZSkKICAgICAgICAgICAgcmVhbHRpbWVfbG9nczoKICAgICAgICAgICAgICB0eXBlOiByZW1hcAogICAgICAgICAgICAgIGlucHV0czoKICAgICAgICAgICAgICAgIC0gcm91dGVyLnJlYWx0aW1lCiAgICAgICAgICAgICAgc291cmNlOiB8LQogICAgICAgICAgICAgICAgLm1ldGFkYXRhLnByb2plY3QgPSBkZWwoLnByb2plY3QpCiAgICAgICAgICAgICAgICAubWV0YWRhdGEuZXh0ZXJuYWxfaWQgPSAubWV0YWRhdGEucHJvamVjdAogICAgICAgICAgICAgICAgcGFyc2VkLCBlcnIgPSBwYXJzZV9yZWdleCguZXZlbnRfbWVzc2FnZSwgcideKD9QPHRpbWU+XGQrOlxkKzpcZCtcLlxkKykgXFsoP1A8bGV2ZWw+XHcrKVxdICg/UDxtc2c+LiopJCcpCiAgICAgICAgICAgICAgICBpZiBlcnIgPT0gbnVsbCB7CiAgICAgICAgICAgICAgICAgICAgLmV2ZW50X21lc3NhZ2UgPSBwYXJzZWQubXNnCiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLmxldmVsID0gcGFyc2VkLmxldmVsCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICMgU3RvcmFnZSBsb2dzIG1heSBjb250YWluIGpzb24gb2JqZWN0cyBzbyB3ZSBwYXJzZSB0aGVtIGZvciBjb21wbGV0ZW5lc3MKICAgICAgICAgICAgc3RvcmFnZV9sb2dzOgogICAgICAgICAgICAgIHR5cGU6IHJlbWFwCiAgICAgICAgICAgICAgaW5wdXRzOgogICAgICAgICAgICAgICAgLSByb3V0ZXIuc3RvcmFnZQogICAgICAgICAgICAgIHNvdXJjZTogfC0KICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5wcm9qZWN0ID0gZGVsKC5wcm9qZWN0KQogICAgICAgICAgICAgICAgLm1ldGFkYXRhLnRlbmFudElkID0gLm1ldGFkYXRhLnByb2plY3QKICAgICAgICAgICAgICAgIHBhcnNlZCwgZXJyID0gcGFyc2VfanNvbiguZXZlbnRfbWVzc2FnZSkKICAgICAgICAgICAgICAgIGlmIGVyciA9PSBudWxsIHsKICAgICAgICAgICAgICAgICAgICAuZXZlbnRfbWVzc2FnZSA9IHBhcnNlZC5tc2cKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEubGV2ZWwgPSBwYXJzZWQubGV2ZWwKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEudGltZXN0YW1wID0gcGFyc2VkLnRpbWUKICAgICAgICAgICAgICAgICAgICAubWV0YWRhdGEuY29udGV4dFswXS5ob3N0ID0gcGFyc2VkLmhvc3RuYW1lCiAgICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLmNvbnRleHRbMF0ucGlkID0gcGFyc2VkLnBpZAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAjIFBvc3RncmVzIGxvZ3Mgc29tZSBtZXNzYWdlcyB0byBzdGRlcnIgd2hpY2ggd2UgbWFwIHRvIHdhcm5pbmcgc2V2ZXJpdHkgbGV2ZWwKICAgICAgICAgICAgZGJfbG9nczoKICAgICAgICAgICAgICB0eXBlOiByZW1hcAogICAgICAgICAgICAgIGlucHV0czoKICAgICAgICAgICAgICAgIC0gcm91dGVyLmRiCiAgICAgICAgICAgICAgc291cmNlOiB8LQogICAgICAgICAgICAgICAgLm1ldGFkYXRhLmhvc3QgPSAiZGItZGVmYXVsdCIKICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5wYXJzZWQudGltZXN0YW1wID0gLnRpbWVzdGFtcAoKICAgICAgICAgICAgICAgIHBhcnNlZCwgZXJyID0gcGFyc2VfcmVnZXgoLmV2ZW50X21lc3NhZ2UsIHInLiooP1A8bGV2ZWw+SU5GT3xOT1RJQ0V8V0FSTklOR3xFUlJPUnxMT0d8RkFUQUx8UEFOSUM/KTouKicsIG51bWVyaWNfZ3JvdXBzOiB0cnVlKQoKICAgICAgICAgICAgICAgIGlmIGVyciAhPSBudWxsIHx8IHBhcnNlZCA9PSBudWxsIHsKICAgICAgICAgICAgICAgICAgLm1ldGFkYXRhLnBhcnNlZC5lcnJvcl9zZXZlcml0eSA9ICJpbmZvIgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgcGFyc2VkICE9IG51bGwgewogICAgICAgICAgICAgICAgLm1ldGFkYXRhLnBhcnNlZC5lcnJvcl9zZXZlcml0eSA9IHBhcnNlZC5sZXZlbAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgaWYgLm1ldGFkYXRhLnBhcnNlZC5lcnJvcl9zZXZlcml0eSA9PSAiaW5mbyIgewogICAgICAgICAgICAgICAgICAgIC5tZXRhZGF0YS5wYXJzZWQuZXJyb3Jfc2V2ZXJpdHkgPSAibG9nIgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgLm1ldGFkYXRhLnBhcnNlZC5lcnJvcl9zZXZlcml0eSA9IHVwY2FzZSEoLm1ldGFkYXRhLnBhcnNlZC5lcnJvcl9zZXZlcml0eSkKCiAgICAgICAgICBzaW5rczoKICAgICAgICAgICAgbG9nZmxhcmVfYXV0aDoKICAgICAgICAgICAgICB0eXBlOiAnaHR0cCcKICAgICAgICAgICAgICBpbnB1dHM6CiAgICAgICAgICAgICAgICAtIGF1dGhfbG9ncwogICAgICAgICAgICAgIGVuY29kaW5nOgogICAgICAgICAgICAgICAgY29kZWM6ICdqc29uJwogICAgICAgICAgICAgIG1ldGhvZDogJ3Bvc3QnCiAgICAgICAgICAgICAgcmVxdWVzdDoKICAgICAgICAgICAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMAogICAgICAgICAgICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1nb3RydWUubG9ncy5wcm9kJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZfScKICAgICAgICAgICAgbG9nZmxhcmVfcmVhbHRpbWU6CiAgICAgICAgICAgICAgdHlwZTogJ2h0dHAnCiAgICAgICAgICAgICAgaW5wdXRzOgogICAgICAgICAgICAgICAgLSByZWFsdGltZV9sb2dzCiAgICAgICAgICAgICAgZW5jb2Rpbmc6CiAgICAgICAgICAgICAgICBjb2RlYzogJ2pzb24nCiAgICAgICAgICAgICAgbWV0aG9kOiAncG9zdCcKICAgICAgICAgICAgICByZXF1ZXN0OgogICAgICAgICAgICAgICAgcmV0cnlfbWF4X2R1cmF0aW9uX3NlY3M6IDEwCiAgICAgICAgICAgICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWFuYWx5dGljczo0MDAwL2FwaS9sb2dzP3NvdXJjZV9uYW1lPXJlYWx0aW1lLmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nCiAgICAgICAgICAgIGxvZ2ZsYXJlX3Jlc3Q6CiAgICAgICAgICAgICAgdHlwZTogJ2h0dHAnCiAgICAgICAgICAgICAgaW5wdXRzOgogICAgICAgICAgICAgICAgLSByZXN0X2xvZ3MKICAgICAgICAgICAgICBlbmNvZGluZzoKICAgICAgICAgICAgICAgIGNvZGVjOiAnanNvbicKICAgICAgICAgICAgICBtZXRob2Q6ICdwb3N0JwogICAgICAgICAgICAgIHJlcXVlc3Q6CiAgICAgICAgICAgICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTAKICAgICAgICAgICAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M/c291cmNlX25hbWU9cG9zdGdSRVNULmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nCiAgICAgICAgICAgIGxvZ2ZsYXJlX2RiOgogICAgICAgICAgICAgIHR5cGU6ICdodHRwJwogICAgICAgICAgICAgIGlucHV0czoKICAgICAgICAgICAgICAgIC0gZGJfbG9ncwogICAgICAgICAgICAgIGVuY29kaW5nOgogICAgICAgICAgICAgICAgY29kZWM6ICdqc29uJwogICAgICAgICAgICAgIG1ldGhvZDogJ3Bvc3QnCiAgICAgICAgICAgICAgcmVxdWVzdDoKICAgICAgICAgICAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMAogICAgICAgICAgICAgICMgV2UgbXVzdCByb3V0ZSB0aGUgc2luayB0aHJvdWdoIGtvbmcgYmVjYXVzZSBpbmdlc3RpbmcgbG9ncyBiZWZvcmUgbG9nZmxhcmUgaXMgZnVsbHkgaW5pdGlhbGlzZWQgd2lsbAogICAgICAgICAgICAgICMgbGVhZCB0byBicm9rZW4gcXVlcmllcyBmcm9tIHN0dWRpby4gVGhpcyB3b3JrcyBieSB0aGUgYXNzdW1wdGlvbiB0aGF0IGNvbnRhaW5lcnMgYXJlIHN0YXJ0ZWQgaW4gdGhlCiAgICAgICAgICAgICAgIyBmb2xsb3dpbmcgb3JkZXI6IHZlY3RvciA+IGRiID4gbG9nZmxhcmUgPiBrb25nCiAgICAgICAgICAgICAgdXJpOiAnaHR0cDovL3N1cGFiYXNlLWtvbmc6ODAwMC9hbmFseXRpY3MvdjEvYXBpL2xvZ3M/c291cmNlX25hbWU9cG9zdGdyZXMubG9ncyZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nCiAgICAgICAgICAgIGxvZ2ZsYXJlX2Z1bmN0aW9uczoKICAgICAgICAgICAgICB0eXBlOiAnaHR0cCcKICAgICAgICAgICAgICBpbnB1dHM6CiAgICAgICAgICAgICAgICAtIHJvdXRlci5mdW5jdGlvbnMKICAgICAgICAgICAgICBlbmNvZGluZzoKICAgICAgICAgICAgICAgIGNvZGVjOiAnanNvbicKICAgICAgICAgICAgICBtZXRob2Q6ICdwb3N0JwogICAgICAgICAgICAgIHJlcXVlc3Q6CiAgICAgICAgICAgICAgICByZXRyeV9tYXhfZHVyYXRpb25fc2VjczogMTAKICAgICAgICAgICAgICB1cmk6ICdodHRwOi8vc3VwYWJhc2UtYW5hbHl0aWNzOjQwMDAvYXBpL2xvZ3M/c291cmNlX25hbWU9ZGVuby1yZWxheS1sb2dzJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZfScKICAgICAgICAgICAgbG9nZmxhcmVfc3RvcmFnZToKICAgICAgICAgICAgICB0eXBlOiAnaHR0cCcKICAgICAgICAgICAgICBpbnB1dHM6CiAgICAgICAgICAgICAgICAtIHN0b3JhZ2VfbG9ncwogICAgICAgICAgICAgIGVuY29kaW5nOgogICAgICAgICAgICAgICAgY29kZWM6ICdqc29uJwogICAgICAgICAgICAgIG1ldGhvZDogJ3Bvc3QnCiAgICAgICAgICAgICAgcmVxdWVzdDoKICAgICAgICAgICAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMAogICAgICAgICAgICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1zdG9yYWdlLmxvZ3MucHJvZC4yJmFwaV9rZXk9JHtMT0dGTEFSRV9BUElfS0VZfScKICAgICAgICAgICAgbG9nZmxhcmVfa29uZzoKICAgICAgICAgICAgICB0eXBlOiAnaHR0cCcKICAgICAgICAgICAgICBpbnB1dHM6CiAgICAgICAgICAgICAgICAtIGtvbmdfbG9ncwogICAgICAgICAgICAgICAgLSBrb25nX2VycgogICAgICAgICAgICAgIGVuY29kaW5nOgogICAgICAgICAgICAgICAgY29kZWM6ICdqc29uJwogICAgICAgICAgICAgIG1ldGhvZDogJ3Bvc3QnCiAgICAgICAgICAgICAgcmVxdWVzdDoKICAgICAgICAgICAgICAgIHJldHJ5X21heF9kdXJhdGlvbl9zZWNzOiAxMAogICAgICAgICAgICAgIHVyaTogJ2h0dHA6Ly9zdXBhYmFzZS1hbmFseXRpY3M6NDAwMC9hcGkvbG9ncz9zb3VyY2VfbmFtZT1jbG91ZGZsYXJlLmxvZ3MucHJvZCZhcGlfa2V5PSR7TE9HRkxBUkVfQVBJX0tFWX0nCgogICAgICAtIC92YXIvcnVuL2RvY2tlci5zb2NrOi92YXIvcnVuL2RvY2tlci5zb2NrOnJvCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBMT0dGTEFSRV9BUElfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9MT0dGTEFSRX0KICAgIGNvbW1hbmQ6IFsiLS1jb25maWciLCAiZXRjL3ZlY3Rvci92ZWN0b3IueW1sIl0KCiAgc3VwYWJhc2UtcmVzdDoKICAgIGltYWdlOiBwb3N0Z3Jlc3QvcG9zdGdyZXN0OnYxMi4wLjEKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgICMgRGlzYWJsZSB0aGlzIGlmIHlvdSBhcmUgdXNpbmcgYW4gZXh0ZXJuYWwgUG9zdGdyZXMgZGF0YWJhc2UKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQR1JTVF9EQl9VUkk9cG9zdGdyZXM6Ly9hdXRoZW50aWNhdG9yOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0KICAgICAgLSBQR1JTVF9EQl9TQ0hFTUFTPSR7UEdSU1RfREJfU0NIRU1BUzotcHVibGljfQogICAgICAtIFBHUlNUX0RCX0FOT05fUk9MRT1hbm9uCiAgICAgIC0gUEdSU1RfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfQogICAgICAtIFBHUlNUX0RCX1VTRV9MRUdBQ1lfR1VDUz1mYWxzZQogICAgICAtIFBHUlNUX0FQUF9TRVRUSU5HU19KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9CiAgICAgIC0gUEdSU1RfQVBQX1NFVFRJTkdTX0pXVF9FWFA9JHtKV1RfRVhQSVJZOi0zNjAwfQogICAgY29tbWFuZDogInBvc3RncmVzdCIKICBzdXBhYmFzZS1hdXRoOgogICAgaW1hZ2U6IHN1cGFiYXNlL2dvdHJ1ZTp2Mi4xNDMuMAogICAgZGVwZW5kc19vbjoKICAgICAgc3VwYWJhc2UtZGI6CiAgICAgICAgIyBEaXNhYmxlIHRoaXMgaWYgeW91IGFyZSB1c2luZyBhbiBleHRlcm5hbCBQb3N0Z3JlcyBkYXRhYmFzZQogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHN1cGFiYXNlLWFuYWx5dGljczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgWwogICAgICAgICAgIkNNRCIsCiAgICAgICAgICAid2dldCIsCiAgICAgICAgICAiLS1uby12ZXJib3NlIiwKICAgICAgICAgICItLXRyaWVzPTEiLAogICAgICAgICAgIi0tc3BpZGVyIiwKICAgICAgICAgICJodHRwOi8vbG9jYWxob3N0Ojk5OTkvaGVhbHRoIiwKICAgICAgICBdCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBHT1RSVUVfQVBJX0hPU1Q9MC4wLjAuMAogICAgICAtIEdPVFJVRV9BUElfUE9SVD05OTk5CiAgICAgIC0gQVBJX0VYVEVSTkFMX1VSTD0ke0FQSV9FWFRFUk5BTF9VUkw6LWh0dHA6Ly9zdXBhYmFzZS1rb25nOjgwMDB9CgogICAgICAtIEdPVFJVRV9EQl9EUklWRVI9cG9zdGdyZXMKICAgICAgLSBHT1RSVUVfREJfREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vc3VwYWJhc2VfYXV0aF9hZG1pbjoke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QCR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9OiR7UE9TVEdSRVNfUE9SVDotNTQzMn0vJHtQT1NUR1JFU19EQjotc3VwYWJhc2V9CgogICAgICAtIEdPVFJVRV9TSVRFX1VSTD0ke1NFUlZJQ0VfU0lURV9VUkx9CiAgICAgIC0gR09UUlVFX1VSSV9BTExPV19MSVNUPSR7QURESVRJT05BTF9SRURJUkVDVF9VUkxTfQogICAgICAtIEdPVFJVRV9ESVNBQkxFX1NJR05VUD0ke0RJU0FCTEVfU0lHTlVQOi1mYWxzZX0KCiAgICAgIC0gR09UUlVFX0pXVF9BRE1JTl9ST0xFUz1zZXJ2aWNlX3JvbGUKICAgICAgLSBHT1RSVUVfSldUX0FVRD1hdXRoZW50aWNhdGVkCiAgICAgIC0gR09UUlVFX0pXVF9ERUZBVUxUX0dST1VQX05BTUU9YXV0aGVudGljYXRlZAogICAgICAtIEdPVFJVRV9KV1RfRVhQPSR7SldUX0VYUElSWTotMzYwMH0KICAgICAgLSBHT1RSVUVfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfQoKICAgICAgLSBHT1RSVUVfRVhURVJOQUxfRU1BSUxfRU5BQkxFRD0ke0VOQUJMRV9FTUFJTF9TSUdOVVA6LXRydWV9CiAgICAgIC0gR09UUlVFX01BSUxFUl9BVVRPQ09ORklSTT0ke0VOQUJMRV9FTUFJTF9BVVRPQ09ORklSTTotZmFsc2V9CiAgICAgICMgR09UUlVFX01BSUxFUl9TRUNVUkVfRU1BSUxfQ0hBTkdFX0VOQUJMRUQ9dHJ1ZQogICAgICAjIEdPVFJVRV9TTVRQX01BWF9GUkVRVUVOQ1k9MXMKICAgICAgLSBHT1RSVUVfU01UUF9BRE1JTl9FTUFJTD0ke1NNVFBfQURNSU5fRU1BSUx9CiAgICAgIC0gR09UUlVFX1NNVFBfSE9TVD0ke1NNVFBfSE9TVH0KICAgICAgLSBHT1RSVUVfU01UUF9QT1JUPSR7U01UUF9QT1JUOi01ODd9CiAgICAgIC0gR09UUlVFX1NNVFBfVVNFUj0ke1NNVFBfVVNFUn0KICAgICAgLSBHT1RSVUVfU01UUF9QQVNTPSR7U01UUF9QQVNTfQogICAgICAtIEdPVFJVRV9TTVRQX1NFTkRFUl9OQU1FPSR7U01UUF9TRU5ERVJfTkFNRX0KICAgICAgLSBHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX0lOVklURT0ke01BSUxFUl9VUkxQQVRIU19JTlZJVEU6LS9hdXRoL3YxL3ZlcmlmeX0KICAgICAgLSBHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX0NPTkZJUk1BVElPTj0ke01BSUxFUl9VUkxQQVRIU19DT05GSVJNQVRJT046LS9hdXRoL3YxL3ZlcmlmeX0KICAgICAgLSBHT1RSVUVfTUFJTEVSX1VSTFBBVEhTX1JFQ09WRVJZPSR7TUFJTEVSX1VSTFBBVEhTX1JFQ09WRVJZOi0vYXV0aC92MS92ZXJpZnl9CiAgICAgIC0gR09UUlVFX01BSUxFUl9VUkxQQVRIU19FTUFJTF9DSEFOR0U9JHtNQUlMRVJfVVJMUEFUSFNfRU1BSUxfQ0hBTkdFOi0vYXV0aC92MS92ZXJpZnl9CiAgICAgIC0gR09UUlVFX01BSUxFUl9URU1QTEFURVNfSU5WSVRFPSR7TUFJTEVSX1RFTVBMQVRFU19JTlZJVEV9CiAgICAgIC0gR09UUlVFX01BSUxFUl9URU1QTEFURVNfQ09ORklSTUFUSU9OPSR7TUFJTEVSX1RFTVBMQVRFU19DT05GSVJNQVRJT059CiAgICAgIC0gR09UUlVFX01BSUxFUl9URU1QTEFURVNfUkVDT1ZFUlk9JHtNQUlMRVJfVEVNUExBVEVTX1JFQ09WRVJZfQogICAgICAtIEdPVFJVRV9NQUlMRVJfVEVNUExBVEVTX01BR0lDX0xJTks9JHtNQUlMRVJfVEVNUExBVEVTX01BR0lDX0xJTkt9CiAgICAgIC0gR09UUlVFX01BSUxFUl9URU1QTEFURVNfRU1BSUxfQ0hBTkdFPSR7TUFJTEVSX1RFTVBMQVRFU19FTUFJTF9DSEFOR0V9CgogICAgICAtIEdPVFJVRV9NQUlMRVJfU1VCSkVDVFNfQ09ORklSTUFUSU9OPSR7TUFJTEVSX1NVQkpFQ1RTX0NPTkZJUk1BVElPTn0KICAgICAgLSBHT1RSVUVfTUFJTEVSX1NVQkpFQ1RTX1JFQ09WRVJZPSR7TUFJTEVSX1NVQkpFQ1RTX1JFQ09WRVJZfQogICAgICAtIEdPVFJVRV9NQUlMRVJfU1VCSkVDVFNfTUFHSUNfTElOSz0ke01BSUxFUl9TVUJKRUNUU19NQUdJQ19MSU5LfQogICAgICAtIEdPVFJVRV9NQUlMRVJfU1VCSkVDVFNfRU1BSUxfQ0hBTkdFPSR7TUFJTEVSX1NVQkpFQ1RTX0VNQUlMX0NIQU5HRX0KICAgICAgLSBHT1RSVUVfTUFJTEVSX1NVQkpFQ1RTX0lOVklURT0ke01BSUxFUl9TVUJKRUNUU19JTlZJVEV9CgogICAgICAtIEdPVFJVRV9FWFRFUk5BTF9QSE9ORV9FTkFCTEVEPSR7RU5BQkxFX1BIT05FX1NJR05VUDotdHJ1ZX0KICAgICAgLSBHT1RSVUVfU01TX0FVVE9DT05GSVJNPSR7RU5BQkxFX1BIT05FX0FVVE9DT05GSVJNOi10cnVlfQogIHN1cGFiYXNlLXJlYWx0aW1lOgogICAgIyBUaGlzIGNvbnRhaW5lciBuYW1lIGxvb2tzIGluY29uc2lzdGVudCBidXQgaXMgY29ycmVjdCBiZWNhdXNlIHJlYWx0aW1lIGNvbnN0cnVjdHMgdGVuYW50IGlkIGJ5IHBhcnNpbmcgdGhlIHN1YmRvbWFpbgogICAgaW1hZ2U6IHN1cGFiYXNlL3JlYWx0aW1lOnYyLjI1LjY2CiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1kYjoKICAgICAgICAjIERpc2FibGUgdGhpcyBpZiB5b3UgYXJlIHVzaW5nIGFuIGV4dGVybmFsIFBvc3RncmVzIGRhdGFiYXNlCiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgc3VwYWJhc2UtYW5hbHl0aWNzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAiYmFzaCIsICItYyIsICJwcmludGYgXFwwID4gL2Rldi90Y3AvbG9jYWxob3N0LzQwMDAiXQogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogNXMKICAgICAgcmV0cmllczogMwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9SVD00MDAwCiAgICAgIC0gREJfSE9TVD0ke1BPU1RHUkVTX0hPU1Q6LXN1cGFiYXNlLWRifQogICAgICAtIERCX1BPUlQ9JHtQT1NUR1JFU19QT1JUOi01NDMyfQogICAgICAtIERCX1VTRVI9c3VwYWJhc2VfYWRtaW4KICAgICAgLSBEQl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgIC0gREJfTkFNRT0ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0KICAgICAgLSBEQl9BRlRFUl9DT05ORUNUX1FVRVJZPVNFVCBzZWFyY2hfcGF0aCBUTyBfcmVhbHRpbWUKICAgICAgLSBEQl9FTkNfS0VZPXN1cGFiYXNlcmVhbHRpbWUKICAgICAgLSBBUElfSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfQogICAgICAtIEZMWV9BTExPQ19JRD1mbHkxMjMKICAgICAgLSBGTFlfQVBQX05BTUU9cmVhbHRpbWUKICAgICAgLSBTRUNSRVRfS0VZX0JBU0U9JHtTRUNSRVRfUEFTU1dPUkRfUkVBTFRJTUV9CiAgICAgIC0gRVJMX0FGTEFHUz0tcHJvdG9fZGlzdCBpbmV0X3RjcAogICAgICAtIEVOQUJMRV9UQUlMU0NBTEU9ZmFsc2UKICAgICAgLSBETlNfTk9ERVM9JycKICAgIGNvbW1hbmQ6ID4KICAgICAgc2ggLWMgIi9hcHAvYmluL21pZ3JhdGUgJiYgL2FwcC9iaW4vcmVhbHRpbWUgZXZhbCAnUmVhbHRpbWUuUmVsZWFzZS5zZWVkcyhSZWFsdGltZS5SZXBvKScgJiYgL2FwcC9iaW4vc2VydmVyIgogIHN1cGFiYXNlLW1pbmlvOgogICAgaW1hZ2U6IG1pbmlvL21pbmlvCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99CiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99CiAgICBjb21tYW5kOiBzZXJ2ZXIgLS1jb25zb2xlLWFkZHJlc3MgIjo5MDAxIiAvZGF0YQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IHNsZWVwIDUgJiYgZXhpdCAwCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogICAgdm9sdW1lczoKICAgICAgLSAuL3ZvbHVtZXMvc3RvcmFnZTovZGF0YQoKICBtaW5pby1jcmVhdGVidWNrZXQ6CiAgICBpbWFnZTogbWluaW8vbWMKICAgIHJlc3RhcnQ6ICJubyIKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgLSBNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30KICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLW1pbmlvOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnRyeXBvaW50OiBbIi9lbnRyeXBvaW50LnNoIl0KICAgIHZvbHVtZXM6CiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi9lbnRyeXBvaW50LnNoCiAgICAgICAgdGFyZ2V0OiAvZW50cnlwb2ludC5zaAogICAgICAgIGNvbnRlbnQ6IHwKICAgICAgICAgICMhL2Jpbi9zaAogICAgICAgICAgL3Vzci9iaW4vbWMgYWxpYXMgc2V0IHN1cGFiYXNlLW1pbmlvIGh0dHA6Ly9zdXBhYmFzZS1taW5pbzo5MDAwICR7TUlOSU9fUk9PVF9VU0VSfSAke01JTklPX1JPT1RfUEFTU1dPUkR9OwogICAgICAgICAgL3Vzci9iaW4vbWMgbWIgc3VwYWJhc2UtbWluaW8vc3R1YjsKICAgICAgICAgIGV4aXQgMAoKICBzdXBhYmFzZS1zdG9yYWdlOgogICAgaW1hZ2U6IHN1cGFiYXNlL3N0b3JhZ2UtYXBpOnYwLjQ2LjQKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgICMgRGlzYWJsZSB0aGlzIGlmIHlvdSBhcmUgdXNpbmcgYW4gZXh0ZXJuYWwgUG9zdGdyZXMgZGF0YWJhc2UKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBzdXBhYmFzZS1yZXN0OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICAgIGltZ3Byb3h5OgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICBbCiAgICAgICAgICAiQ01EIiwKICAgICAgICAgICJ3Z2V0IiwKICAgICAgICAgICItLW5vLXZlcmJvc2UiLAogICAgICAgICAgIi0tdHJpZXM9MSIsCiAgICAgICAgICAiLS1zcGlkZXIiLAogICAgICAgICAgImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9zdGF0dXMiLAogICAgICAgIF0KICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHJldHJpZXM6IDMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZFUl9QT1JUPTUwMDAKICAgICAgLSBTRVJWRVJfUkVHSU9OPWxvY2FsCiAgICAgIC0gTVVMVElfVEVOQU5UPWZhbHNlCiAgICAgIC0gQVVUSF9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9CiAgICAgIC0gREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vc3VwYWJhc2Vfc3RvcmFnZV9hZG1pbjoke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QCR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9OiR7UE9TVEdSRVNfUE9SVDotNTQzMn0vJHtQT1NUR1JFU19EQjotc3VwYWJhc2V9CiAgICAgIC0gREJfSU5TVEFMTF9ST0xFUz1mYWxzZQogICAgICAtIFNUT1JBR0VfQkFDS0VORD1zMwogICAgICAtIFNUT1JBR0VfUzNfQlVDS0VUPXN0dWIKICAgICAgLSBTVE9SQUdFX1MzX0VORFBPSU5UPWh0dHA6Ly9zdXBhYmFzZS1taW5pbzo5MDAwCiAgICAgIC0gU1RPUkFHRV9TM19GT1JDRV9QQVRIX1NUWUxFPXRydWUKICAgICAgLSBTVE9SQUdFX1MzX1JFR0lPTj11cy1lYXN0LTEKICAgICAgLSBBV1NfQUNDRVNTX0tFWV9JRD0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgLSBBV1NfU0VDUkVUX0FDQ0VTU19LRVk9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFVQTE9BRF9GSUxFX1NJWkVfTElNSVQ9NTI0Mjg4MDAwCiAgICAgIC0gVVBMT0FEX0ZJTEVfU0laRV9MSU1JVF9TVEFOREFSRD01MjQyODgwMDAKICAgICAgLSBVUExPQURfU0lHTkVEX1VSTF9FWFBJUkFUSU9OX1RJTUU9MTIwCiAgICAgIC0gVFVTX1VSTF9QQVRIPS91cGxvYWQvcmVzdW1hYmxlCiAgICAgIC0gVFVTX01BWF9TSVpFPTM2MDAwMDAKICAgICAgLSBJTUFHRV9UUkFOU0ZPUk1BVElPTl9FTkFCTEVEPXRydWUKICAgICAgLSBJTUdQUk9YWV9VUkw9aHR0cDovL2ltZ3Byb3h5OjgwODAKICAgICAgLSBJTUdQUk9YWV9SRVFVRVNUX1RJTUVPVVQ9MTUKICAgICAgLSBEQVRBQkFTRV9TRUFSQ0hfUEFUSD1zdG9yYWdlCgogICAgICAjIC0gQU5PTl9LRVk9ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV3b2dJQ0p5YjJ4bElqb2dJbUZ1YjI0aUxBb2dJQ0pwYzNNaU9pQWljM1Z3WVdKaGMyVWlMQW9nSUNKcFlYUWlPaUF4TnpBNE9UZzROREF3TEFvZ0lDSmxlSEFpT2lBeE9EWTJPRFF4TWpBd0NuMC5qQ0Rxc29YR1Q1OEpuQWpmMjdLT293TlFzb2trazBhUjdyZGJHRzE4UC04CiAgICAgICMgLSBTRVJWSUNFX0tFWT1leUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXdvZ0lDSnliMnhsSWpvZ0luTmxjblpwWTJWZmNtOXNaU0lzQ2lBZ0ltbHpjeUk2SUNKemRYQmhZbUZ6WlNJc0NpQWdJbWxoZENJNklERTNNRGc1T0RnME1EQXNDaUFnSW1WNGNDSTZJREU0TmpZNE5ERXlNREFLZlEuR0E3eUYyQm1xVHpxR2tQX29xRGRKQVFWdDBkampJeEdZdWhFMHpGREpWNAogICAgICAjIC0gUE9TVEdSRVNUX1VSTD1odHRwOi8vc3VwYWJhc2UtcmVzdDozMDAwCiAgICAgICMgLSBQR1JTVF9KV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9CiAgICAgICMgLSBEQVRBQkFTRV9VUkw9cG9zdGdyZXM6Ly9zdXBhYmFzZV9zdG9yYWdlX2FkbWluOiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU31AJHtQT1NUR1JFU19IT1NUOi1zdXBhYmFzZS1kYn06JHtQT1NUR1JFU19QT1JUOi01NDMyfS8ke1BPU1RHUkVTX0RCOi1zdXBhYmFzZX0KICAgICAgIyAtIEZJTEVfU0laRV9MSU1JVD01MjQyODgwMAogICAgICAjIC0gU1RPUkFHRV9CQUNLRU5EPXMzCiAgICAgICMgLSBTVE9SQUdFX1MzX0JVQ0tFVD1zdHViCiAgICAgICMgLSBTVE9SQUdFX1MzX0VORFBPSU5UPWh0dHA6Ly9zdXBhYmFzZS1taW5pbzo5MDAwCiAgICAgICMgLSBTVE9SQUdFX1MzX1BST1RPQ09MPWh0dHAKICAgICAgIyAtIFNUT1JBR0VfUzNfUkVHSU9OPXN0dWIKICAgICAgIyAtIFNUT1JBR0VfUzNfRk9SQ0VfUEFUSF9TVFlMRT10cnVlCiAgICAgICMgLSBBV1NfQUNDRVNTX0tFWV9JRD0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgIyAtIEFXU19TRUNSRVRfQUNDRVNTX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99CiAgICAgICMgLSBBV1NfREVGQVVMVF9SRUdJT049c3R1YgogICAgICAjIC0gRklMRV9TVE9SQUdFX0JBQ0tFTkRfUEFUSD0vdmFyL2xpYi9zdG9yYWdlCiAgICAgICMgLSBURU5BTlRfSUQ9c3R1YgogICAgICAjICMgVE9ETzogaHR0cHM6Ly9naXRodWIuY29tL3N1cGFiYXNlL3N0b3JhZ2UtYXBpL2lzc3Vlcy81NQogICAgICAjIC0gUkVHSU9OPXN0dWIKICAgICAgIyAtIEVOQUJMRV9JTUFHRV9UUkFOU0ZPUk1BVElPTj10cnVlCiAgICAgICMgLSBJTUdQUk9YWV9VUkw9aHR0cDovL2ltZ3Byb3h5OjgwODAKICAgIHZvbHVtZXM6CiAgICAgIC0gLi92b2x1bWVzL3N0b3JhZ2U6L3Zhci9saWIvc3RvcmFnZQogIGltZ3Byb3h5OgogICAgaW1hZ2U6IGRhcnRoc2ltL2ltZ3Byb3h5OnYzLjguMAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IFsiQ01EIiwgImltZ3Byb3h5IiwgImhlYWx0aCJdCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiA1cwogICAgICByZXRyaWVzOiAzCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBJTUdQUk9YWV9MT0NBTF9GSUxFU1lTVEVNX1JPT1Q9LwogICAgICAtIElNR1BST1hZX1VTRV9FVEFHPXRydWUKICAgICAgLSBJTUdQUk9YWV9FTkFCTEVfV0VCUF9ERVRFQ1RJT049JHtJTUdQUk9YWV9FTkFCTEVfV0VCUF9ERVRFQ1RJT046LXRydWV9CiAgICB2b2x1bWVzOgogICAgICAtIC4vdm9sdW1lcy9zdG9yYWdlOi92YXIvbGliL3N0b3JhZ2UKCiAgc3VwYWJhc2UtbWV0YToKICAgIGltYWdlOiBzdXBhYmFzZS9wb3N0Z3Jlcy1tZXRhOnYwLjc5LjAKICAgIGRlcGVuZHNfb246CiAgICAgIHN1cGFiYXNlLWRiOgogICAgICAgICMgRGlzYWJsZSB0aGlzIGlmIHlvdSBhcmUgdXNpbmcgYW4gZXh0ZXJuYWwgUG9zdGdyZXMgZGF0YWJhc2UKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIFBHX01FVEFfUE9SVD04MDgwCiAgICAgIC0gUEdfTUVUQV9EQl9IT1NUPSR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9CiAgICAgIC0gUEdfTUVUQV9EQl9QT1JUPSR7UE9TVEdSRVNfUE9SVDotNTQzMn0KICAgICAgLSBQR19NRVRBX0RCX05BTUU9JHtQT1NUR1JFU19EQjotc3VwYWJhc2V9CiAgICAgIC0gUEdfTUVUQV9EQl9VU0VSPXN1cGFiYXNlX2FkbWluCiAgICAgIC0gUEdfTUVUQV9EQl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CgogIHN1cGFiYXNlLWVkZ2UtZnVuY3Rpb25zOgogICAgaW1hZ2U6IHN1cGFiYXNlL2VkZ2UtcnVudGltZTp2MS4zOC4wCiAgICBkZXBlbmRzX29uOgogICAgICBzdXBhYmFzZS1hbmFseXRpY3M6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIEpXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0KICAgICAgLSBTVVBBQkFTRV9VUkw9aHR0cDovL3N1cGFiYXNlLWtvbmc6ODAwMAogICAgICAtIFNVUEFCQVNFX0FOT05fS0VZPSR7U0VSVklDRV9TVVBBQkFTRUFOT05fS0VZfQogICAgICAtIFNVUEFCQVNFX1NFUlZJQ0VfUk9MRV9LRVk9JHtTRVJWSUNFX1NVUEFCQVNFU0VSVklDRV9LRVl9CiAgICAgIC0gU1VQQUJBU0VfREJfVVJMPXBvc3RncmVzcWw6Ly9wb3N0Z3Jlczoke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9QCR7UE9TVEdSRVNfSE9TVDotc3VwYWJhc2UtZGJ9OiR7UE9TVEdSRVNfUE9SVDotNTQzMn0vJHtQT1NUR1JFU19EQjotc3VwYWJhc2V9CiAgICAgICMgVE9ETzogQWxsb3cgY29uZmlndXJpbmcgVkVSSUZZX0pXVCBwZXIgZnVuY3Rpb24uIFRoaXMgUFIgbWlnaHQgaGVscDogaHR0cHM6Ly9naXRodWIuY29tL3N1cGFiYXNlL2NsaS9wdWxsLzc4NgogICAgICAtIFZFUklGWV9KV1Q9JHtGVU5DVElPTlNfVkVSSUZZX0pXVDotZmFsc2V9CiAgICB2b2x1bWVzOgogICAgICAtIC4vdm9sdW1lcy9mdW5jdGlvbnM6L2hvbWUvZGVuby9mdW5jdGlvbnMKICAgICAgLSB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL3ZvbHVtZXMvZnVuY3Rpb25zL21haW4vaW5kZXgudHMKICAgICAgICB0YXJnZXQ6IC9ob21lL2Rlbm8vZnVuY3Rpb25zL21haW4vaW5kZXgudHMKICAgICAgICBjb250ZW50OiB8CiAgICAgICAgICBpbXBvcnQgeyBzZXJ2ZSB9IGZyb20gJ2h0dHBzOi8vZGVuby5sYW5kL3N0ZEAwLjEzMS4wL2h0dHAvc2VydmVyLnRzJwogICAgICAgICAgaW1wb3J0ICogYXMgam9zZSBmcm9tICdodHRwczovL2Rlbm8ubGFuZC94L2pvc2VAdjQuMTQuNC9pbmRleC50cycKCiAgICAgICAgICBjb25zb2xlLmxvZygnbWFpbiBmdW5jdGlvbiBzdGFydGVkJykKCiAgICAgICAgICBjb25zdCBKV1RfU0VDUkVUID0gRGVuby5lbnYuZ2V0KCdKV1RfU0VDUkVUJykKICAgICAgICAgIGNvbnN0IFZFUklGWV9KV1QgPSBEZW5vLmVudi5nZXQoJ1ZFUklGWV9KV1QnKSA9PT0gJ3RydWUnCgogICAgICAgICAgZnVuY3Rpb24gZ2V0QXV0aFRva2VuKHJlcTogUmVxdWVzdCkgewogICAgICAgICAgICBjb25zdCBhdXRoSGVhZGVyID0gcmVxLmhlYWRlcnMuZ2V0KCdhdXRob3JpemF0aW9uJykKICAgICAgICAgICAgaWYgKCFhdXRoSGVhZGVyKSB7CiAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNaXNzaW5nIGF1dGhvcml6YXRpb24gaGVhZGVyJykKICAgICAgICAgICAgfQogICAgICAgICAgICBjb25zdCBbYmVhcmVyLCB0b2tlbl0gPSBhdXRoSGVhZGVyLnNwbGl0KCcgJykKICAgICAgICAgICAgaWYgKGJlYXJlciAhPT0gJ0JlYXJlcicpIHsKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEF1dGggaGVhZGVyIGlzIG5vdCAnQmVhcmVyIHt0b2tlbn0nYCkKICAgICAgICAgICAgfQogICAgICAgICAgICByZXR1cm4gdG9rZW4KICAgICAgICAgIH0KCiAgICAgICAgICBhc3luYyBmdW5jdGlvbiB2ZXJpZnlKV1Qoand0OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHsKICAgICAgICAgICAgY29uc3QgZW5jb2RlciA9IG5ldyBUZXh0RW5jb2RlcigpCiAgICAgICAgICAgIGNvbnN0IHNlY3JldEtleSA9IGVuY29kZXIuZW5jb2RlKEpXVF9TRUNSRVQpCiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgYXdhaXQgam9zZS5qd3RWZXJpZnkoand0LCBzZWNyZXRLZXkpCiAgICAgICAgICAgIH0gY2F0Y2ggKGVycikgewogICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKQogICAgICAgICAgICAgIHJldHVybiBmYWxzZQogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiB0cnVlCiAgICAgICAgICB9CgogICAgICAgICAgc2VydmUoYXN5bmMgKHJlcTogUmVxdWVzdCkgPT4gewogICAgICAgICAgICBpZiAocmVxLm1ldGhvZCAhPT0gJ09QVElPTlMnICYmIFZFUklGWV9KV1QpIHsKICAgICAgICAgICAgICB0cnkgewogICAgICAgICAgICAgICAgY29uc3QgdG9rZW4gPSBnZXRBdXRoVG9rZW4ocmVxKQogICAgICAgICAgICAgICAgY29uc3QgaXNWYWxpZEpXVCA9IGF3YWl0IHZlcmlmeUpXVCh0b2tlbikKCiAgICAgICAgICAgICAgICBpZiAoIWlzVmFsaWRKV1QpIHsKICAgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeSh7IG1zZzogJ0ludmFsaWQgSldUJyB9KSwgewogICAgICAgICAgICAgICAgICAgIHN0YXR1czogNDAxLAogICAgICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogICAgICAgICAgICAgICAgICB9KQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSkKICAgICAgICAgICAgICAgIHJldHVybiBuZXcgUmVzcG9uc2UoSlNPTi5zdHJpbmdpZnkoeyBtc2c6IGUudG9TdHJpbmcoKSB9KSwgewogICAgICAgICAgICAgICAgICBzdGF0dXM6IDQwMSwKICAgICAgICAgICAgICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sCiAgICAgICAgICAgICAgICB9KQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQoKICAgICAgICAgICAgY29uc3QgdXJsID0gbmV3IFVSTChyZXEudXJsKQogICAgICAgICAgICBjb25zdCB7IHBhdGhuYW1lIH0gPSB1cmwKICAgICAgICAgICAgY29uc3QgcGF0aF9wYXJ0cyA9IHBhdGhuYW1lLnNwbGl0KCcvJykKICAgICAgICAgICAgY29uc3Qgc2VydmljZV9uYW1lID0gcGF0aF9wYXJ0c1sxXQoKICAgICAgICAgICAgaWYgKCFzZXJ2aWNlX25hbWUgfHwgc2VydmljZV9uYW1lID09PSAnJykgewogICAgICAgICAgICAgIGNvbnN0IGVycm9yID0geyBtc2c6ICdtaXNzaW5nIGZ1bmN0aW9uIG5hbWUgaW4gcmVxdWVzdCcgfQogICAgICAgICAgICAgIHJldHVybiBuZXcgUmVzcG9uc2UoSlNPTi5zdHJpbmdpZnkoZXJyb3IpLCB7CiAgICAgICAgICAgICAgICBzdGF0dXM6IDQwMCwKICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogICAgICAgICAgICAgIH0pCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGNvbnN0IHNlcnZpY2VQYXRoID0gYC9ob21lL2Rlbm8vZnVuY3Rpb25zLyR7c2VydmljZV9uYW1lfWAKICAgICAgICAgICAgY29uc29sZS5lcnJvcihgc2VydmluZyB0aGUgcmVxdWVzdCB3aXRoICR7c2VydmljZVBhdGh9YCkKCiAgICAgICAgICAgIGNvbnN0IG1lbW9yeUxpbWl0TWIgPSAxNTAKICAgICAgICAgICAgY29uc3Qgd29ya2VyVGltZW91dE1zID0gMSAqIDYwICogMTAwMAogICAgICAgICAgICBjb25zdCBub01vZHVsZUNhY2hlID0gZmFsc2UKICAgICAgICAgICAgY29uc3QgaW1wb3J0TWFwUGF0aCA9IG51bGwKICAgICAgICAgICAgY29uc3QgZW52VmFyc09iaiA9IERlbm8uZW52LnRvT2JqZWN0KCkKICAgICAgICAgICAgY29uc3QgZW52VmFycyA9IE9iamVjdC5rZXlzKGVudlZhcnNPYmopLm1hcCgoaykgPT4gW2ssIGVudlZhcnNPYmpba11dKQoKICAgICAgICAgICAgdHJ5IHsKICAgICAgICAgICAgICBjb25zdCB3b3JrZXIgPSBhd2FpdCBFZGdlUnVudGltZS51c2VyV29ya2Vycy5jcmVhdGUoewogICAgICAgICAgICAgICAgc2VydmljZVBhdGgsCiAgICAgICAgICAgICAgICBtZW1vcnlMaW1pdE1iLAogICAgICAgICAgICAgICAgd29ya2VyVGltZW91dE1zLAogICAgICAgICAgICAgICAgbm9Nb2R1bGVDYWNoZSwKICAgICAgICAgICAgICAgIGltcG9ydE1hcFBhdGgsCiAgICAgICAgICAgICAgICBlbnZWYXJzLAogICAgICAgICAgICAgIH0pCiAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHdvcmtlci5mZXRjaChyZXEpCiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgICAgICBjb25zdCBlcnJvciA9IHsgbXNnOiBlLnRvU3RyaW5nKCkgfQogICAgICAgICAgICAgIHJldHVybiBuZXcgUmVzcG9uc2UoSlNPTi5zdHJpbmdpZnkoZXJyb3IpLCB7CiAgICAgICAgICAgICAgICBzdGF0dXM6IDUwMCwKICAgICAgICAgICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LAogICAgICAgICAgICAgIH0pCiAgICAgICAgICAgIH0KICAgICAgICAgIH0pCiAgICAgIC0gdHlwZTogYmluZAogICAgICAgIHNvdXJjZTogLi92b2x1bWVzL2Z1bmN0aW9ucy9oZWxsby9pbmRleC50cwogICAgICAgIHRhcmdldDogL2hvbWUvZGVuby9mdW5jdGlvbnMvaGVsbG8vaW5kZXgudHMKICAgICAgICBjb250ZW50OiB8CiAgICAgICAgICAvLyBGb2xsb3cgdGhpcyBzZXR1cCBndWlkZSB0byBpbnRlZ3JhdGUgdGhlIERlbm8gbGFuZ3VhZ2Ugc2VydmVyIHdpdGggeW91ciBlZGl0b3I6CiAgICAgICAgICAvLyBodHRwczovL2Rlbm8ubGFuZC9tYW51YWwvZ2V0dGluZ19zdGFydGVkL3NldHVwX3lvdXJfZW52aXJvbm1lbnQKICAgICAgICAgIC8vIFRoaXMgZW5hYmxlcyBhdXRvY29tcGxldGUsIGdvIHRvIGRlZmluaXRpb24sIGV0Yy4KCiAgICAgICAgICBpbXBvcnQgeyBzZXJ2ZSB9IGZyb20gImh0dHBzOi8vZGVuby5sYW5kL3N0ZEAwLjE3Ny4xL2h0dHAvc2VydmVyLnRzIgoKICAgICAgICAgIHNlcnZlKGFzeW5jICgpID0+IHsKICAgICAgICAgICAgcmV0dXJuIG5ldyBSZXNwb25zZSgKICAgICAgICAgICAgICBgIkhlbGxvIGZyb20gRWRnZSBGdW5jdGlvbnMhImAsCiAgICAgICAgICAgICAgeyBoZWFkZXJzOiB7ICJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiIgfSB9LAogICAgICAgICAgICApCiAgICAgICAgICB9KQoKICAgICAgICAgIC8vIFRvIGludm9rZToKICAgICAgICAgIC8vIGN1cmwgJ2h0dHA6Ly9sb2NhbGhvc3Q6PEtPTkdfSFRUUF9QT1JUPi9mdW5jdGlvbnMvdjEvaGVsbG8nIFwKICAgICAgICAgIC8vICAgLS1oZWFkZXIgJ0F1dGhvcml6YXRpb246IEJlYXJlciA8YW5vbi9zZXJ2aWNlX3JvbGUgQVBJIGtleT4nCgogICAgY29tbWFuZDoKICAgICAgLSBzdGFydAogICAgICAtIC0tbWFpbi1zZXJ2aWNlCiAgICAgIC0gL2hvbWUvZGVuby9mdW5jdGlvbnMvbWFpbgo=",
"tags": [
"firebase",
"alternative",
@@ -802,4 +802,4 @@
"logo": "svgs\/wordpress.svg",
"minversion": "0.0.0"
}
-}
\ No newline at end of file
+}
From a4c164a57efde1529ee7faccedc3a52161d28c01 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Thu, 14 Mar 2024 21:19:37 +0100
Subject: [PATCH 76/97] fix: duplicate dockerfile
---
app/Jobs/ApplicationDeploymentJob.php | 4 ++--
config/sentry.php | 2 +-
config/version.php | 2 +-
versions.json | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 129df7814..f96a68582 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -1128,9 +1128,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->custom_healthcheck_found = false;
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
$this->execute_remote_command([
- executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile', "ignore_errors" => true
+ executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true
]);
- $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
+ $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
if (str($dockerfile)->contains('HEALTHCHECK')) {
$this->custom_healthcheck_found = true;
}
diff --git a/config/sentry.php b/config/sentry.php
index 150b055c3..1fe45fcc9 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.238',
+ 'release' => '4.0.0-beta.239',
// 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 6a881ff74..cf8c3ba8b 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Thu, 14 Mar 2024 23:00:06 +0100
Subject: [PATCH 77/97] fix: $ in env variable feat: multiline envs
---
app/Jobs/ApplicationDeploymentJob.php | 17 ++++++++---
.../Shared/EnvironmentVariable/Show.php | 2 ++
bootstrap/helpers/docker.php | 10 ++++++-
config/sentry.php | 2 +-
config/version.php | 2 +-
.../2024_03_14_214402_add_multiline_envs.php | 28 +++++++++++++++++++
.../environment-variable/show.blade.php | 11 ++++++--
templates/compose/uptime-kuma.yaml | 2 +-
templates/service-templates.json | 2 +-
versions.json | 2 +-
10 files changed, 66 insertions(+), 12 deletions(-)
create mode 100644 database/migrations/2024_03_14_214402_add_multiline_envs.php
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index f96a68582..ca0efd2c3 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -1359,23 +1359,31 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$environment_variables = collect();
if ($this->pull_request_id === 0) {
foreach ($this->application->runtime_environment_variables as $env) {
- $environment_variables->push("$env->key=$env->real_value");
+ $real_value = escapeEnvVariables($env->real_value);
+ $environment_variables->push("$env->key=$real_value");
}
foreach ($this->application->nixpacks_environment_variables as $env) {
- $environment_variables->push("$env->key=$env->real_value");
+ $real_value = escapeEnvVariables($env->real_value);
+ $environment_variables->push("$env->key=$real_value");
}
} else {
foreach ($this->application->runtime_environment_variables_preview as $env) {
- $environment_variables->push("$env->key=$env->real_value");
+ $real_value = escapeEnvVariables($env->real_value);
+ $environment_variables->push("$env->key=$real_value");
}
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
- $environment_variables->push("$env->key=$env->real_value");
+ $real_value = escapeEnvVariables($env->real_value);
+ $environment_variables->push("$env->key=$real_value");
}
}
// Add PORT if not exists, use the first port as default
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
$environment_variables->push("PORT={$ports[0]}");
}
+ // Add HOST if not exists
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('HOST'))->isEmpty()) {
+ $environment_variables->push("HOST=0.0.0.0");
+ }
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('SOURCE_COMMIT'))->isEmpty()) {
if (!is_null($this->commit)) {
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
@@ -1383,6 +1391,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$environment_variables->push("SOURCE_COMMIT=unknown");
}
}
+ ray($environment_variables->all());
return $environment_variables->all();
}
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
index 9252c44f8..7903e8e51 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -21,6 +21,7 @@ class Show extends Component
'env.key' => 'required|string',
'env.value' => 'nullable',
'env.is_build_time' => 'required|boolean',
+ 'env.is_multiline' => 'required|boolean',
'env.is_shown_once' => 'required|boolean',
'env.real_value' => 'nullable',
];
@@ -28,6 +29,7 @@ class Show extends Component
'env.key' => 'Key',
'env.value' => 'Value',
'env.is_build_time' => 'Build Time',
+ 'env.is_multiline' => 'Multiline',
'env.is_shown_once' => 'Shown Once',
];
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 898789adf..aed77a7bb 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -557,7 +557,8 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
return $compose_options->toArray();
}
-function validateComposeFile(string $compose, int $server_id): string|Throwable {
+function validateComposeFile(string $compose, int $server_id): string|Throwable
+{
return 'OK';
try {
$uuid = Str::random(10);
@@ -578,3 +579,10 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable
], $server);
}
}
+
+function escapeEnvVariables($value)
+{
+ $search = array("\\", "\r", "\t", "\x0", '"', "'", "$");
+ $replace = array("\\\\", "\\r", "\\t", "\\0", '\"', "\'", "$$");
+ return str_replace($search, $replace, $value);
+}
diff --git a/config/sentry.php b/config/sentry.php
index 1fe45fcc9..5d46ba7f3 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.239',
+ 'release' => '4.0.0-beta.240',
// 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 cf8c3ba8b..c7e1008fc 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
boolean('is_multiline')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('environment_variables', function (Blueprint $table) {
+ $table->dropColumn('is_multiline');
+ });
+ }
+};
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 5b2c95373..d64490605 100644
--- a/resources/views/livewire/project/shared/environment-variable/show.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php
@@ -20,11 +20,18 @@
@endif
@else
-
-
+ @if ($env->is_multiline)
+
+
+ @else
+
+
+ @endif
@if ($env->is_shared)
@endif
+
@if ($type !== 'service' && !$isSharedVariable)
@endif
diff --git a/templates/compose/uptime-kuma.yaml b/templates/compose/uptime-kuma.yaml
index d5c07684d..cd20d60cb 100644
--- a/templates/compose/uptime-kuma.yaml
+++ b/templates/compose/uptime-kuma.yaml
@@ -8,7 +8,7 @@ services:
uptime-kuma:
image: louislam/uptime-kuma:1
environment:
- - SERVICE_FQDN_3001
+ - SERVICE_FQDN_UPTIME-KUMA_3001
volumes:
- uptime-kuma:/app/data
healthcheck:
diff --git a/templates/service-templates.json b/templates/service-templates.json
index c7a4d91db..76bdf2bd8 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -772,7 +772,7 @@
"uptime-kuma": {
"documentation": "https:\/\/github.com\/louislam\/uptime-kuma?tab=readme-ov-file",
"slogan": "Uptime Kuma is a monitoring tool for tracking the status and performance of your applications in real-time.",
- "compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fMzAwMQogICAgdm9sdW1lczoKICAgICAgLSAndXB0aW1lLWt1bWE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtIGV4dHJhL2hlYWx0aGNoZWNrCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVVBUSU1FLUtVTUFfMzAwMQogICAgdm9sdW1lczoKICAgICAgLSAndXB0aW1lLWt1bWE6L2FwcC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtIGV4dHJhL2hlYWx0aGNoZWNrCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
"monitoring",
"status",
diff --git a/versions.json b/versions.json
index 8f30ba88c..ba197ca84 100644
--- a/versions.json
+++ b/versions.json
@@ -4,7 +4,7 @@
"version": "3.12.36"
},
"v4": {
- "version": "4.0.0-beta.239"
+ "version": "4.0.0-beta.240"
}
}
}
From 73f889ac9fd24238a3cf26af1b499179ba7d8c62 Mon Sep 17 00:00:00 2001
From: Manuel
Date: Fri, 15 Mar 2024 07:11:05 +0100
Subject: [PATCH 78/97] Fix realtime container name and env var
---
templates/compose/supabase.yaml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/templates/compose/supabase.yaml b/templates/compose/supabase.yaml
index 39d9f5f24..0fd2faf90 100644
--- a/templates/compose/supabase.yaml
+++ b/templates/compose/supabase.yaml
@@ -942,6 +942,7 @@ services:
- GOTRUE_SMS_AUTOCONFIRM=${ENABLE_PHONE_AUTOCONFIRM:-true}
supabase-realtime:
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
+ container_name: realtime-dev.supabase-realtime
image: supabase/realtime:v2.25.50
depends_on:
supabase-db:
@@ -961,7 +962,7 @@ services:
- DB_USER=supabase_admin
- DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- DB_NAME=${POSTGRES_DB:-supabase}
- - DB_AFTER_CONNECT_QUERY='SET search_path TO _realtime'
+ - DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime
- DB_ENC_KEY=supabaserealtime
- API_JWT_SECRET=${SERVICE_PASSWORD_JWT}
- FLY_ALLOC_ID=fly123
From 1894573c2fd4aa6ba4e78faeecff7a2c708e9343 Mon Sep 17 00:00:00 2001
From: Manuel
Date: Fri, 15 Mar 2024 09:19:30 +0100
Subject: [PATCH 79/97] Change service name directly
---
templates/compose/supabase.yaml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/templates/compose/supabase.yaml b/templates/compose/supabase.yaml
index 0fd2faf90..0b2d868e2 100644
--- a/templates/compose/supabase.yaml
+++ b/templates/compose/supabase.yaml
@@ -940,9 +940,10 @@ services:
- GOTRUE_EXTERNAL_PHONE_ENABLED=${ENABLE_PHONE_SIGNUP:-true}
- GOTRUE_SMS_AUTOCONFIRM=${ENABLE_PHONE_AUTOCONFIRM:-true}
- supabase-realtime:
+ realtime-dev.supabase-realtime:
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
- container_name: realtime-dev.supabase-realtime
+ # Name of service has to be changed as coolify is generating container names automatically
+ # container_name: realtime-dev.supabase-realtime
image: supabase/realtime:v2.25.50
depends_on:
supabase-db:
From 350e32326fa79075dad1677281450cc88f87960f Mon Sep 17 00:00:00 2001
From: Manuel
Date: Fri, 15 Mar 2024 13:25:49 +0100
Subject: [PATCH 80/97] Fix app name of realtime for logging
---
templates/compose/supabase.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/compose/supabase.yaml b/templates/compose/supabase.yaml
index 0b2d868e2..af61c35db 100644
--- a/templates/compose/supabase.yaml
+++ b/templates/compose/supabase.yaml
@@ -663,7 +663,7 @@ services:
kong: 'starts_with(string!(.appname), "supabase-kong")'
auth: 'starts_with(string!(.appname), "supabase-auth")'
rest: 'starts_with(string!(.appname), "supabase-rest")'
- realtime: 'starts_with(string!(.appname), "supabase-realtime")'
+ realtime: 'starts_with(string!(.appname), "realtime-dev.supabase-realtime")'
storage: 'starts_with(string!(.appname), "supabase-storage")'
functions: 'starts_with(string!(.appname), "supabase-functions")'
db: 'starts_with(string!(.appname), "supabase-db")'
From 657c7d8cff3775359bbe26fb9774d3887b0ce0c8 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Fri, 15 Mar 2024 20:40:50 +0100
Subject: [PATCH 81/97] Refactor form components
Update Input and Textarea components to use nullable type declarations and remove unused imports.
---
app/View/Components/Forms/Input.php | 22 +++++++++++-----------
app/View/Components/Forms/Textarea.php | 26 +++++++++++++-------------
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/app/View/Components/Forms/Input.php b/app/View/Components/Forms/Input.php
index 09cee0338..93598cea5 100644
--- a/app/View/Components/Forms/Input.php
+++ b/app/View/Components/Forms/Input.php
@@ -10,17 +10,17 @@ use Visus\Cuid2\Cuid2;
class Input extends Component
{
public function __construct(
- public string|null $id = null,
- public string|null $name = null,
- public string|null $type = 'text',
- public string|null $value = null,
- public string|null $label = null,
- public bool $required = false,
- public bool $disabled = false,
- public bool $readonly = false,
- public string|null $helper = null,
- public bool $allowToPeak = true,
- public string $defaultClass = "input input-sm bg-coolgray-100 rounded text-white w-full disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
+ public ?string $id = null,
+ public ?string $name = null,
+ public ?string $type = 'text',
+ public ?string $value = null,
+ public ?string $label = null,
+ public bool $required = false,
+ public bool $disabled = false,
+ public bool $readonly = false,
+ public ?string $helper = null,
+ public bool $allowToPeak = true,
+ public string $defaultClass = "input input-sm bg-coolgray-100 rounded text-white w-full disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
) {
}
diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php
index 50ffe77f7..aa8d874ad 100644
--- a/app/View/Components/Forms/Textarea.php
+++ b/app/View/Components/Forms/Textarea.php
@@ -4,7 +4,6 @@ namespace App\View\Components\Forms;
use Closure;
use Illuminate\Contracts\View\View;
-use Illuminate\Support\Str;
use Illuminate\View\Component;
use Visus\Cuid2\Cuid2;
@@ -14,18 +13,19 @@ class Textarea extends Component
* Create a new component instance.
*/
public function __construct(
- public string|null $id = null,
- public string|null $name = null,
- public string|null $type = 'text',
- public string|null $value = null,
- public string|null $label = null,
- public string|null $placeholder = null,
- public bool $required = false,
- public bool $disabled = false,
- public bool $readonly = false,
- public string|null $helper = null,
- public bool $realtimeValidation = false,
- public string $defaultClass = "textarea leading-normal bg-coolgray-100 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
+ public ?string $id = null,
+ public ?string $name = null,
+ public ?string $type = 'text',
+ public ?string $value = null,
+ public ?string $label = null,
+ public ?string $placeholder = null,
+ public bool $required = false,
+ public bool $disabled = false,
+ public bool $readonly = false,
+ public ?string $helper = null,
+ public bool $realtimeValidation = false,
+ // public bool $allowToPeak = true,
+ public string $defaultClass = "textarea leading-normal bg-coolgray-100 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
) {
//
}
From 3ea3674407ee8b57840b93798f3be143443db9ba Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Fri, 15 Mar 2024 22:02:37 +0100
Subject: [PATCH 82/97] fix: multiline env variables
---
app/Livewire/Project/Edit.php | 5 ++
app/Livewire/Project/EnvironmentEdit.php | 5 ++
.../Shared/EnvironmentVariable/Add.php | 5 ++
.../Shared/EnvironmentVariable/All.php | 11 ++++-
app/Livewire/TeamSharedVariablesIndex.php | 5 ++
app/Models/EnvironmentVariable.php | 18 +++++--
app/View/Components/Forms/Textarea.php | 5 +-
.../2024_03_14_214402_add_multiline_envs.php | 6 +++
.../views/components/forms/input.blade.php | 6 +--
.../views/components/forms/textarea.blade.php | 47 ++++++++++++++++---
resources/views/layouts/base.blade.php | 4 +-
.../shared/environment-variable/add.blade.php | 6 ++-
.../environment-variable/show.blade.php | 8 ++--
13 files changed, 106 insertions(+), 25 deletions(-)
diff --git a/app/Livewire/Project/Edit.php b/app/Livewire/Project/Edit.php
index b2cda7a47..a80b1af76 100644
--- a/app/Livewire/Project/Edit.php
+++ b/app/Livewire/Project/Edit.php
@@ -17,9 +17,14 @@ class Edit extends Component
public function saveKey($data)
{
try {
+ $found = $this->project->environment_variables()->where('key', $data['key'])->first();
+ if ($found) {
+ throw new \Exception('Variable already exists.');
+ }
$this->project->environment_variables()->create([
'key' => $data['key'],
'value' => $data['value'],
+ 'is_multiline' => $data['is_multiline'],
'type' => 'project',
'team_id' => currentTeam()->id,
]);
diff --git a/app/Livewire/Project/EnvironmentEdit.php b/app/Livewire/Project/EnvironmentEdit.php
index 276aa58df..c5e4ccf11 100644
--- a/app/Livewire/Project/EnvironmentEdit.php
+++ b/app/Livewire/Project/EnvironmentEdit.php
@@ -21,9 +21,14 @@ class EnvironmentEdit extends Component
public function saveKey($data)
{
try {
+ $found = $this->environment->environment_variables()->where('key', $data['key'])->first();
+ if ($found) {
+ throw new \Exception('Variable already exists.');
+ }
$this->environment->environment_variables()->create([
'key' => $data['key'],
'value' => $data['value'],
+ 'is_multiline' => $data['is_multiline'],
'type' => 'environment',
'team_id' => currentTeam()->id,
]);
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
index 923f0d455..c04242963 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
@@ -11,17 +11,20 @@ class Add extends Component
public string $key;
public ?string $value = null;
public bool $is_build_time = false;
+ public bool $is_multiline = false;
protected $listeners = ['clearAddEnv' => 'clear'];
protected $rules = [
'key' => 'required|string',
'value' => 'nullable',
'is_build_time' => 'required|boolean',
+ 'is_multiline' => 'required|boolean',
];
protected $validationAttributes = [
'key' => 'key',
'value' => 'value',
'is_build_time' => 'build',
+ 'is_multiline' => 'multiline',
];
public function mount()
@@ -43,6 +46,7 @@ class Add extends Component
'key' => $this->key,
'value' => $this->value,
'is_build_time' => $this->is_build_time,
+ 'is_multiline' => $this->is_multiline,
'is_preview' => $this->is_preview,
]);
$this->clear();
@@ -53,5 +57,6 @@ class Add extends Component
$this->key = '';
$this->value = '';
$this->is_build_time = false;
+ $this->is_multiline = false;
}
}
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
index 3a304d3e9..53dd0ffe3 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -11,7 +11,7 @@ class All extends Component
{
public $resource;
public bool $showPreview = false;
- public string|null $modalId = null;
+ public ?string $modalId = null;
public ?string $variables = null;
public ?string $variablesPreview = null;
public string $view = 'normal';
@@ -34,6 +34,9 @@ class All extends Component
if ($item->is_shown_once) {
return "$item->key=(locked secret)";
}
+ if ($item->is_multiline) {
+ return "$item->key=(multiline, edit in normal view)";
+ }
return "$item->key=$item->value";
})->sort()->join('
');
@@ -42,6 +45,9 @@ class All extends Component
if ($item->is_shown_once) {
return "$item->key=(locked secret)";
}
+ if ($item->is_multiline) {
+ return "$item->key=(multiline, edit in normal view)";
+ }
return "$item->key=$item->value";
})->sort()->join('
');
@@ -67,7 +73,7 @@ class All extends Component
$found = $this->resource->environment_variables()->where('key', $key)->first();
}
if ($found) {
- if ($found->is_shown_once) {
+ if ($found->is_shown_once || $found->is_multiline) {
continue;
}
$found->value = $variable;
@@ -144,6 +150,7 @@ class All extends Component
$environment->key = $data['key'];
$environment->value = $data['value'];
$environment->is_build_time = $data['is_build_time'];
+ $environment->is_multiline = $data['is_multiline'];
$environment->is_preview = $data['is_preview'];
switch ($this->resource->type()) {
diff --git a/app/Livewire/TeamSharedVariablesIndex.php b/app/Livewire/TeamSharedVariablesIndex.php
index d2776e8b2..dbb64ab65 100644
--- a/app/Livewire/TeamSharedVariablesIndex.php
+++ b/app/Livewire/TeamSharedVariablesIndex.php
@@ -13,9 +13,14 @@ class TeamSharedVariablesIndex extends Component
public function saveKey($data)
{
try {
+ $found = $this->team->environment_variables()->where('key', $data['key'])->first();
+ if ($found) {
+ throw new \Exception('Variable already exists.');
+ }
$this->team->environment_variables()->create([
'key' => $data['key'],
'value' => $data['value'],
+ 'is_multiline' => $data['is_multiline'],
'type' => 'team',
'team_id' => currentTeam()->id,
]);
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index a3611a0c6..41fe40c58 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -49,7 +49,7 @@ class EnvironmentVariable extends Model
set: fn (?string $value = null) => $this->set_environment_variables($value),
);
}
- public function realValue(): Attribute
+ public function resource()
{
$resource = null;
if ($this->application_id) {
@@ -71,9 +71,19 @@ class EnvironmentVariable extends Model
}
}
}
+ return $resource;
+ }
+ public function realValue(): Attribute
+ {
+ $resource = $this->resource();
return Attribute::make(
get: function () use ($resource) {
- return $this->get_real_environment_variables($this->value, $resource);
+ $env = $this->get_real_environment_variables($this->value, $resource);
+ return data_get($env, 'value', $env);
+ if (is_string($env)) {
+ return $env;
+ }
+ return $env->value;
}
);
}
@@ -89,7 +99,7 @@ class EnvironmentVariable extends Model
}
);
}
- private function get_real_environment_variables(?string $environment_variable = null, $resource = null): string|null
+ private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
{
if (!$environment_variable || !$resource) {
return null;
@@ -112,7 +122,7 @@ class EnvironmentVariable extends Model
}
$environment_variable_found = SharedEnvironmentVariable::where("type", $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
if ($environment_variable_found) {
- return $environment_variable_found->value;
+ return $environment_variable_found;
}
}
return $environment_variable;
diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php
index aa8d874ad..8c50af533 100644
--- a/app/View/Components/Forms/Textarea.php
+++ b/app/View/Components/Forms/Textarea.php
@@ -24,8 +24,9 @@ class Textarea extends Component
public bool $readonly = false,
public ?string $helper = null,
public bool $realtimeValidation = false,
- // public bool $allowToPeak = true,
- public string $defaultClass = "textarea leading-normal bg-coolgray-100 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
+ public bool $allowToPeak = true,
+ public string $defaultClass = "textarea leading-normal bg-coolgray-100 rounded text-white w-full scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50",
+ public string $defaultClassInput = "input input-sm bg-coolgray-100 rounded text-white w-full disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
) {
//
}
diff --git a/database/migrations/2024_03_14_214402_add_multiline_envs.php b/database/migrations/2024_03_14_214402_add_multiline_envs.php
index c8d809927..b3e869bc3 100644
--- a/database/migrations/2024_03_14_214402_add_multiline_envs.php
+++ b/database/migrations/2024_03_14_214402_add_multiline_envs.php
@@ -14,6 +14,9 @@ return new class extends Migration
Schema::table('environment_variables', function (Blueprint $table) {
$table->boolean('is_multiline')->default(false);
});
+ Schema::table('shared_environment_variables', function (Blueprint $table) {
+ $table->boolean('is_multiline')->default(false);
+ });
}
/**
@@ -24,5 +27,8 @@ return new class extends Migration
Schema::table('environment_variables', function (Blueprint $table) {
$table->dropColumn('is_multiline');
});
+ Schema::table('shared_environment_variables', function (Blueprint $table) {
+ $table->dropColumn('is_multiline');
+ });
}
};
diff --git a/resources/views/components/forms/input.blade.php b/resources/views/components/forms/input.blade.php
index c13de00c1..5cabb3c70 100644
--- a/resources/views/components/forms/input.blade.php
+++ b/resources/views/components/forms/input.blade.php
@@ -1,4 +1,4 @@
-
+
@if ($label)
{{ $label }}
@if ($required)
@@ -10,7 +10,7 @@
@endif
@if ($type === 'password')
-