From 91acd4cb6ab6493be27488ccbc8ad97e87f8d13c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 09:34:35 +0200
Subject: [PATCH 01/29] fix: backups should be done with internal db url fix:
create default database on mongodb start with a collection
---
app/Actions/Database/StartMongodb.php | 15 +++++++++++++++
app/Jobs/DatabaseBackupJob.php | 2 +-
app/Models/StandaloneMongodb.php | 16 ++++++++++++----
app/Models/StandalonePostgresql.php | 4 ++--
config/sentry.php | 4 ++--
config/version.php | 2 +-
versions.json | 2 +-
7 files changed, 34 insertions(+), 11 deletions(-)
diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php
index fbdf0bba4..645ed6ee9 100644
--- a/app/Actions/Database/StartMongodb.php
+++ b/app/Actions/Database/StartMongodb.php
@@ -94,6 +94,14 @@ public function handle(StandaloneMongodb $database)
];
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
}
+ $this->add_default_database();
+ $docker_compose['services'][$container_name]['volumes'][] = [
+ 'type' => 'bind',
+ 'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
+ 'target' => '/docker-entrypoint-initdb.d',
+ 'read_only' => true,
+ ];
+
$docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose);
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
@@ -160,4 +168,11 @@ private function add_custom_mongo_conf()
$content_base64 = base64_encode($content);
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
}
+ private function add_default_database()
+ {
+ $content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
+ $content_base64 = base64_encode($content);
+ $this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
+ $this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
+ }
}
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index 9104434ea..84aad1b6c 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -170,7 +170,7 @@ public function handle(): void
private function backup_standalone_mongodb(string $databaseWithCollections): void
{
try {
- $url = $this->database->getDbUrl();
+ $url = $this->database->getDbUrl(useInternal: true);
if ($databaseWithCollections === 'all') {
$commands[] = "mkdir -p " . $this->backup_dir;
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php
index 56c644481..6d9158a64 100644
--- a/app/Models/StandaloneMongodb.php
+++ b/app/Models/StandaloneMongodb.php
@@ -15,8 +15,16 @@ protected static function booted()
{
static::created(function ($database) {
LocalPersistentVolume::create([
- 'name' => 'mongodb-data-' . $database->uuid,
- 'mount_path' => '/data',
+ 'name' => 'mongodb-configdb-' . $database->uuid,
+ 'mount_path' => '/data/configdb',
+ 'host_path' => null,
+ 'resource_id' => $database->id,
+ 'resource_type' => $database->getMorphClass(),
+ 'is_readonly' => true
+ ]);
+ LocalPersistentVolume::create([
+ 'name' => 'mongodb-db-' . $database->uuid,
+ 'mount_path' => '/data/db',
'host_path' => null,
'resource_id' => $database->id,
'resource_type' => $database->getMorphClass(),
@@ -55,8 +63,8 @@ public function type(): string
{
return 'standalone-mongodb';
}
- public function getDbUrl() {
- if ($this->is_public) {
+ public function getDbUrl(bool $useInternal = false) {
+ if ($this->is_public && !$useInternal) {
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
} else {
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true";
diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php
index 669b43f58..3a0432180 100644
--- a/app/Models/StandalonePostgresql.php
+++ b/app/Models/StandalonePostgresql.php
@@ -62,9 +62,9 @@ public function type(): string
{
return 'standalone-postgresql';
}
- public function getDbUrl(): string
+ public function getDbUrl(bool $useInternal = false): string
{
- if ($this->is_public) {
+ if ($this->is_public && !$useInternal) {
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";
} else {
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}";
diff --git a/config/sentry.php b/config/sentry.php
index a7d0a2a84..b8cd87fa3 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -3,11 +3,11 @@
return [
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
- 'dsn' => 'https://72f02655749d5d687297b6b9f078b8b9@o1082494.ingest.sentry.io/4505347448045568',
+ 'dsn' => 'https://c35fe90ee56e18b220bb55e8217d4839@o1082494.ingest.sentry.io/4505347448045568',
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.98',
+ 'release' => '4.0.0-beta.99',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index 36134c947..63857a361 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Tue, 24 Oct 2023 10:10:45 +0200
Subject: [PATCH 02/29] fix: clone to with the same environment name
---
app/Http/Livewire/Project/CloneProject.php | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/Http/Livewire/Project/CloneProject.php b/app/Http/Livewire/Project/CloneProject.php
index b271a7970..e12138d10 100644
--- a/app/Http/Livewire/Project/CloneProject.php
+++ b/app/Http/Livewire/Project/CloneProject.php
@@ -55,18 +55,21 @@ public function clone()
'selectedServer' => 'required',
'newProjectName' => 'required',
]);
+ $foundProject = Project::where('name', $this->newProjectName)->first();
+ if ($foundProject) {
+ throw new \Exception('Project with the same name already exists.');
+ }
$newProject = Project::create([
'name' => $this->newProjectName,
'team_id' => currentTeam()->id,
'description' => $this->project->description . ' (clone)',
]);
- if ($this->environment->id !== 1) {
+ if ($this->environment->name !== 'production') {
$newProject->environments()->create([
'name' => $this->environment->name,
]);
- $newProject->environments()->find(1)->delete();
}
- $newEnvironment = $newProject->environments->first();
+ $newEnvironment = $newProject->environments->where('name', $this->environment->name)->first();
// Clone Applications
$applications = $this->environment->applications;
$databases = $this->environment->databases();
@@ -80,7 +83,6 @@ public function clone()
'environment_id' => $newEnvironment->id,
'destination_id' => $this->selectedServer,
]);
- $newApplication->environment_id = $newProject->environments->first()->id;
$newApplication->save();
$environmentVaribles = $application->environment_variables()->get();
foreach ($environmentVaribles as $environmentVarible) {
@@ -105,7 +107,6 @@ public function clone()
'environment_id' => $newEnvironment->id,
'destination_id' => $this->selectedServer,
]);
- $newDatabase->environment_id = $newProject->environments->first()->id;
$newDatabase->save();
$environmentVaribles = $database->environment_variables()->get();
foreach ($environmentVaribles as $environmentVarible) {
@@ -128,7 +129,6 @@ public function clone()
'environment_id' => $newEnvironment->id,
'destination_id' => $this->selectedServer,
]);
- $newService->environment_id = $newProject->environments->first()->id;
$newService->save();
$newService->parse();
}
From 554222abc71d139edf2cb10cc9934906e998b0b0 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:10:55 +0200
Subject: [PATCH 03/29] fix: cleanup stucked resources on start
---
app/Console/Commands/Init.php | 73 +++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php
index 446b0c693..6abac7029 100644
--- a/app/Console/Commands/Init.php
+++ b/app/Console/Commands/Init.php
@@ -3,7 +3,12 @@
namespace App\Console\Commands;
use App\Enums\ApplicationDeploymentStatus;
+use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
+use App\Models\Service;
+use App\Models\StandaloneMongodb;
+use App\Models\StandalonePostgresql;
+use App\Models\StandaloneRedis;
use Illuminate\Console\Command;
class Init extends Command
@@ -13,7 +18,9 @@ class Init extends Command
public function handle()
{
+ ray()->clearAll();
$this->cleanup_in_progress_application_deployments();
+ $this->cleanup_stucked_resources();
}
private function cleanup_in_progress_application_deployments()
@@ -30,4 +37,70 @@ private function cleanup_in_progress_application_deployments()
echo "Error: {$e->getMessage()}\n";
}
}
+ private function cleanup_stucked_resources() {
+ // Cleanup any resources that are not attached to any environment or destination or server
+ try {
+ $applications = Application::all();
+ foreach($applications as $application) {
+ if (!$application->environment) {
+ ray('Application without environment', $application->name);
+ $application->delete();
+ }
+ if (!$application->destination()) {
+ ray('Application without destination', $application->name);
+ $application->delete();
+ }
+ }
+ $postgresqls = StandalonePostgresql::all();
+ foreach($postgresqls as $postgresql) {
+ if (!$postgresql->environment) {
+ ray('Postgresql without environment', $postgresql->name);
+ $postgresql->delete();
+ }
+ if (!$postgresql->destination()) {
+ ray('Postgresql without destination', $postgresql->name);
+ $postgresql->delete();
+ }
+ }
+ $redis = StandaloneRedis::all();
+ foreach($redis as $redis) {
+ if (!$redis->environment) {
+ ray('Redis without environment', $redis->name);
+ $redis->delete();
+ }
+ if (!$redis->destination()) {
+ ray('Redis without destination', $redis->name);
+ $redis->delete();
+ }
+ }
+ $mongodbs = StandaloneMongodb::all();
+ foreach($mongodbs as $mongodb) {
+ if (!$mongodb->environment) {
+ ray('Mongodb without environment', $mongodb->name);
+ $mongodb->delete();
+ }
+ if (!$mongodb->destination()) {
+ ray('Mongodb without destination', $mongodb->name);
+ $mongodb->delete();
+ }
+ }
+ $services = Service::all();
+ foreach($services as $service) {
+ if (!$service->environment) {
+ ray('Service without environment', $service->name);
+ $service->delete();
+ }
+ if (!$service->server) {
+ ray('Service without server', $service->name);
+ $service->delete();
+ }
+ if (!$service->destination()) {
+ ray('Service without destination', $service->name);
+ $service->delete();
+ }
+ }
+ } catch (\Throwable $e) {
+ echo "Error: {$e->getMessage()}\n";
+ }
+ }
}
From 8bfc1a7c06d6b9d848805905ef22ffe653ad08b6 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:11:21 +0200
Subject: [PATCH 04/29] fix: do not allow to delete env if a resource is
defined
---
app/Http/Livewire/Project/DeleteEnvironment.php | 8 ++++----
app/Models/Environment.php | 2 +-
app/Models/Project.php | 2 +-
resources/views/project/resources.blade.php | 12 ++++++------
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/app/Http/Livewire/Project/DeleteEnvironment.php b/app/Http/Livewire/Project/DeleteEnvironment.php
index f341d7cb5..0b6254b3b 100644
--- a/app/Http/Livewire/Project/DeleteEnvironment.php
+++ b/app/Http/Livewire/Project/DeleteEnvironment.php
@@ -21,10 +21,10 @@ public function delete()
'environment_id' => 'required|int',
]);
$environment = Environment::findOrFail($this->environment_id);
- if ($environment->applications->count() > 0) {
- return $this->emit('error', 'Environment has resources defined, please delete them first.');
+ if ($environment->isEmpty()) {
+ $environment->delete();
+ return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
}
- $environment->delete();
- return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
+ return $this->emit('error', 'Environment has defined resources, please delete them first.');
}
}
diff --git a/app/Models/Environment.php b/app/Models/Environment.php
index 8f67ed004..941586edd 100644
--- a/app/Models/Environment.php
+++ b/app/Models/Environment.php
@@ -12,7 +12,7 @@ class Environment extends Model
'project_id',
];
- public function can_delete_environment()
+ public function isEmpty()
{
return $this->applications()->count() == 0 &&
$this->redis()->count() == 0 &&
diff --git a/app/Models/Project.php b/app/Models/Project.php
index f8f9622b8..a910348b4 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -18,7 +18,7 @@ protected static function booted()
'project_id' => $project->id,
]);
Environment::create([
- 'name' => 'Production',
+ 'name' => 'production',
'project_id' => $project->id,
]);
});
diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php
index da726e658..ef2780719 100644
--- a/resources/views/project/resources.blade.php
+++ b/resources/views/project/resources.blade.php
@@ -2,17 +2,17 @@
Resources
- @if ($environment->can_delete_environment())
+
+ Clone
+
+ @if ($environment->isEmpty())
@else
+
New
@endif
-
- Clone
-
@@ -36,7 +36,7 @@ class="font-normal text-white normal-case border-none rounded hover:no-underline
- @if ($environment->can_delete_environment())
+ @if ($environment->isEmpty())
+ Add New Resource
@endif
From 69691b2ca7929d8f0b4a22e66f7f5eeb86c2049a Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:19:12 +0200
Subject: [PATCH 05/29] fix: service template generator + appwrite
---
app/Console/Commands/GenerateServiceTemplates.php | 4 +++-
app/Http/Livewire/Project/Service/ComposeModal.php | 4 ++--
templates/service-templates.json | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/Console/Commands/GenerateServiceTemplates.php b/app/Console/Commands/GenerateServiceTemplates.php
index 6220a3774..bf271e8ad 100644
--- a/app/Console/Commands/GenerateServiceTemplates.php
+++ b/app/Console/Commands/GenerateServiceTemplates.php
@@ -89,7 +89,9 @@ private function process_file($file)
'compose' => $yaml,
];
if ($env_file) {
- $payload['envs'] = $env_file;
+ $env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
+ $env_file_base64 = base64_encode($env_file_content);
+ $payload['envs'] = $env_file_base64;
}
return $payload;
}
diff --git a/app/Http/Livewire/Project/Service/ComposeModal.php b/app/Http/Livewire/Project/Service/ComposeModal.php
index 0c9f5e98f..4203f4507 100644
--- a/app/Http/Livewire/Project/Service/ComposeModal.php
+++ b/app/Http/Livewire/Project/Service/ComposeModal.php
@@ -6,8 +6,8 @@
class ComposeModal extends Component
{
- public string $raw;
- public string $actual;
+ public ?string $raw = null;
+ public ?string $actual = null;
public function render()
{
return view('livewire.project.service.compose-modal');
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 7d3136784..33576e92b 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -8,7 +8,7 @@
"documentation": "https:\/\/appwrite.io\/docs",
"slogan": "Appwrite is a self-hosted backend-as-a-service platform that simplifies the development of web and mobile applications by providing a range of features and APIs.",
"compose": "eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnZlcnNpb246ICczJwpzZXJ2aWNlczoKICBhcHB3cml0ZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40JwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBsYWJlbHM6CiAgICAgIC0gdHJhZWZpay5jb25zdHJhaW50LWxhYmVsLXN0YWNrPWFwcHdyaXRlCiAgICAgIC0gdHJhZWZpay5kb2NrZXIubmV0d29yaz1hcHB3cml0ZQogICAgICAtIHRyYWVmaWsuaHR0cC5zZXJ2aWNlcy5hcHB3cml0ZV9hcGkubG9hZGJhbGFuY2VyLnNlcnZlci5wb3J0PTgwCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfYXBpX2h0dHAuZW50cnlwb2ludHM9d2ViCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfYXBpX2h0dHAucnVsZT1QYXRoUHJlZml4KGAvYCkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cC5zZXJ2aWNlPWFwcHdyaXRlX2FwaQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX2FwaV9odHRwcy5lbnRyeXBvaW50cz13ZWJzZWN1cmUKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMucnVsZT1QYXRoUHJlZml4KGAvYCkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMuc2VydmljZT1hcHB3cml0ZV9hcGkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMudGxzPXRydWUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICAgICAgLSByZWRpcwogICAgICAtIGluZmx1eGRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQVBQV1JJVEU9LwogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0xPQ0FMRQogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfUk9PVAogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfRU1BSUxTCiAgICAgIC0gX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFMKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1NZU1RFTV9SRVNQT05TRV9GT1JNQVQKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFMKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlMKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfSU5GTFVYREJfSE9TVAogICAgICAtIF9BUFBfSU5GTFVYREJfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU1JVAogICAgICAtIF9BUFBfU1RPUkFHRV9QUkVWSUVXX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVUwogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVAogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0NQVVMKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19NRU1PUlkKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfU1RBVFNEX0hPU1QKICAgICAgLSBfQVBQX1NUQVRTRF9QT1JUCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0VYRUNVVElPTgogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0NBQ0hFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQUJVU0UKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUwogICAgICAtIF9BUFBfU01TX1BST1ZJREVSCiAgICAgIC0gX0FQUF9TTVNfRlJPTQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfQkFUQ0hfU0laRQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfQ09NUExFWElUWQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfREVQVEgKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1dFQkhPT0tfU0VDUkVUCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0NMSUVOVF9TRUNSRVQKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfU0VDUkVUCiAgICAgIC0gX0FQUF9BU1NJU1RBTlRfT1BFTkFJX0FQSV9LRVkKICBhcHB3cml0ZS1yZWFsdGltZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiByZWFsdGltZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlYWx0aW1lCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBsYWJlbHM6CiAgICAgIC0gdHJhZWZpay5jb25zdHJhaW50LWxhYmVsLXN0YWNrPWFwcHdyaXRlCiAgICAgIC0gdHJhZWZpay5kb2NrZXIubmV0d29yaz1hcHB3cml0ZQogICAgICAtIHRyYWVmaWsuaHR0cC5zZXJ2aWNlcy5hcHB3cml0ZV9yZWFsdGltZS5sb2FkYmFsYW5jZXIuc2VydmVyLnBvcnQ9ODAKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93cy5lbnRyeXBvaW50cz13ZWIKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93cy5ydWxlPVBhdGhQcmVmaXgoYC92MS9yZWFsdGltZWApCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3Muc2VydmljZT1hcHB3cml0ZV9yZWFsdGltZQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX3JlYWx0aW1lX3dzcy5lbnRyeXBvaW50cz13ZWJzZWN1cmUKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93c3MucnVsZT1QYXRoUHJlZml4KGAvdjEvcmVhbHRpbWVgKQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX3JlYWx0aW1lX3dzcy5zZXJ2aWNlPWFwcHdyaXRlX3JlYWx0aW1lCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3NzLnRscz10cnVlCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3NzLnRscy5jZXJ0cmVzb2x2ZXI9ZG5zCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSBtYXJpYWRiCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BUFBXUklURT0vdjEvcmVhbHRpbWUKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUFRJT05TX0FCVVNFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItYXVkaXRzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1hdWRpdHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYXVkaXRzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci13ZWJob29rczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItd2ViaG9va3MKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItd2ViaG9va3MKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgICAtIG1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZGVsZXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItZGVsZXRlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kZWxldGVzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS11cGxvYWRzOi9zdG9yYWdlL3VwbG9hZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNhY2hlOi9zdG9yYWdlL2NhY2hlOnJ3JwogICAgICAtICdhcHB3cml0ZS1mdW5jdGlvbnM6L3N0b3JhZ2UvZnVuY3Rpb25zOnJ3JwogICAgICAtICdhcHB3cml0ZS1idWlsZHM6L3N0b3JhZ2UvYnVpbGRzOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIF9BUFBfRVhFQ1VUT1JfSE9TVAogIGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogd29ya2VyLWRhdGFiYXNlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXMKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgICAtIG1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtd29ya2VyLWJ1aWxkczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItYnVpbGRzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWJ1aWxkcwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBkZXBlbmRzX29uOgogICAgICAtIHJlZGlzCiAgICAgIC0gbWFyaWFkYgogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9PUFRJT05TX0ZPUkNFX0hUVFBTCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1jZXJ0aWZpY2F0ZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS1jb25maWc6L3N0b3JhZ2UvY29uZmlnOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICAgIC0gb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9ET0NLRVJfSFVCX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogIGFwcHdyaXRlLXdvcmtlci1tYWlsczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWFpbHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWFpbHMKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmc6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogd29ya2VyLW1lc3NhZ2luZwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01TX1BST1ZJREVSCiAgICAgIC0gX0FQUF9TTVNfRlJPTQogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItbWlncmF0aW9uczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWlncmF0aW9ucwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQKICBhcHB3cml0ZS1tYWludGVuYW5jZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiBtYWludGVuYW5jZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1haW50ZW5hbmNlCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICBhcHB3cml0ZS11c2FnZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB1c2FnZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXVzYWdlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gaW5mbHV4ZGIKICAgICAgLSBtYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9JTkZMVVhEQl9IT1NUCiAgICAgIC0gX0FQUF9JTkZMVVhEQl9QT1JUCiAgICAgIC0gX0FQUF9VU0FHRV9BR0dSRUdBVElPTl9JTlRFUlZBTAogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtc2NoZWR1bGU6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogc2NoZWR1bGUKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLWFzc2lzdGFudDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXNzaXN0YW50OjAuMi4xJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWFzc2lzdGFudAogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0FTU0lTVEFOVF9PUEVOQUlfQVBJX0tFWQogIG9wZW5ydW50aW1lcy1leGVjdXRvcjoKICAgIGNvbnRhaW5lcl9uYW1lOiBvcGVucnVudGltZXMtZXhlY3V0b3IKICAgIGhvc3RuYW1lOiBhcHB3cml0ZS1leGVjdXRvcgogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgc3RvcF9zaWduYWw6IFNJR0lOVAogICAgaW1hZ2U6ICdvcGVucnVudGltZXMvZXhlY3V0b3I6MC40LjEnCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgICAtIHJ1bnRpbWVzCiAgICB2b2x1bWVzOgogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnL3RtcDovdG1wOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gT1BSX0VYRUNVVE9SX0lOQUNUSVZFX1RSRVNIT0xEPSRfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQKICAgICAgLSBPUFJfRVhFQ1VUT1JfTUFJTlRFTkFOQ0VfSU5URVJWQUw9JF9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMCiAgICAgIC0gT1BSX0VYRUNVVE9SX05FVFdPUks9JF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUksKICAgICAgLSBPUFJfRVhFQ1VUT1JfRE9DS0VSX0hVQl9VU0VSTkFNRT0kX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FCiAgICAgIC0gT1BSX0VYRUNVVE9SX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9JF9BUFBfRE9DS0VSX0hVQl9QQVNTV09SRAogICAgICAtIE9QUl9FWEVDVVRPUl9FTlY9JF9BUFBfRU5WCiAgICAgIC0gT1BSX0VYRUNVVE9SX1JVTlRJTUVTPSRfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIE9QUl9FWEVDVVRPUl9TRUNSRVQ9JF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX0xPR0dJTkdfUFJPVklERVI9JF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIE9QUl9FWEVDVVRPUl9MT0dHSU5HX0NPTkZJRz0kX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RFVklDRT0kX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfU0VDUkVUPSRfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfUkVHSU9OPSRfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfQlVDS0VUPSRfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQ9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT049JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfUkVHSU9OPSRfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX1JFR0lPTj0kX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfQlVDS0VUPSRfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogIG1hcmlhZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTAuNycKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1tYXJpYWRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLW1hcmlhZGI6L3Zhci9saWIvbXlzcWw6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke19BUFBfREJfUk9PVF9QQVNTfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtfQVBQX0RCX1NDSEVNQX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtfQVBQX0RCX1VTRVJ9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke19BUFBfREJfUEFTU30nCiAgICBjb21tYW5kOiAnbXlzcWxkIC0taW5ub2RiLWZsdXNoLW1ldGhvZD1mc3luYycKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny4wLjQtYWxwaW5lJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlZGlzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogInJlZGlzLXNlcnZlciAtLW1heG1lbW9yeSAgICAgICAgICAgIDUxMm1iIC0tbWF4bWVtb3J5LXBvbGljeSAgICAgYWxsa2V5cy1scnUgLS1tYXhtZW1vcnktc2FtcGxlcyAgICA1XG4iCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtcmVkaXM6L2RhdGE6cncnCiAgaW5mbHV4ZGI6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2luZmx1eGRiOjEuNS4wJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWluZmx1eGRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWluZmx1eGRiOi92YXIvbGliL2luZmx1eGRiOnJ3JwogIHRlbGVncmFmOgogICAgaW1hZ2U6ICdhcHB3cml0ZS90ZWxlZ3JhZjoxLjQuMCcKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS10ZWxlZ3JhZgogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0lORkxVWERCX0hPU1QKICAgICAgLSBfQVBQX0lORkxVWERCX1BPUlQKbmV0d29ya3M6CiAgZ2F0ZXdheToKICAgIG5hbWU6IGdhdGV3YXkKICBhcHB3cml0ZToKICAgIG5hbWU6IGFwcHdyaXRlCiAgcnVudGltZXM6CiAgICBuYW1lOiBydW50aW1lcwp2b2x1bWVzOgogIGFwcHdyaXRlLW1hcmlhZGI6IG51bGwKICBhcHB3cml0ZS1yZWRpczogbnVsbAogIGFwcHdyaXRlLWNhY2hlOiBudWxsCiAgYXBwd3JpdGUtdXBsb2FkczogbnVsbAogIGFwcHdyaXRlLWNlcnRpZmljYXRlczogbnVsbAogIGFwcHdyaXRlLWZ1bmN0aW9uczogbnVsbAogIGFwcHdyaXRlLWJ1aWxkczogbnVsbAogIGFwcHdyaXRlLWluZmx1eGRiOiBudWxsCiAgYXBwd3JpdGUtY29uZmlnOiBudWxsCg==",
- "envs": "appwrite.env"
+ "envs": "X0FQUF9FTlY9cHJvZHVjdGlvbgpfQVBQX0xPQ0FMRT1lbgpfQVBQX09QVElPTlNfQUJVU0U9ZW5hYmxlZApfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFM9ZGlzYWJsZWQKX0FQUF9PUEVOU1NMX0tFWV9WMT0KX0FQUF9ET01BSU5fRlVOQ1RJT05TPQpfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1Q9ZW5hYmxlZApfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUz0KX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFM9Cl9BUFBfU1lTVEVNX0VNQUlMX05BTUU9QXBwd3JpdGUKX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUz10ZWFtQGFwcHdyaXRlLmlvCl9BUFBfU1lTVEVNX1JFU1BPTlNFX0ZPUk1BVD0KX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUz1jZXJ0c0BhcHB3cml0ZS5pbwpfQVBQX1VTQUdFX1NUQVRTPWVuYWJsZWQKX0FQUF9MT0dHSU5HX1BST1ZJREVSPQpfQVBQX0xPR0dJTkdfQ09ORklHPQpfQVBQX1VTQUdFX0FHR1JFR0FUSU9OX0lOVEVSVkFMPTMwCl9BUFBfVVNBR0VfVElNRVNFUklFU19JTlRFUlZBTD0zMApfQVBQX1VTQUdFX0RBVEFCQVNFX0lOVEVSVkFMPTkwMApfQVBQX1dPUktFUl9QRVJfQ09SRT02Cl9BUFBfUkVESVNfSE9TVD1yZWRpcwpfQVBQX1JFRElTX1BPUlQ9NjM3OQpfQVBQX1JFRElTX1VTRVI9Cl9BUFBfUkVESVNfUEFTUz0KX0FQUF9EQl9IT1NUPW1hcmlhZGIKX0FQUF9EQl9QT1JUPTMzMDYKX0FQUF9EQl9TQ0hFTUE9YXBwd3JpdGUKX0FQUF9EQl9VU0VSPSRTRVJWSUNFX1VTRVJfTVlTUUwKX0FQUF9EQl9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCl9BUFBfREJfUk9PVF9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1RNWVNRTApfQVBQX0lORkxVWERCX0hPU1Q9aW5mbHV4ZGIKX0FQUF9JTkZMVVhEQl9QT1JUPTgwODYKX0FQUF9TVEFUU0RfSE9TVD10ZWxlZ3JhZgpfQVBQX1NUQVRTRF9QT1JUPTgxMjUKX0FQUF9TTVRQX0hPU1Q9Cl9BUFBfU01UUF9QT1JUPQpfQVBQX1NNVFBfU0VDVVJFPQpfQVBQX1NNVFBfVVNFUk5BTUU9Cl9BUFBfU01UUF9QQVNTV09SRD0KX0FQUF9TTVNfUFJPVklERVI9Cl9BUFBfU01TX0ZST009Cl9BUFBfU1RPUkFHRV9MSU1JVD0zMDAwMDAwMApfQVBQX1NUT1JBR0VfUFJFVklFV19MSU1JVD0yMDAwMDAwMApfQVBQX1NUT1JBR0VfQU5USVZJUlVTPWRpc2FibGVkCl9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVD1jbGFtYXYKX0FQUF9TVE9SQUdFX0FOVElWSVJVU19QT1JUPTMzMTAKX0FQUF9TVE9SQUdFX0RFVklDRT1sb2NhbApfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX1MzX1JFR0lPTj11cy1lYXN0LTEKX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT049dXMtZWFzdC0xCl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTj11cy13ZXN0LTAwNApfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT049ZXUtY2VudHJhbC0xCl9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUPQpfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUPQpfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTj1ldS1jZW50cmFsLTEKX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQ9Cl9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQ9MzAwMDAwMDAKX0FQUF9GVU5DVElPTlNfVElNRU9VVD05MDAKX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVD05MDAKX0FQUF9GVU5DVElPTlNfQ09OVEFJTkVSUz0xMApfQVBQX0ZVTkNUSU9OU19DUFVTPTAKX0FQUF9GVU5DVElPTlNfTUVNT1JZPTAKX0FQUF9GVU5DVElPTlNfTUVNT1JZX1NXQVA9MApfQVBQX0ZVTkNUSU9OU19SVU5USU1FUz1ub2RlLTE2LjAscGhwLTguMCxweXRob24tMy45LHJ1YnktMy4wCl9BUFBfRVhFQ1VUT1JfU0VDUkVUPXlvdXItc2VjcmV0LWtleQpfQVBQX0VYRUNVVE9SX0hPU1Q9aHR0cDovL2FwcHdyaXRlLWV4ZWN1dG9yL3YxCl9BUFBfRVhFQ1VUT1JfUlVOVElNRV9ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX0VOVlM9bm9kZS0xNi4wLHBocC03LjQscHl0aG9uLTMuOSxydWJ5LTMuMApfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQ9NjAKRE9DS0VSSFVCX1BVTExfVVNFUk5BTUU9CkRPQ0tFUkhVQl9QVUxMX1BBU1NXT1JEPQpET0NLRVJIVUJfUFVMTF9FTUFJTD0KT1BFTl9SVU5USU1FU19ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUks9cnVudGltZXMKX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FPQpfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9Cl9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMPTM2MDAKX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FPQpfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVk9Cl9BUFBfVkNTX0dJVEhVQl9BUFBfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUPQpfQVBQX1ZDU19HSVRIVUJfV0VCSE9PS19TRUNSRVQ9Cl9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUw9ODY0MDAKX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEU9MjU5MjAwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT049MTIwOTYwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVD0xMjA5NjAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFPTg2NDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWT04NjQwMDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUz04NjQwMApfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkU9MTAKX0FQUF9HUkFQSFFMX01BWF9DT01QTEVYSVRZPTI1MApfQVBQX0dSQVBIUUxfTUFYX0RFUFRIPTMKX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRD0KX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQ9Cl9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZPQo="
},
"babybuddy": {
"documentation": "https:\/\/docs.baby-buddy.net",
From 551032177672881439cc8f2059ece4cb4fda8c63 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:22:36 +0200
Subject: [PATCH 06/29] syncbunny update
---
app/Console/Commands/SyncBunny.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/Console/Commands/SyncBunny.php b/app/Console/Commands/SyncBunny.php
index 6a85ffd91..509614f4c 100644
--- a/app/Console/Commands/SyncBunny.php
+++ b/app/Console/Commands/SyncBunny.php
@@ -16,7 +16,7 @@ class SyncBunny extends Command
*
* @var string
*/
- protected $signature = 'sync:bunny {--only-template} {--only-version}';
+ protected $signature = 'sync:bunny {templates?} {release?}';
/**
* The console command description.
@@ -31,8 +31,8 @@ class SyncBunny extends Command
public function handle()
{
$that = $this;
- $only_template = $this->option('only-template');
- $only_version = $this->option('only-version');
+ $only_template = $this->argument('templates');
+ $only_version = $this->argument('release');
$bunny_cdn = "https://cdn.coollabs.io";
$bunny_cdn_path = "coolify";
$bunny_cdn_storage_name = "coolcdn";
From f187040b7e2eeffa05de813c984a0b807254e71c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:42:28 +0200
Subject: [PATCH 07/29] fix: mongodb backup
---
app/Jobs/DatabaseBackupJob.php | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index 84aad1b6c..d73474018 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -175,13 +175,20 @@ private function backup_standalone_mongodb(string $databaseWithCollections): voi
$commands[] = "mkdir -p " . $this->backup_dir;
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
} else {
- $collectionsToExclude = str($databaseWithCollections)->after(':')->explode(',');
- $databaseName = str($databaseWithCollections)->before(':');
+ if (str($databaseWithCollections)->contains(':')) {
+ $databaseName = str($databaseWithCollections)->before(':');
+ $collectionsToExclude = str($databaseWithCollections)->after(':')->explode(',');
+ } else {
+ $databaseName = $databaseWithCollections;
+ $collectionsToExclude = collect();
+ }
$commands[] = "mkdir -p " . $this->backup_dir;
- $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
+ if ($collectionsToExclude->count() === 0) {
+ $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
+ } else {
+ $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
+ }
}
-
- ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output);
if ($this->backup_output === '') {
From a6f9e5f0afe94c1f921eebf348f347f9fb5dffa4 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:42:33 +0200
Subject: [PATCH 08/29] fixes
---
resources/views/layouts/base.blade.php | 3 +--
resources/views/project/resources.blade.php | 12 ++++++++----
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php
index 1a83dc1fb..a5a6ad3af 100644
--- a/resources/views/layouts/base.blade.php
+++ b/resources/views/layouts/base.blade.php
@@ -96,8 +96,7 @@ function upgrade() {
}
function copyToClipboard(text) {
- navigator.clipboard.writeText(text);
- Livewire.emit('success', 'Copied to clipboard.');
+ navigator?.clipboard?.writeText(text) && Livewire.emit('success', 'Copied to clipboard.');
}
Livewire.on('reloadWindow', (timeout) => {
diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php
index ef2780719..a81982ec7 100644
--- a/resources/views/project/resources.blade.php
+++ b/resources/views/project/resources.blade.php
@@ -2,16 +2,20 @@
From 18c32decad65397f701da6c62ed597122f3762b2 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 10:43:34 +0200
Subject: [PATCH 09/29] guarded
---
app/Models/Environment.php | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/app/Models/Environment.php b/app/Models/Environment.php
index 941586edd..55dbeee94 100644
--- a/app/Models/Environment.php
+++ b/app/Models/Environment.php
@@ -7,11 +7,7 @@
class Environment extends Model
{
- protected $fillable = [
- 'name',
- 'project_id',
- ];
-
+ protected $guarded = [];
public function isEmpty()
{
return $this->applications()->count() == 0 &&
From 678b264688f7a111fa9c3a694c063bef9d400c8a Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 11:08:05 +0200
Subject: [PATCH 10/29] fix: make sure coolfiy network exists on install
---
docker-compose.yml | 1 +
scripts/upgrade.sh | 3 +++
2 files changed, 4 insertions(+)
diff --git a/docker-compose.yml b/docker-compose.yml
index ecd3f8b7c..001b1b212 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -28,3 +28,4 @@ networks:
coolify:
name: coolify
driver: bridge
+ external: true
diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh
index 01405e873..bb3adcf35 100644
--- a/scripts/upgrade.sh
+++ b/scripts/upgrade.sh
@@ -15,4 +15,7 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
# Merge .env and .env.production. New values will be added to .env
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' > /data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env
+# Make sure coolify network exists
+docker network create coolify 2>/dev/null
+
docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
From 393c334b129b4ad884b485265ada3e57916e3c5c Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 11:08:11 +0200
Subject: [PATCH 11/29] version++
---
config/sentry.php | 2 +-
config/version.php | 2 +-
versions.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/config/sentry.php b/config/sentry.php
index b8cd87fa3..25d04008a 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@
// 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.99',
+ 'release' => '4.0.0-beta.100',
// 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 63857a361..042813982 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Tue, 24 Oct 2023 11:08:15 +0200
Subject: [PATCH 12/29] fix: syncbunny command
---
app/Console/Commands/SyncBunny.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/Console/Commands/SyncBunny.php b/app/Console/Commands/SyncBunny.php
index 509614f4c..f3ddaaffe 100644
--- a/app/Console/Commands/SyncBunny.php
+++ b/app/Console/Commands/SyncBunny.php
@@ -16,7 +16,7 @@ class SyncBunny extends Command
*
* @var string
*/
- protected $signature = 'sync:bunny {templates?} {release?}';
+ protected $signature = 'sync:bunny {--templates} {--release}';
/**
* The console command description.
@@ -31,8 +31,8 @@ class SyncBunny extends Command
public function handle()
{
$that = $this;
- $only_template = $this->argument('templates');
- $only_version = $this->argument('release');
+ $only_template = $this->option('templates');
+ $only_version = $this->option('release');
$bunny_cdn = "https://cdn.coollabs.io";
$bunny_cdn_path = "coolify";
$bunny_cdn_storage_name = "coolcdn";
From c82e02218f4114eba035c1b3a451a3686e481f12 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 11:08:59 +0200
Subject: [PATCH 13/29] version++
---
scripts/install.sh | 2 +-
scripts/upgrade.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/install.sh b/scripts/install.sh
index 9ea40c8c2..293c54e27 100644
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -5,7 +5,7 @@
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" or "scripts/run sync-bunny" if you update this file.
###########
-VERSION="1.0.0"
+VERSION="1.0.1"
DOCKER_VERSION="24.0"
CDN="https://cdn.coollabs.io/coolify"
diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh
index bb3adcf35..3c44308a0 100644
--- a/scripts/upgrade.sh
+++ b/scripts/upgrade.sh
@@ -5,7 +5,7 @@
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" if you update this file.
###########
-VERSION="1.0.0"
+VERSION="1.0.1"
CDN="https://cdn.coollabs.io/coolify"
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
From b2d111e49a7ab06d414bd8e8d2e4395d1874f12f Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 12:33:49 +0200
Subject: [PATCH 14/29] feat: simple search functionality
---
...viceTemplates.php => ServicesGenerate.php} | 11 +-
app/Http/Livewire/Project/New/Select.php | 39 +++--
.../livewire/project/new/select.blade.php | 11 +-
templates/compose/appsmith.yaml | 1 +
templates/compose/appwrite.yaml | 1 +
templates/compose/babybuddy.yaml | 1 +
templates/compose/code-server.yaml | 1 +
templates/compose/dokuwiki.yaml | 1 +
templates/compose/fider.yaml | 1 +
templates/compose/ghost.yaml | 1 +
templates/compose/heimdall.yaml | 1 +
templates/compose/metube.yaml | 1 +
templates/compose/minio.yaml | 1 +
templates/compose/pairdrop.yaml | 1 +
templates/compose/plausible.yaml | 1 +
templates/compose/snapdrop.yaml | 1 +
templates/compose/umami.yaml | 1 +
templates/compose/uptime-kuma.yaml | 1 +
templates/compose/wordpress-with-mariadb.yaml | 1 +
templates/compose/wordpress-with-mysql.yaml | 1 +
.../compose/wordpress-without-database.yaml | 1 +
templates/service-templates.json | 140 ++++++++++++++++--
22 files changed, 189 insertions(+), 30 deletions(-)
rename app/Console/Commands/{GenerateServiceTemplates.php => ServicesGenerate.php} (88%)
diff --git a/app/Console/Commands/GenerateServiceTemplates.php b/app/Console/Commands/ServicesGenerate.php
similarity index 88%
rename from app/Console/Commands/GenerateServiceTemplates.php
rename to app/Console/Commands/ServicesGenerate.php
index bf271e8ad..530dd259a 100644
--- a/app/Console/Commands/GenerateServiceTemplates.php
+++ b/app/Console/Commands/ServicesGenerate.php
@@ -5,7 +5,7 @@
use Illuminate\Console\Command;
use Symfony\Component\Yaml\Yaml;
-class GenerateServiceTemplates extends Command
+class ServicesGenerate extends Command
{
/**
* The name and signature of the console command.
@@ -80,6 +80,14 @@ private function process_file($file)
$env_file = null;
}
+ $tags = collect(preg_grep('/^# tags:/', explode("\n", $content)))->values();
+ if ($tags->count() > 0) {
+ $tags = str($tags[0])->after('# tags:')->trim()->explode(',')->map(function ($tag) {
+ return str($tag)->trim()->lower()->value();
+ })->values();
+ } else {
+ $tags = null;
+ }
$json = Yaml::parse($content);
$yaml = base64_encode(Yaml::dump($json, 10, 2));
$payload = [
@@ -87,6 +95,7 @@ private function process_file($file)
'documentation' => $documentation,
'slogan' => $slogan,
'compose' => $yaml,
+ 'tags' => $tags,
];
if ($env_file) {
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
diff --git a/app/Http/Livewire/Project/New/Select.php b/app/Http/Livewire/Project/New/Select.php
index 8f1271c35..a4b80ce69 100644
--- a/app/Http/Livewire/Project/New/Select.php
+++ b/app/Http/Livewire/Project/New/Select.php
@@ -21,14 +21,18 @@ class Select extends Component
public Collection|array $swarmDockers = [];
public array $parameters;
public Collection|array $services = [];
+ public Collection|array $allServices = [];
+
public bool $loadingServices = true;
public bool $loading = false;
public $environments = [];
public ?string $selectedEnvironment = null;
public ?string $existingPostgresqlUrl = null;
+ public ?string $search = null;
protected $queryString = [
'server',
+ 'search'
];
public function mount()
@@ -41,6 +45,11 @@ public function mount()
$this->environments = Project::whereUuid($projectUuid)->first()->environments;
$this->selectedEnvironment = data_get($this->parameters, 'environment_name');
}
+ public function render()
+ {
+ $this->loadServices();
+ return view('livewire.project.new.select');
+ }
public function updatedSelectedEnvironment()
{
@@ -49,6 +58,7 @@ public function updatedSelectedEnvironment()
'environment_name' => $this->selectedEnvironment,
]);
}
+
// public function addExistingPostgresql()
// {
// try {
@@ -59,19 +69,28 @@ public function updatedSelectedEnvironment()
// }
// }
- public function loadThings()
- {
- $this->loadServices();
- $this->loadServers();
- }
- public function loadServices(bool $forceReload = false)
+ public function loadServices(bool $force = false)
{
try {
- if ($forceReload) {
- Cache::forget('services');
+ if (count($this->allServices) > 0 && !$force) {
+ if (!$this->search) {
+ $this->services = $this->allServices;
+ return;
+ }
+ $this->services = $this->allServices->filter(function ($service, $key) {
+ $tags = collect(data_get($service, 'tags', []));
+ return str_contains(strtolower($key), strtolower($this->search)) || $tags->contains(function ($tag) {
+ return str_contains(strtolower($tag), strtolower($this->search));
+ });
+ });
+ } else {
+ $this->search = null;
+ $this->allServices = getServiceTemplates();
+ $this->services = $this->allServices->filter(function ($service, $key) {
+ return str_contains(strtolower($key), strtolower($this->search));
+ });;
+ $this->emit('success', 'Successfully loaded services.');
}
- $this->services = getServiceTemplates();
- $this->emit('success', 'Successfully loaded services.');
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php
index ab5b0b894..6ebefda41 100644
--- a/resources/views/livewire/project/new/select.blade.php
+++ b/resources/views/livewire/project/new/select.blade.php
@@ -1,4 +1,4 @@
-
+
New Resource
@@ -128,12 +128,15 @@
Services
Reload Services List
+
@if ($loadingServices)
@else
- @foreach ($services as $serviceName => $service)
+ @forelse ($services as $serviceName => $service)
@if (data_get($service, 'disabled'))
@@ -158,7 +161,9 @@
@endif
- @endforeach
+ @empty
+
No service found.
+ @endforelse
@endif
Trademarks Policy: The respective trademarks mentioned here are owned by the
diff --git a/templates/compose/appsmith.yaml b/templates/compose/appsmith.yaml
index ce15f4e45..a30f4c4e5 100644
--- a/templates/compose/appsmith.yaml
+++ b/templates/compose/appsmith.yaml
@@ -1,5 +1,6 @@
# documentation: https://docs.appsmith.com
# slogan: Appsmith is an open-source, self-hosted application development platform that enables you to build powerful web applications with ease.
+# tags: lowcode,nocode,no,low,platform
services:
appsmith:
diff --git a/templates/compose/appwrite.yaml b/templates/compose/appwrite.yaml
index aa4e9da4a..ff2efb78a 100644
--- a/templates/compose/appwrite.yaml
+++ b/templates/compose/appwrite.yaml
@@ -1,6 +1,7 @@
# documentation: https://appwrite.io/docs
# slogan: Appwrite is a self-hosted backend-as-a-service platform that simplifies the development of web and mobile applications by providing a range of features and APIs.
# env_file: appwrite.env
+# tags: backend-as-a-service, platform
x-logging: &x-logging
diff --git a/templates/compose/babybuddy.yaml b/templates/compose/babybuddy.yaml
index e73f01c08..19b17037f 100644
--- a/templates/compose/babybuddy.yaml
+++ b/templates/compose/babybuddy.yaml
@@ -1,5 +1,6 @@
# documentation: https://docs.baby-buddy.net
# slogan: Baby Buddy is an open-source web application that helps parents track their baby's daily activities, growth, and health with ease.
+# tags: baby, parents, health, growth, activities
services:
babybuddy:
diff --git a/templates/compose/code-server.yaml b/templates/compose/code-server.yaml
index 612439035..19858601a 100644
--- a/templates/compose/code-server.yaml
+++ b/templates/compose/code-server.yaml
@@ -1,5 +1,6 @@
# documentation: https://coder.com/docs/code-server/latest/guide
# slogan: Code-Server is a self-hosted, web-based code editor that enables remote coding and collaboration from any device, anywhere.
+# tags: code, editor, remote, collaboration
services:
code-server:
diff --git a/templates/compose/dokuwiki.yaml b/templates/compose/dokuwiki.yaml
index 9ae0972ab..81f317c42 100644
--- a/templates/compose/dokuwiki.yaml
+++ b/templates/compose/dokuwiki.yaml
@@ -1,5 +1,6 @@
# documentation: https://www.dokuwiki.org/faq
# slogan: A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases with simplicity and flexibility.
+# tags: wiki, documentation, knowledge, base
services:
dokuwiki:
diff --git a/templates/compose/fider.yaml b/templates/compose/fider.yaml
index 71bfa0330..e2bf00910 100644
--- a/templates/compose/fider.yaml
+++ b/templates/compose/fider.yaml
@@ -1,5 +1,6 @@
# documentation: https://fider.io/doc
# slogan: Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.
+# tags: feedback, user-feedback
services:
fider:
diff --git a/templates/compose/ghost.yaml b/templates/compose/ghost.yaml
index fd1aca678..1ed34911b 100644
--- a/templates/compose/ghost.yaml
+++ b/templates/compose/ghost.yaml
@@ -1,5 +1,6 @@
# documentation: https://ghost.org/docs
# slogan: Ghost is a popular open-source content management system (CMS) and blogging platform, known for its simplicity and focus on content creation.
+# tags: cms, blog, content, management, system
services:
ghost:
diff --git a/templates/compose/heimdall.yaml b/templates/compose/heimdall.yaml
index 8216f0ea2..dcae08feb 100644
--- a/templates/compose/heimdall.yaml
+++ b/templates/compose/heimdall.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/linuxserver/Heimdall
# slogan: Heimdall is a self-hosted dashboard for managing and organizing your server applications, providing a centralized and efficient interface.
+# tags: dashboard, server, applications, interface
services:
heimdall:
diff --git a/templates/compose/metube.yaml b/templates/compose/metube.yaml
index 4bebefbb9..9a39e4959 100644
--- a/templates/compose/metube.yaml
+++ b/templates/compose/metube.yaml
@@ -1,5 +1,6 @@
# 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
services:
metube:
diff --git a/templates/compose/minio.yaml b/templates/compose/minio.yaml
index 372c928c0..6923af788 100644
--- a/templates/compose/minio.yaml
+++ b/templates/compose/minio.yaml
@@ -1,5 +1,6 @@
# documentation: https://docs.min.io/docs/minio-docker-quickstart-guide.html
# slogan: MinIO is a high performance object storage server compatible with Amazon S3 APIs.
+# tags: object, storage, server, s3, api
services:
minio:
diff --git a/templates/compose/pairdrop.yaml b/templates/compose/pairdrop.yaml
index c84d50434..924dae0d8 100644
--- a/templates/compose/pairdrop.yaml
+++ b/templates/compose/pairdrop.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/schlagmichdoch/PairDrop/blob/master/docs/faq.md
# slogan: Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.
+# tags: file, sharing, collaboration, teamwork
services:
pairdrop:
diff --git a/templates/compose/plausible.yaml b/templates/compose/plausible.yaml
index b64f9c6ef..a08324b0b 100644
--- a/templates/compose/plausible.yaml
+++ b/templates/compose/plausible.yaml
@@ -1,6 +1,7 @@
# ignore: true
# 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
version: "3.3"
services:
diff --git a/templates/compose/snapdrop.yaml b/templates/compose/snapdrop.yaml
index 345486859..066fb2013 100644
--- a/templates/compose/snapdrop.yaml
+++ b/templates/compose/snapdrop.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/RobinLinus/snapdrop/blob/master/docs/faq.md
# slogan: A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.
+# tags: file, sharing, transfer, local, network, internet
services:
snapdrop:
diff --git a/templates/compose/umami.yaml b/templates/compose/umami.yaml
index d443279a1..83ac2ba26 100644
--- a/templates/compose/umami.yaml
+++ b/templates/compose/umami.yaml
@@ -1,5 +1,6 @@
# documentation: https://umami.is/docs/getting-started
# slogan: Umami is a lightweight, self-hosted web analytics platform designed to provide website owners with insights into visitor behavior without compromising user privacy.
+# tags: analytics, insights, privacy
services:
umami:
diff --git a/templates/compose/uptime-kuma.yaml b/templates/compose/uptime-kuma.yaml
index d64a0d05c..a1a02f91f 100644
--- a/templates/compose/uptime-kuma.yaml
+++ b/templates/compose/uptime-kuma.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/louislam/uptime-kuma/wiki
# slogan: Uptime Kuma is a free, self-hosted monitoring tool for tracking the status and performance of your web services and applications in real-time.
+# tags: monitoring, status, performance, web, services, applications, real-time
services:
uptime-kuma:
diff --git a/templates/compose/wordpress-with-mariadb.yaml b/templates/compose/wordpress-with-mariadb.yaml
index ccd7f0c70..4d59daf32 100644
--- a/templates/compose/wordpress-with-mariadb.yaml
+++ b/templates/compose/wordpress-with-mariadb.yaml
@@ -1,5 +1,6 @@
# documentation: https://wordpress.org/documentation/
# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
+# tags: cms, blog, content, management, mariadb
services:
wordpress:
diff --git a/templates/compose/wordpress-with-mysql.yaml b/templates/compose/wordpress-with-mysql.yaml
index b796db3ec..c264ecbf3 100644
--- a/templates/compose/wordpress-with-mysql.yaml
+++ b/templates/compose/wordpress-with-mysql.yaml
@@ -1,5 +1,6 @@
# documentation: https://wordpress.org/documentation/
# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
+# tags: cms, blog, content, management, mysql
services:
wordpress:
diff --git a/templates/compose/wordpress-without-database.yaml b/templates/compose/wordpress-without-database.yaml
index 0a0745a24..ddcefe90d 100644
--- a/templates/compose/wordpress-without-database.yaml
+++ b/templates/compose/wordpress-without-database.yaml
@@ -1,5 +1,6 @@
# documentation: https://wordpress.org/documentation/
# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
+# tags: cms, blog, content, management
services:
wordpress:
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 33576e92b..d5af2b37c 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -2,87 +2,195 @@
"appsmith": {
"documentation": "https:\/\/docs.appsmith.com",
"slogan": "Appsmith is an open-source, self-hosted application development platform that enables you to build powerful web applications with ease.",
- "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycK"
+ "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycK",
+ "tags": [
+ "lowcode",
+ "nocode",
+ "no",
+ "low",
+ "platform"
+ ]
},
"appwrite": {
"documentation": "https:\/\/appwrite.io\/docs",
"slogan": "Appwrite is a self-hosted backend-as-a-service platform that simplifies the development of web and mobile applications by providing a range of features and APIs.",
"compose": "eC1sb2dnaW5nOgogIGxvZ2dpbmc6CiAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgb3B0aW9uczoKICAgICAgbWF4LWZpbGU6ICc1JwogICAgICBtYXgtc2l6ZTogMTBtCnZlcnNpb246ICczJwpzZXJ2aWNlczoKICBhcHB3cml0ZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40JwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBsYWJlbHM6CiAgICAgIC0gdHJhZWZpay5jb25zdHJhaW50LWxhYmVsLXN0YWNrPWFwcHdyaXRlCiAgICAgIC0gdHJhZWZpay5kb2NrZXIubmV0d29yaz1hcHB3cml0ZQogICAgICAtIHRyYWVmaWsuaHR0cC5zZXJ2aWNlcy5hcHB3cml0ZV9hcGkubG9hZGJhbGFuY2VyLnNlcnZlci5wb3J0PTgwCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfYXBpX2h0dHAuZW50cnlwb2ludHM9d2ViCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfYXBpX2h0dHAucnVsZT1QYXRoUHJlZml4KGAvYCkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cC5zZXJ2aWNlPWFwcHdyaXRlX2FwaQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX2FwaV9odHRwcy5lbnRyeXBvaW50cz13ZWJzZWN1cmUKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMucnVsZT1QYXRoUHJlZml4KGAvYCkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMuc2VydmljZT1hcHB3cml0ZV9hcGkKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9hcGlfaHR0cHMudGxzPXRydWUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLXVwbG9hZHM6L3N0b3JhZ2UvdXBsb2FkczpydycKICAgICAgLSAnYXBwd3JpdGUtY2FjaGU6L3N0b3JhZ2UvY2FjaGU6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNvbmZpZzovc3RvcmFnZS9jb25maWc6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNlcnRpZmljYXRlczovc3RvcmFnZS9jZXJ0aWZpY2F0ZXM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWZ1bmN0aW9uczovc3RvcmFnZS9mdW5jdGlvbnM6cncnCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICAgICAgLSByZWRpcwogICAgICAtIGluZmx1eGRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQVBQV1JJVEU9LwogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0xPQ0FMRQogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfUk9PVAogICAgICAtIF9BUFBfQ09OU09MRV9XSElURUxJU1RfRU1BSUxTCiAgICAgIC0gX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFMKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1NZU1RFTV9SRVNQT05TRV9GT1JNQVQKICAgICAgLSBfQVBQX09QVElPTlNfQUJVU0UKICAgICAgLSBfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFMKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX0RPTUFJTl9UQVJHRVQKICAgICAgLSBfQVBQX0RPTUFJTl9GVU5DVElPTlMKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfSU5GTFVYREJfSE9TVAogICAgICAtIF9BUFBfSU5GTFVYREJfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU1JVAogICAgICAtIF9BUFBfU1RPUkFHRV9QUkVWSUVXX0xJTUlUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0FOVElWSVJVUwogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVAogICAgICAtIF9BUFBfU1RPUkFHRV9BTlRJVklSVVNfUE9SVAogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19USU1FT1VUCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0NQVVMKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19NRU1PUlkKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9IT1NUCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfU1RBVFNEX0hPU1QKICAgICAgLSBfQVBQX1NUQVRTRF9QT1JUCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9JTlRFUlZBTAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0VYRUNVVElPTgogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0NBQ0hFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQUJVU0UKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVAogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUwogICAgICAtIF9BUFBfU01TX1BST1ZJREVSCiAgICAgIC0gX0FQUF9TTVNfRlJPTQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfQkFUQ0hfU0laRQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfQ09NUExFWElUWQogICAgICAtIF9BUFBfR1JBUEhRTF9NQVhfREVQVEgKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX1dFQkhPT0tfU0VDUkVUCiAgICAgIC0gX0FQUF9WQ1NfR0lUSFVCX0NMSUVOVF9TRUNSRVQKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRAogICAgICAtIF9BUFBfTUlHUkFUSU9OU19GSVJFQkFTRV9DTElFTlRfU0VDUkVUCiAgICAgIC0gX0FQUF9BU1NJU1RBTlRfT1BFTkFJX0FQSV9LRVkKICBhcHB3cml0ZS1yZWFsdGltZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiByZWFsdGltZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlYWx0aW1lCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBsYWJlbHM6CiAgICAgIC0gdHJhZWZpay5jb25zdHJhaW50LWxhYmVsLXN0YWNrPWFwcHdyaXRlCiAgICAgIC0gdHJhZWZpay5kb2NrZXIubmV0d29yaz1hcHB3cml0ZQogICAgICAtIHRyYWVmaWsuaHR0cC5zZXJ2aWNlcy5hcHB3cml0ZV9yZWFsdGltZS5sb2FkYmFsYW5jZXIuc2VydmVyLnBvcnQ9ODAKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93cy5lbnRyeXBvaW50cz13ZWIKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93cy5ydWxlPVBhdGhQcmVmaXgoYC92MS9yZWFsdGltZWApCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3Muc2VydmljZT1hcHB3cml0ZV9yZWFsdGltZQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX3JlYWx0aW1lX3dzcy5lbnRyeXBvaW50cz13ZWJzZWN1cmUKICAgICAgLSB0cmFlZmlrLmh0dHAucm91dGVycy5hcHB3cml0ZV9yZWFsdGltZV93c3MucnVsZT1QYXRoUHJlZml4KGAvdjEvcmVhbHRpbWVgKQogICAgICAtIHRyYWVmaWsuaHR0cC5yb3V0ZXJzLmFwcHdyaXRlX3JlYWx0aW1lX3dzcy5zZXJ2aWNlPWFwcHdyaXRlX3JlYWx0aW1lCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3NzLnRscz10cnVlCiAgICAgIC0gdHJhZWZpay5odHRwLnJvdXRlcnMuYXBwd3JpdGVfcmVhbHRpbWVfd3NzLnRscy5jZXJ0cmVzb2x2ZXI9ZG5zCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSBtYXJpYWRiCiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9BUFBXUklURT0vdjEvcmVhbHRpbWUKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUFRJT05TX0FCVVNFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9VU0FHRV9TVEFUUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItYXVkaXRzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1hdWRpdHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItYXVkaXRzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci13ZWJob29rczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItd2ViaG9va3MKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItd2ViaG9va3MKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgICAtIG1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZGVsZXRlczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItZGVsZXRlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kZWxldGVzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS11cGxvYWRzOi9zdG9yYWdlL3VwbG9hZHM6cncnCiAgICAgIC0gJ2FwcHdyaXRlLWNhY2hlOi9zdG9yYWdlL2NhY2hlOnJ3JwogICAgICAtICdhcHB3cml0ZS1mdW5jdGlvbnM6L3N0b3JhZ2UvZnVuY3Rpb25zOnJ3JwogICAgICAtICdhcHB3cml0ZS1idWlsZHM6L3N0b3JhZ2UvYnVpbGRzOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfU1RPUkFHRV9ERVZJQ0UKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9TM19TRUNSRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0JVQ0tFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX0VYRUNVVE9SX1NFQ1JFVAogICAgICAtIF9BUFBfRVhFQ1VUT1JfSE9TVAogIGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXM6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogd29ya2VyLWRhdGFiYXNlcwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1kYXRhYmFzZXMKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgICAtIG1hcmlhZGIKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9SRURJU19IT1NUCiAgICAgIC0gX0FQUF9SRURJU19QT1JUCiAgICAgIC0gX0FQUF9SRURJU19VU0VSCiAgICAgIC0gX0FQUF9SRURJU19QQVNTCiAgICAgIC0gX0FQUF9EQl9IT1NUCiAgICAgIC0gX0FQUF9EQl9QT1JUCiAgICAgIC0gX0FQUF9EQl9TQ0hFTUEKICAgICAgLSBfQVBQX0RCX1VTRVIKICAgICAgLSBfQVBQX0RCX1BBU1MKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtd29ya2VyLWJ1aWxkczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItYnVpbGRzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICBjb250YWluZXJfbmFtZTogYXBwd3JpdGUtd29ya2VyLWJ1aWxkcwogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBkZXBlbmRzX29uOgogICAgICAtIHJlZGlzCiAgICAgIC0gbWFyaWFkYgogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX09QRU5TU0xfS0VZX1YxCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX05BTUUKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVkKICAgICAgLSBfQVBQX1ZDU19HSVRIVUJfQVBQX0lECiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9PUFRJT05TX0ZPUkNFX0hUVFBTCiAgICAgIC0gX0FQUF9ET01BSU4KICAgICAgLSBfQVBQX1NUT1JBR0VfREVWSUNFCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gX0FQUF9TVE9SQUdFX1MzX1JFR0lPTgogICAgICAtIF9BUFBfU1RPUkFHRV9TM19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9MSU5PREVfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX0xJTk9ERV9CVUNLRVQKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVkKICAgICAgLSBfQVBQX1NUT1JBR0VfV0FTQUJJX1NFQ1JFVAogICAgICAtIF9BUFBfU1RPUkFHRV9XQVNBQklfUkVHSU9OCiAgICAgIC0gX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQKICBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1jZXJ0aWZpY2F0ZXMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItY2VydGlmaWNhdGVzCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICB2b2x1bWVzOgogICAgICAtICdhcHB3cml0ZS1jb25maWc6L3N0b3JhZ2UvY29uZmlnOnJ3JwogICAgICAtICdhcHB3cml0ZS1jZXJ0aWZpY2F0ZXM6L3N0b3JhZ2UvY2VydGlmaWNhdGVzOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zOgogICAgaW1hZ2U6ICdhcHB3cml0ZS9hcHB3cml0ZToxLjQuMycKICAgIGVudHJ5cG9pbnQ6IHdvcmtlci1mdW5jdGlvbnMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItZnVuY3Rpb25zCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgICAgLSBtYXJpYWRiCiAgICAgIC0gb3BlbnJ1bnRpbWVzLWV4ZWN1dG9yCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfVElNRU9VVAogICAgICAtIF9BUFBfRlVOQ1RJT05TX0JVSUxEX1RJTUVPVVQKICAgICAgLSBfQVBQX0ZVTkNUSU9OU19DUFVTCiAgICAgIC0gX0FQUF9GVU5DVElPTlNfTUVNT1JZCiAgICAgIC0gX0FQUF9FWEVDVVRPUl9TRUNSRVQKICAgICAgLSBfQVBQX0VYRUNVVE9SX0hPU1QKICAgICAgLSBfQVBQX1VTQUdFX1NUQVRTCiAgICAgIC0gX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9ET0NLRVJfSFVCX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogIGFwcHdyaXRlLXdvcmtlci1tYWlsczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWFpbHMKICAgIGxvZ2dpbmc6CiAgICAgIGRyaXZlcjoganNvbi1maWxlCiAgICAgIG9wdGlvbnM6CiAgICAgICAgbWF4LWZpbGU6ICc1JwogICAgICAgIG1heC1zaXplOiAxMG0KICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS13b3JrZXItbWFpbHMKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1NZU1RFTV9FTUFJTF9OQU1FCiAgICAgIC0gX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUwogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01UUF9IT1NUCiAgICAgIC0gX0FQUF9TTVRQX1BPUlQKICAgICAgLSBfQVBQX1NNVFBfU0VDVVJFCiAgICAgIC0gX0FQUF9TTVRQX1VTRVJOQU1FCiAgICAgIC0gX0FQUF9TTVRQX1BBU1NXT1JECiAgICAgIC0gX0FQUF9MT0dHSU5HX1BST1ZJREVSCiAgICAgIC0gX0FQUF9MT0dHSU5HX0NPTkZJRwogIGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmc6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogd29ya2VyLW1lc3NhZ2luZwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1tZXNzYWdpbmcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgZGVwZW5kc19vbjoKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfU01TX1BST1ZJREVSCiAgICAgIC0gX0FQUF9TTVNfRlJPTQogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICBhcHB3cml0ZS13b3JrZXItbWlncmF0aW9uczoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB3b3JrZXItbWlncmF0aW9ucwogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXdvcmtlci1taWdyYXRpb25zCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gbWFyaWFkYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfU1lTVEVNX1NFQ1VSSVRZX0VNQUlMX0FERFJFU1MKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIF9BUFBfTE9HR0lOR19DT05GSUcKICAgICAgLSBfQVBQX01JR1JBVElPTlNfRklSRUJBU0VfQ0xJRU5UX0lECiAgICAgIC0gX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQKICBhcHB3cml0ZS1tYWludGVuYW5jZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiBtYWludGVuYW5jZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLW1haW50ZW5hbmNlCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gcmVkaXMKICAgIGVudmlyb25tZW50OgogICAgICAtIF9BUFBfRU5WCiAgICAgIC0gX0FQUF9XT1JLRVJfUEVSX0NPUkUKICAgICAgLSBfQVBQX0RPTUFJTgogICAgICAtIF9BUFBfRE9NQUlOX1RBUkdFVAogICAgICAtIF9BUFBfRE9NQUlOX0ZVTkNUSU9OUwogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUwKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT04KICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9DQUNIRQogICAgICAtIF9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFCiAgICAgIC0gX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQVVESVQKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9VU0FHRV9IT1VSTFkKICAgICAgLSBfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9TQ0hFRFVMRVMKICBhcHB3cml0ZS11c2FnZToKICAgIGltYWdlOiAnYXBwd3JpdGUvYXBwd3JpdGU6MS40LjMnCiAgICBlbnRyeXBvaW50OiB1c2FnZQogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXVzYWdlCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIGRlcGVuZHNfb246CiAgICAgIC0gaW5mbHV4ZGIKICAgICAgLSBtYXJpYWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0VOVgogICAgICAtIF9BUFBfV09SS0VSX1BFUl9DT1JFCiAgICAgIC0gX0FQUF9PUEVOU1NMX0tFWV9WMQogICAgICAtIF9BUFBfREJfSE9TVAogICAgICAtIF9BUFBfREJfUE9SVAogICAgICAtIF9BUFBfREJfU0NIRU1BCiAgICAgIC0gX0FQUF9EQl9VU0VSCiAgICAgIC0gX0FQUF9EQl9QQVNTCiAgICAgIC0gX0FQUF9JTkZMVVhEQl9IT1NUCiAgICAgIC0gX0FQUF9JTkZMVVhEQl9QT1JUCiAgICAgIC0gX0FQUF9VU0FHRV9BR0dSRUdBVElPTl9JTlRFUlZBTAogICAgICAtIF9BUFBfUkVESVNfSE9TVAogICAgICAtIF9BUFBfUkVESVNfUE9SVAogICAgICAtIF9BUFBfUkVESVNfVVNFUgogICAgICAtIF9BUFBfUkVESVNfUEFTUwogICAgICAtIF9BUFBfVVNBR0VfU1RBVFMKICAgICAgLSBfQVBQX0xPR0dJTkdfUFJPVklERVIKICAgICAgLSBfQVBQX0xPR0dJTkdfQ09ORklHCiAgYXBwd3JpdGUtc2NoZWR1bGU6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2FwcHdyaXRlOjEuNC4zJwogICAgZW50cnlwb2ludDogc2NoZWR1bGUKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1zY2hlZHVsZQogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICAgICAgLSByZWRpcwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gX0FQUF9FTlYKICAgICAgLSBfQVBQX1dPUktFUl9QRVJfQ09SRQogICAgICAtIF9BUFBfT1BFTlNTTF9LRVlfVjEKICAgICAgLSBfQVBQX1JFRElTX0hPU1QKICAgICAgLSBfQVBQX1JFRElTX1BPUlQKICAgICAgLSBfQVBQX1JFRElTX1VTRVIKICAgICAgLSBfQVBQX1JFRElTX1BBU1MKICAgICAgLSBfQVBQX0RCX0hPU1QKICAgICAgLSBfQVBQX0RCX1BPUlQKICAgICAgLSBfQVBQX0RCX1NDSEVNQQogICAgICAtIF9BUFBfREJfVVNFUgogICAgICAtIF9BUFBfREJfUEFTUwogIGFwcHdyaXRlLWFzc2lzdGFudDoKICAgIGltYWdlOiAnYXBwd3JpdGUvYXNzaXN0YW50OjAuMi4xJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWFzc2lzdGFudAogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0FTU0lTVEFOVF9PUEVOQUlfQVBJX0tFWQogIG9wZW5ydW50aW1lcy1leGVjdXRvcjoKICAgIGNvbnRhaW5lcl9uYW1lOiBvcGVucnVudGltZXMtZXhlY3V0b3IKICAgIGhvc3RuYW1lOiBhcHB3cml0ZS1leGVjdXRvcgogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgc3RvcF9zaWduYWw6IFNJR0lOVAogICAgaW1hZ2U6ICdvcGVucnVudGltZXMvZXhlY3V0b3I6MC40LjEnCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgICAtIHJ1bnRpbWVzCiAgICB2b2x1bWVzOgogICAgICAtICcvdmFyL3J1bi9kb2NrZXIuc29jazovdmFyL3J1bi9kb2NrZXIuc29jaycKICAgICAgLSAnYXBwd3JpdGUtYnVpbGRzOi9zdG9yYWdlL2J1aWxkczpydycKICAgICAgLSAnYXBwd3JpdGUtZnVuY3Rpb25zOi9zdG9yYWdlL2Z1bmN0aW9uczpydycKICAgICAgLSAnL3RtcDovdG1wOnJ3JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gT1BSX0VYRUNVVE9SX0lOQUNUSVZFX1RSRVNIT0xEPSRfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQKICAgICAgLSBPUFJfRVhFQ1VUT1JfTUFJTlRFTkFOQ0VfSU5URVJWQUw9JF9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMCiAgICAgIC0gT1BSX0VYRUNVVE9SX05FVFdPUks9JF9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUksKICAgICAgLSBPUFJfRVhFQ1VUT1JfRE9DS0VSX0hVQl9VU0VSTkFNRT0kX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FCiAgICAgIC0gT1BSX0VYRUNVVE9SX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9JF9BUFBfRE9DS0VSX0hVQl9QQVNTV09SRAogICAgICAtIE9QUl9FWEVDVVRPUl9FTlY9JF9BUFBfRU5WCiAgICAgIC0gT1BSX0VYRUNVVE9SX1JVTlRJTUVTPSRfQVBQX0ZVTkNUSU9OU19SVU5USU1FUwogICAgICAtIE9QUl9FWEVDVVRPUl9TRUNSRVQ9JF9BUFBfRVhFQ1VUT1JfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX0xPR0dJTkdfUFJPVklERVI9JF9BUFBfTE9HR0lOR19QUk9WSURFUgogICAgICAtIE9QUl9FWEVDVVRPUl9MT0dHSU5HX0NPTkZJRz0kX0FQUF9MT0dHSU5HX0NPTkZJRwogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RFVklDRT0kX0FQUF9TVE9SQUdFX0RFVklDRQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1MzX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9TM19BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfU0VDUkVUPSRfQVBQX1NUT1JBR0VfUzNfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfUkVHSU9OPSRfQVBQX1NUT1JBR0VfUzNfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfUzNfQlVDS0VUPSRfQVBQX1NUT1JBR0VfUzNfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0RPX1NQQUNFU19TRUNSRVQ9JF9BUFBfU1RPUkFHRV9ET19TUEFDRVNfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfRE9fU1BBQ0VTX1JFR0lPTj0kX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUPSRfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX0JVQ0tFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9BQ0NFU1NfS0VZPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVkKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUPSRfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1NFQ1JFVAogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0JBQ0tCTEFaRV9SRUdJT049JF9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfUkVHSU9OCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVD0kX0FQUF9TVE9SQUdFX0JBQ0tCTEFaRV9CVUNLRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfQUNDRVNTX0tFWT0kX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfTElOT0RFX1NFQ1JFVD0kX0FQUF9TVE9SQUdFX0xJTk9ERV9TRUNSRVQKICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9MSU5PREVfUkVHSU9OPSRfQVBQX1NUT1JBR0VfTElOT0RFX1JFR0lPTgogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX0xJTk9ERV9CVUNLRVQ9JF9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVk9JF9BUFBfU1RPUkFHRV9XQVNBQklfQUNDRVNTX0tFWQogICAgICAtIE9QUl9FWEVDVVRPUl9TVE9SQUdFX1dBU0FCSV9TRUNSRVQ9JF9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUCiAgICAgIC0gT1BSX0VYRUNVVE9SX1NUT1JBR0VfV0FTQUJJX1JFR0lPTj0kX0FQUF9TVE9SQUdFX1dBU0FCSV9SRUdJT04KICAgICAgLSBPUFJfRVhFQ1VUT1JfU1RPUkFHRV9XQVNBQklfQlVDS0VUPSRfQVBQX1NUT1JBR0VfV0FTQUJJX0JVQ0tFVAogIG1hcmlhZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTAuNycKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS1tYXJpYWRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLW1hcmlhZGI6L3Zhci9saWIvbXlzcWw6cncnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke19BUFBfREJfUk9PVF9QQVNTfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtfQVBQX0RCX1NDSEVNQX0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtfQVBQX0RCX1VTRVJ9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke19BUFBfREJfUEFTU30nCiAgICBjb21tYW5kOiAnbXlzcWxkIC0taW5ub2RiLWZsdXNoLW1ldGhvZD1mc3luYycKICByZWRpczoKICAgIGltYWdlOiAncmVkaXM6Ny4wLjQtYWxwaW5lJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLXJlZGlzCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgY29tbWFuZDogInJlZGlzLXNlcnZlciAtLW1heG1lbW9yeSAgICAgICAgICAgIDUxMm1iIC0tbWF4bWVtb3J5LXBvbGljeSAgICAgYWxsa2V5cy1scnUgLS1tYXhtZW1vcnktc2FtcGxlcyAgICA1XG4iCiAgICBuZXR3b3JrczoKICAgICAgLSBhcHB3cml0ZQogICAgdm9sdW1lczoKICAgICAgLSAnYXBwd3JpdGUtcmVkaXM6L2RhdGE6cncnCiAgaW5mbHV4ZGI6CiAgICBpbWFnZTogJ2FwcHdyaXRlL2luZmx1eGRiOjEuNS4wJwogICAgY29udGFpbmVyX25hbWU6IGFwcHdyaXRlLWluZmx1eGRiCiAgICBsb2dnaW5nOgogICAgICBkcml2ZXI6IGpzb24tZmlsZQogICAgICBvcHRpb25zOgogICAgICAgIG1heC1maWxlOiAnNScKICAgICAgICBtYXgtc2l6ZTogMTBtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya3M6CiAgICAgIC0gYXBwd3JpdGUKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2FwcHdyaXRlLWluZmx1eGRiOi92YXIvbGliL2luZmx1eGRiOnJ3JwogIHRlbGVncmFmOgogICAgaW1hZ2U6ICdhcHB3cml0ZS90ZWxlZ3JhZjoxLjQuMCcKICAgIGNvbnRhaW5lcl9uYW1lOiBhcHB3cml0ZS10ZWxlZ3JhZgogICAgbG9nZ2luZzoKICAgICAgZHJpdmVyOiBqc29uLWZpbGUKICAgICAgb3B0aW9uczoKICAgICAgICBtYXgtZmlsZTogJzUnCiAgICAgICAgbWF4LXNpemU6IDEwbQogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIG5ldHdvcmtzOgogICAgICAtIGFwcHdyaXRlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBfQVBQX0lORkxVWERCX0hPU1QKICAgICAgLSBfQVBQX0lORkxVWERCX1BPUlQKbmV0d29ya3M6CiAgZ2F0ZXdheToKICAgIG5hbWU6IGdhdGV3YXkKICBhcHB3cml0ZToKICAgIG5hbWU6IGFwcHdyaXRlCiAgcnVudGltZXM6CiAgICBuYW1lOiBydW50aW1lcwp2b2x1bWVzOgogIGFwcHdyaXRlLW1hcmlhZGI6IG51bGwKICBhcHB3cml0ZS1yZWRpczogbnVsbAogIGFwcHdyaXRlLWNhY2hlOiBudWxsCiAgYXBwd3JpdGUtdXBsb2FkczogbnVsbAogIGFwcHdyaXRlLWNlcnRpZmljYXRlczogbnVsbAogIGFwcHdyaXRlLWZ1bmN0aW9uczogbnVsbAogIGFwcHdyaXRlLWJ1aWxkczogbnVsbAogIGFwcHdyaXRlLWluZmx1eGRiOiBudWxsCiAgYXBwd3JpdGUtY29uZmlnOiBudWxsCg==",
+ "tags": [
+ "backend-as-a-service",
+ "platform"
+ ],
"envs": "X0FQUF9FTlY9cHJvZHVjdGlvbgpfQVBQX0xPQ0FMRT1lbgpfQVBQX09QVElPTlNfQUJVU0U9ZW5hYmxlZApfQVBQX09QVElPTlNfRk9SQ0VfSFRUUFM9ZGlzYWJsZWQKX0FQUF9PUEVOU1NMX0tFWV9WMT0KX0FQUF9ET01BSU5fRlVOQ1RJT05TPQpfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX1JPT1Q9ZW5hYmxlZApfQVBQX0NPTlNPTEVfV0hJVEVMSVNUX0VNQUlMUz0KX0FQUF9DT05TT0xFX1dISVRFTElTVF9JUFM9Cl9BUFBfU1lTVEVNX0VNQUlMX05BTUU9QXBwd3JpdGUKX0FQUF9TWVNURU1fRU1BSUxfQUREUkVTUz10ZWFtQGFwcHdyaXRlLmlvCl9BUFBfU1lTVEVNX1JFU1BPTlNFX0ZPUk1BVD0KX0FQUF9TWVNURU1fU0VDVVJJVFlfRU1BSUxfQUREUkVTUz1jZXJ0c0BhcHB3cml0ZS5pbwpfQVBQX1VTQUdFX1NUQVRTPWVuYWJsZWQKX0FQUF9MT0dHSU5HX1BST1ZJREVSPQpfQVBQX0xPR0dJTkdfQ09ORklHPQpfQVBQX1VTQUdFX0FHR1JFR0FUSU9OX0lOVEVSVkFMPTMwCl9BUFBfVVNBR0VfVElNRVNFUklFU19JTlRFUlZBTD0zMApfQVBQX1VTQUdFX0RBVEFCQVNFX0lOVEVSVkFMPTkwMApfQVBQX1dPUktFUl9QRVJfQ09SRT02Cl9BUFBfUkVESVNfSE9TVD1yZWRpcwpfQVBQX1JFRElTX1BPUlQ9NjM3OQpfQVBQX1JFRElTX1VTRVI9Cl9BUFBfUkVESVNfUEFTUz0KX0FQUF9EQl9IT1NUPW1hcmlhZGIKX0FQUF9EQl9QT1JUPTMzMDYKX0FQUF9EQl9TQ0hFTUE9YXBwd3JpdGUKX0FQUF9EQl9VU0VSPSRTRVJWSUNFX1VTRVJfTVlTUUwKX0FQUF9EQl9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCl9BUFBfREJfUk9PVF9QQVNTPSRTRVJWSUNFX1BBU1NXT1JEX1JPT1RNWVNRTApfQVBQX0lORkxVWERCX0hPU1Q9aW5mbHV4ZGIKX0FQUF9JTkZMVVhEQl9QT1JUPTgwODYKX0FQUF9TVEFUU0RfSE9TVD10ZWxlZ3JhZgpfQVBQX1NUQVRTRF9QT1JUPTgxMjUKX0FQUF9TTVRQX0hPU1Q9Cl9BUFBfU01UUF9QT1JUPQpfQVBQX1NNVFBfU0VDVVJFPQpfQVBQX1NNVFBfVVNFUk5BTUU9Cl9BUFBfU01UUF9QQVNTV09SRD0KX0FQUF9TTVNfUFJPVklERVI9Cl9BUFBfU01TX0ZST009Cl9BUFBfU1RPUkFHRV9MSU1JVD0zMDAwMDAwMApfQVBQX1NUT1JBR0VfUFJFVklFV19MSU1JVD0yMDAwMDAwMApfQVBQX1NUT1JBR0VfQU5USVZJUlVTPWRpc2FibGVkCl9BUFBfU1RPUkFHRV9BTlRJVklSVVNfSE9TVD1jbGFtYXYKX0FQUF9TVE9SQUdFX0FOVElWSVJVU19QT1JUPTMzMTAKX0FQUF9TVE9SQUdFX0RFVklDRT1sb2NhbApfQVBQX1NUT1JBR0VfUzNfQUNDRVNTX0tFWT0KX0FQUF9TVE9SQUdFX1MzX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX1MzX1JFR0lPTj11cy1lYXN0LTEKX0FQUF9TVE9SQUdFX1MzX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfRE9fU1BBQ0VTX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0RPX1NQQUNFU19SRUdJT049dXMtZWFzdC0xCl9BUFBfU1RPUkFHRV9ET19TUEFDRVNfQlVDS0VUPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9CQUNLQkxBWkVfU0VDUkVUPQpfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX1JFR0lPTj11cy13ZXN0LTAwNApfQVBQX1NUT1JBR0VfQkFDS0JMQVpFX0JVQ0tFVD0KX0FQUF9TVE9SQUdFX0xJTk9ERV9BQ0NFU1NfS0VZPQpfQVBQX1NUT1JBR0VfTElOT0RFX1NFQ1JFVD0KX0FQUF9TVE9SQUdFX0xJTk9ERV9SRUdJT049ZXUtY2VudHJhbC0xCl9BUFBfU1RPUkFHRV9MSU5PREVfQlVDS0VUPQpfQVBQX1NUT1JBR0VfV0FTQUJJX0FDQ0VTU19LRVk9Cl9BUFBfU1RPUkFHRV9XQVNBQklfU0VDUkVUPQpfQVBQX1NUT1JBR0VfV0FTQUJJX1JFR0lPTj1ldS1jZW50cmFsLTEKX0FQUF9TVE9SQUdFX1dBU0FCSV9CVUNLRVQ9Cl9BUFBfRlVOQ1RJT05TX1NJWkVfTElNSVQ9MzAwMDAwMDAKX0FQUF9GVU5DVElPTlNfVElNRU9VVD05MDAKX0FQUF9GVU5DVElPTlNfQlVJTERfVElNRU9VVD05MDAKX0FQUF9GVU5DVElPTlNfQ09OVEFJTkVSUz0xMApfQVBQX0ZVTkNUSU9OU19DUFVTPTAKX0FQUF9GVU5DVElPTlNfTUVNT1JZPTAKX0FQUF9GVU5DVElPTlNfTUVNT1JZX1NXQVA9MApfQVBQX0ZVTkNUSU9OU19SVU5USU1FUz1ub2RlLTE2LjAscGhwLTguMCxweXRob24tMy45LHJ1YnktMy4wCl9BUFBfRVhFQ1VUT1JfU0VDUkVUPXlvdXItc2VjcmV0LWtleQpfQVBQX0VYRUNVVE9SX0hPU1Q9aHR0cDovL2FwcHdyaXRlLWV4ZWN1dG9yL3YxCl9BUFBfRVhFQ1VUT1JfUlVOVElNRV9ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX0VOVlM9bm9kZS0xNi4wLHBocC03LjQscHl0aG9uLTMuOSxydWJ5LTMuMApfQVBQX0ZVTkNUSU9OU19JTkFDVElWRV9USFJFU0hPTEQ9NjAKRE9DS0VSSFVCX1BVTExfVVNFUk5BTUU9CkRPQ0tFUkhVQl9QVUxMX1BBU1NXT1JEPQpET0NLRVJIVUJfUFVMTF9FTUFJTD0KT1BFTl9SVU5USU1FU19ORVRXT1JLPWFwcHdyaXRlX3J1bnRpbWVzCl9BUFBfRlVOQ1RJT05TX1JVTlRJTUVTX05FVFdPUks9cnVudGltZXMKX0FQUF9ET0NLRVJfSFVCX1VTRVJOQU1FPQpfQVBQX0RPQ0tFUl9IVUJfUEFTU1dPUkQ9Cl9BUFBfRlVOQ1RJT05TX01BSU5URU5BTkNFX0lOVEVSVkFMPTM2MDAKX0FQUF9WQ1NfR0lUSFVCX0FQUF9OQU1FPQpfQVBQX1ZDU19HSVRIVUJfUFJJVkFURV9LRVk9Cl9BUFBfVkNTX0dJVEhVQl9BUFBfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfSUQ9Cl9BUFBfVkNTX0dJVEhVQl9DTElFTlRfU0VDUkVUPQpfQVBQX1ZDU19HSVRIVUJfV0VCSE9PS19TRUNSRVQ9Cl9BUFBfTUFJTlRFTkFOQ0VfSU5URVJWQUw9ODY0MDAKX0FQUF9NQUlOVEVOQU5DRV9SRVRFTlRJT05fQ0FDSEU9MjU5MjAwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9FWEVDVVRJT049MTIwOTYwMApfQVBQX01BSU5URU5BTkNFX1JFVEVOVElPTl9BVURJVD0xMjA5NjAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX0FCVVNFPTg2NDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1VTQUdFX0hPVVJMWT04NjQwMDAwCl9BUFBfTUFJTlRFTkFOQ0VfUkVURU5USU9OX1NDSEVEVUxFUz04NjQwMApfQVBQX0dSQVBIUUxfTUFYX0JBVENIX1NJWkU9MTAKX0FQUF9HUkFQSFFMX01BWF9DT01QTEVYSVRZPTI1MApfQVBQX0dSQVBIUUxfTUFYX0RFUFRIPTMKX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9JRD0KX0FQUF9NSUdSQVRJT05TX0ZJUkVCQVNFX0NMSUVOVF9TRUNSRVQ9Cl9BUFBfQVNTSVNUQU5UX09QRU5BSV9BUElfS0VZPQo="
},
"babybuddy": {
"documentation": "https:\/\/docs.baby-buddy.net",
"slogan": "Baby Buddy is an open-source web application that helps parents track their baby's daily activities, growth, and health with ease.",
- "compose": "c2VydmljZXM6CiAgYmFieWJ1ZGR5OgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2JhYnlidWRkeTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIENTUkZfVFJVU1RFRF9PUklHSU5TPSRTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICB2b2x1bWVzOgogICAgICAtICdiYWJ5YnVkZHktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo="
+ "compose": "c2VydmljZXM6CiAgYmFieWJ1ZGR5OgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL2JhYnlidWRkeTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIENTUkZfVFJVU1RFRF9PUklHSU5TPSRTRVJWSUNFX0ZRRE5fQkFCWUJVRERZCiAgICB2b2x1bWVzOgogICAgICAtICdiYWJ5YnVkZHktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAwMCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
+ "tags": [
+ "baby",
+ "parents",
+ "health",
+ "growth",
+ "activities"
+ ]
},
"code-server": {
"documentation": "https:\/\/coder.com\/docs\/code-server\/latest\/guide",
"slogan": "Code-Server is a self-hosted, web-based code editor that enables remote coding and collaboration from any device, anywhere.",
- "compose": "c2VydmljZXM6CiAgY29kZS1zZXJ2ZXI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvY29kZS1zZXJ2ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVTRVJWRVIKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfUEFTU1dPUkRDT0RFU0VSVkVSCiAgICAgIC0gU1VET19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9TVURPQ09ERVNFUlZFUgogICAgICAtIERFRkFVTFRfV09SS1NQQUNFPS9jb25maWcvd29ya3NwYWNlCiAgICB2b2x1bWVzOgogICAgICAtICdjb2RlLXNlcnZlci1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4NDQzJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg=="
+ "compose": "c2VydmljZXM6CiAgY29kZS1zZXJ2ZXI6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvY29kZS1zZXJ2ZXI6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NPREVTRVJWRVIKICAgICAgLSBQVUlEPTEwMDAKICAgICAgLSBQR0lEPTEwMDAKICAgICAgLSBUWj1FdXJvcGUvTWFkcmlkCiAgICAgIC0gUEFTU1dPUkQ9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfUEFTU1dPUkRDT0RFU0VSVkVSCiAgICAgIC0gU1VET19QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9TVURPQ09ERVNFUlZFUgogICAgICAtIERFRkFVTFRfV09SS1NQQUNFPS9jb25maWcvd29ya3NwYWNlCiAgICB2b2x1bWVzOgogICAgICAtICdjb2RlLXNlcnZlci1jb25maWc6L2NvbmZpZycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4NDQzJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "tags": [
+ "code",
+ "editor",
+ "remote",
+ "collaboration"
+ ]
},
"dokuwiki": {
"documentation": "https:\/\/www.dokuwiki.org\/faq",
"slogan": "A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases with simplicity and flexibility.",
- "compose": "c2VydmljZXM6CiAgZG9rdXdpa2k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZG9rdXdpa2k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RPS1VXSUtJCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZG9rdXdpa2ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK"
+ "compose": "c2VydmljZXM6CiAgZG9rdXdpa2k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZG9rdXdpa2k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0RPS1VXSUtJCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZG9rdXdpa2ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "wiki",
+ "documentation",
+ "knowledge",
+ "base"
+ ]
},
"fider": {
"documentation": "https:\/\/fider.io\/doc",
"slogan": "Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.",
- "compose": "c2VydmljZXM6CiAgZmlkZXI6CiAgICBpbWFnZTogJ2dldGZpZGVyL2ZpZGVyOnN0YWJsZScKICAgIGVudmlyb25tZW50OgogICAgICBCQVNFX1VSTDogJFNFUlZJQ0VfRlFETl9GSURFUgogICAgICBEQVRBQkFTRV9VUkw6ICdwb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfTVlTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUxAZGF0YWJhc2U6NTQzMi9maWRlcj9zc2xtb2RlPWRpc2FibGUnCiAgICAgIEpXVF9TRUNSRVQ6ICRTRVJWSUNFX1BBU1NXT1JEXzY0X0ZJREVSCiAgICAgIEVNQUlMX05PUkVQTFk6ICcke0VNQUlMX05PUkVQTFk6LW5vcmVwbHlAZXhhbXBsZS5jb219JwogICAgICBFTUFJTF9NQUlMR1VOX0FQSTogJEVNQUlMX01BSUxHVU5fQVBJCiAgICAgIEVNQUlMX01BSUxHVU5fRE9NQUlOOiAkRU1BSUxfTUFJTEdVTl9ET01BSU4KICAgICAgRU1BSUxfTUFJTEdVTl9SRUdJT046ICRFTUFJTF9NQUlMR1VOX1JFR0lPTgogICAgICBFTUFJTF9TTVRQX0hPU1Q6ICcke0VNQUlMX1NNVFBfSE9TVDotc210cC5tYWlsZ3VuLmNvbX0nCiAgICAgIEVNQUlMX1NNVFBfUE9SVDogJyR7RU1BSUxfU01UUF9QT1JUOi01ODd9JwogICAgICBFTUFJTF9TTVRQX1VTRVJOQU1FOiAnJHtFTUFJTF9TTVRQX1VTRVJOQU1FOi1wb3N0bWFzdGVyQG1haWxndW4uY29tfScKICAgICAgRU1BSUxfU01UUF9QQVNTV09SRDogJEVNQUlMX1NNVFBfUEFTU1dPUkQKICAgICAgRU1BSUxfU01UUF9FTkFCTEVfU1RBUlRUTFM6ICRFTUFJTF9TTVRQX0VOQUJMRV9TVEFSVFRMUwogICAgICBFTUFJTF9BV1NTRVNfUkVHSU9OOiAkRU1BSUxfQVdTU0VTX1JFR0lPTgogICAgICBFTUFJTF9BV1NTRVNfQUNDRVNTX0tFWV9JRDogJEVNQUlMX0FXU1NFU19BQ0NFU1NfS0VZX0lECiAgICAgIEVNQUlMX0FXU1NFU19TRUNSRVRfQUNDRVNTX0tFWTogJEVNQUlMX0FXU1NFU19TRUNSRVRfQUNDRVNTX0tFWQogIGRhdGFiYXNlOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnX2RhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIFBPU1RHUkVTX1VTRVI6ICRTRVJWSUNFX1VTRVJfTVlTUUwKICAgICAgUE9TVEdSRVNfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIFBPU1RHUkVTX0RCOiAnJHtQT1NUR1JFU19EQjotZmlkZXJ9Jwo="
+ "compose": "c2VydmljZXM6CiAgZmlkZXI6CiAgICBpbWFnZTogJ2dldGZpZGVyL2ZpZGVyOnN0YWJsZScKICAgIGVudmlyb25tZW50OgogICAgICBCQVNFX1VSTDogJFNFUlZJQ0VfRlFETl9GSURFUgogICAgICBEQVRBQkFTRV9VUkw6ICdwb3N0Z3JlczovLyRTRVJWSUNFX1VTRVJfTVlTUUw6JFNFUlZJQ0VfUEFTU1dPUkRfTVlTUUxAZGF0YWJhc2U6NTQzMi9maWRlcj9zc2xtb2RlPWRpc2FibGUnCiAgICAgIEpXVF9TRUNSRVQ6ICRTRVJWSUNFX1BBU1NXT1JEXzY0X0ZJREVSCiAgICAgIEVNQUlMX05PUkVQTFk6ICcke0VNQUlMX05PUkVQTFk6LW5vcmVwbHlAZXhhbXBsZS5jb219JwogICAgICBFTUFJTF9NQUlMR1VOX0FQSTogJEVNQUlMX01BSUxHVU5fQVBJCiAgICAgIEVNQUlMX01BSUxHVU5fRE9NQUlOOiAkRU1BSUxfTUFJTEdVTl9ET01BSU4KICAgICAgRU1BSUxfTUFJTEdVTl9SRUdJT046ICRFTUFJTF9NQUlMR1VOX1JFR0lPTgogICAgICBFTUFJTF9TTVRQX0hPU1Q6ICcke0VNQUlMX1NNVFBfSE9TVDotc210cC5tYWlsZ3VuLmNvbX0nCiAgICAgIEVNQUlMX1NNVFBfUE9SVDogJyR7RU1BSUxfU01UUF9QT1JUOi01ODd9JwogICAgICBFTUFJTF9TTVRQX1VTRVJOQU1FOiAnJHtFTUFJTF9TTVRQX1VTRVJOQU1FOi1wb3N0bWFzdGVyQG1haWxndW4uY29tfScKICAgICAgRU1BSUxfU01UUF9QQVNTV09SRDogJEVNQUlMX1NNVFBfUEFTU1dPUkQKICAgICAgRU1BSUxfU01UUF9FTkFCTEVfU1RBUlRUTFM6ICRFTUFJTF9TTVRQX0VOQUJMRV9TVEFSVFRMUwogICAgICBFTUFJTF9BV1NTRVNfUkVHSU9OOiAkRU1BSUxfQVdTU0VTX1JFR0lPTgogICAgICBFTUFJTF9BV1NTRVNfQUNDRVNTX0tFWV9JRDogJEVNQUlMX0FXU1NFU19BQ0NFU1NfS0VZX0lECiAgICAgIEVNQUlMX0FXU1NFU19TRUNSRVRfQUNDRVNTX0tFWTogJEVNQUlMX0FXU1NFU19TRUNSRVRfQUNDRVNTX0tFWQogIGRhdGFiYXNlOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxMicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3BnX2RhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIFBPU1RHUkVTX1VTRVI6ICRTRVJWSUNFX1VTRVJfTVlTUUwKICAgICAgUE9TVEdSRVNfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX01ZU1FMCiAgICAgIFBPU1RHUkVTX0RCOiAnJHtQT1NUR1JFU19EQjotZmlkZXJ9Jwo=",
+ "tags": [
+ "feedback",
+ "user-feedback"
+ ]
},
"ghost": {
"documentation": "https:\/\/ghost.org\/docs",
"slogan": "Ghost is a popular open-source content management system (CMS) and blogging platform, known for its simplicity and focus on content creation.",
- "compose": "c2VydmljZXM6CiAgZ2hvc3Q6CiAgICBpbWFnZTogJ2dob3N0OjUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1jb250ZW50LWRhdGE6L3Zhci9saWIvZ2hvc3QvY29udGVudCcKICAgIGVudmlyb25tZW50OgogICAgICAtIHVybD0kU0VSVklDRV9GUUROX0dIT1NUCiAgICAgIC0gZGF0YWJhc2VfX2NsaWVudD1teXNxbAogICAgICAtIGRhdGFiYXNlX19jb25uZWN0aW9uX19ob3N0PW15c3FsCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX3VzZXI9JFNFUlZJQ0VfVVNFUl9NWVNRTAogICAgICAtIGRhdGFiYXNlX19jb25uZWN0aW9uX19wYXNzd29yZD0kU0VSVklDRV9QQVNTV09SRF9NWVNRTAogICAgICAtICdkYXRhYmFzZV9fY29ubmVjdGlvbl9fZGF0YWJhc2U9JHtNWVNRTF9EQVRBQkFTRS1naG9zdH0nCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSBsb2NhbGhvc3QKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCg=="
+ "compose": "c2VydmljZXM6CiAgZ2hvc3Q6CiAgICBpbWFnZTogJ2dob3N0OjUnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1jb250ZW50LWRhdGE6L3Zhci9saWIvZ2hvc3QvY29udGVudCcKICAgIGVudmlyb25tZW50OgogICAgICAtIHVybD0kU0VSVklDRV9GUUROX0dIT1NUCiAgICAgIC0gZGF0YWJhc2VfX2NsaWVudD1teXNxbAogICAgICAtIGRhdGFiYXNlX19jb25uZWN0aW9uX19ob3N0PW15c3FsCiAgICAgIC0gZGF0YWJhc2VfX2Nvbm5lY3Rpb25fX3VzZXI9JFNFUlZJQ0VfVVNFUl9NWVNRTAogICAgICAtIGRhdGFiYXNlX19jb25uZWN0aW9uX19wYXNzd29yZD0kU0VSVklDRV9QQVNTV09SRF9NWVNRTAogICAgICAtICdkYXRhYmFzZV9fY29ubmVjdGlvbl9fZGF0YWJhc2U9JHtNWVNRTF9EQVRBQkFTRS1naG9zdH0nCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICB2b2x1bWVzOgogICAgICAtICdnaG9zdC1teXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFfScKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSBsb2NhbGhvc3QKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCg==",
+ "tags": [
+ "cms",
+ "blog",
+ "content",
+ "management",
+ "system"
+ ]
},
"heimdall": {
"documentation": "https:\/\/github.com\/linuxserver\/Heimdall",
"slogan": "Heimdall is a self-hosted dashboard for managing and organizing your server applications, providing a centralized and efficient interface.",
- "compose": "c2VydmljZXM6CiAgaGVpbWRhbGw6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvaGVpbWRhbGw6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hFSU1EQUxMCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnaGVpbWRhbGwtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK"
+ "compose": "c2VydmljZXM6CiAgaGVpbWRhbGw6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvaGVpbWRhbGw6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hFSU1EQUxMCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnaGVpbWRhbGwtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "dashboard",
+ "server",
+ "applications",
+ "interface"
+ ]
},
"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": "c2VydmljZXM6CiAgbWV0dWJlOgogICAgaW1hZ2U6ICdnaGNyLmlvL2FsZXh0YTY5L21ldHViZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTUVUVUJFCiAgICAgIC0gVUlEPTEwMDAKICAgICAgLSBHSUQ9MTAwMAogICAgdm9sdW1lczoKICAgICAgLSAnbWV0dWJlLWRvd25sb2FkczovZG93bmxvYWRzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwODEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "youtube",
+ "download",
+ "videos",
+ "playlist"
+ ]
},
"minio": {
"documentation": "https:\/\/docs.min.io\/docs\/minio-docker-quickstart-guide.html",
"slogan": "MinIO is a high performance object storage server compatible with Amazon S3 APIs.",
- "compose": "c2VydmljZXM6CiAgbWluaW86CiAgICBpbWFnZTogJ3F1YXkuaW8vbWluaW8vbWluaW86bGF0ZXN0JwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIFNFUlZJQ0VfRlFETl9NSU5JT185MDAwOiBudWxsCiAgICAgIFNFUlZJQ0VfRlFETl9DT05TT0xFXzkwMDE6IG51bGwKICAgICAgTUlOSU9fUk9PVF9VU0VSOiAkU0VSVklDRV9VU0VSX01JTklPCiAgICAgIE1JTklPX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX01JTklPCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjkwMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK"
+ "compose": "c2VydmljZXM6CiAgbWluaW86CiAgICBpbWFnZTogJ3F1YXkuaW8vbWluaW8vbWluaW86bGF0ZXN0JwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWNvbnNvbGUtYWRkcmVzcyAiOjkwMDEiJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIFNFUlZJQ0VfRlFETl9NSU5JT185MDAwOiBudWxsCiAgICAgIFNFUlZJQ0VfRlFETl9DT05TT0xFXzkwMDE6IG51bGwKICAgICAgTUlOSU9fUk9PVF9VU0VSOiAkU0VSVklDRV9VU0VSX01JTklPCiAgICAgIE1JTklPX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX01JTklPCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjkwMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMTAK",
+ "tags": [
+ "object",
+ "storage",
+ "server",
+ "s3",
+ "api"
+ ]
},
"pairdrop": {
"documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop\/blob\/master\/docs\/faq.md",
"slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.",
- "compose": "c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIERFQlVHX01PREU9ZmFsc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg=="
+ "compose": "c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIERFQlVHX01PREU9ZmFsc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "tags": [
+ "file",
+ "sharing",
+ "collaboration",
+ "teamwork"
+ ]
},
"snapdrop": {
"documentation": "https:\/\/github.com\/RobinLinus\/snapdrop\/blob\/master\/docs\/faq.md",
"slogan": "A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.",
- "compose": "c2VydmljZXM6CiAgc25hcGRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvc25hcGRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NOQVBEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnc25hcGRyb3AtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK"
+ "compose": "c2VydmljZXM6CiAgc25hcGRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvc25hcGRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NOQVBEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnc25hcGRyb3AtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "file",
+ "sharing",
+ "transfer",
+ "local",
+ "network",
+ "internet"
+ ]
},
"umami": {
"documentation": "https:\/\/umami.is\/docs\/getting-started",
"slogan": "Umami is a lightweight, self-hosted web analytics platform designed to provide website owners with insights into visitor behavior without compromising user privacy.",
- "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6cG9zdGdyZXNxbC1sYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUkKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlc3FsOjU0MzIvJFBPU1RHUkVTX0RCJwogICAgICAtIERBVEFCQVNFX1RZUEU9cG9zdGdyZXMKICAgICAgLSBBUFBfU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEXzY0X1VNQU1JCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXVtYW1pfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAo="
+ "compose": "c2VydmljZXM6CiAgdW1hbWk6CiAgICBpbWFnZTogJ2doY3IuaW8vdW1hbWktc29mdHdhcmUvdW1hbWk6cG9zdGdyZXNxbC1sYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fVU1BTUkKICAgICAgLSAnREFUQUJBU0VfVVJMPXBvc3RncmVzOi8vJFNFUlZJQ0VfVVNFUl9QT1NUR1JFUzokU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU0Bwb3N0Z3Jlc3FsOjU0MzIvJFBPU1RHUkVTX0RCJwogICAgICAtIERBVEFCQVNFX1RZUEU9cG9zdGdyZXMKICAgICAgLSBBUFBfU0VDUkVUPSRTRVJWSUNFX1BBU1NXT1JEXzY0X1VNQU1JCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3Jlc3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXVtYW1pfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAo=",
+ "tags": [
+ "analytics",
+ "insights",
+ "privacy"
+ ]
},
"uptime-kuma": {
"documentation": "https:\/\/github.com\/louislam\/uptime-kuma\/wiki",
"slogan": "Uptime Kuma is a free, self-hosted monitoring tool for tracking the status and performance of your web services and applications in real-time.",
- "compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwdGltZS1rdW1hOi9hcHAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSBleHRyYS9oZWFsdGhjaGVjawogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg=="
+ "compose": "c2VydmljZXM6CiAgdXB0aW1lLWt1bWE6CiAgICBpbWFnZTogJ2xvdWlzbGFtL3VwdGltZS1rdW1hOjEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgIHZvbHVtZXM6CiAgICAgIC0gJ3VwdGltZS1rdW1hOi9hcHAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSBleHRyYS9oZWFsdGhjaGVjawogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "tags": [
+ "monitoring",
+ "status",
+ "performance",
+ "web",
+ "services",
+ "applications",
+ "real-time"
+ ]
},
"wordpress-with-mariadb": {
"documentation": "https:\/\/wordpress.org\/documentation\/",
"slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"",
- "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBtYXJpYWRiCiAgICAgIFdPUkRQUkVTU19EQl9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfTkFNRTogd29yZHByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg=="
+ "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBtYXJpYWRiCiAgICAgIFdPUkRQUkVTU19EQl9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfTkFNRTogd29yZHByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==",
+ "tags": [
+ "cms",
+ "blog",
+ "content",
+ "management",
+ "mariadb"
+ ]
},
"wordpress-with-mysql": {
"documentation": "https:\/\/wordpress.org\/documentation\/",
"slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"",
- "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBteXNxbAogICAgICBXT1JEUFJFU1NfREJfVVNFUjogJFNFUlZJQ0VfVVNFUl9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX1BBU1NXT1JEOiAkU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX05BTUU6IHdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBteXNxbAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg=="
+ "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBteXNxbAogICAgICBXT1JEUFJFU1NfREJfVVNFUjogJFNFUlZJQ0VfVVNFUl9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX1BBU1NXT1JEOiAkU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX05BTUU6IHdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBteXNxbAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==",
+ "tags": [
+ "cms",
+ "blog",
+ "content",
+ "management",
+ "mysql"
+ ]
},
"wordpress-without-database": {
"documentation": "https:\/\/wordpress.org\/documentation\/",
"slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"",
- "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCg=="
+ "compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCg==",
+ "tags": [
+ "cms",
+ "blog",
+ "content",
+ "management"
+ ]
}
}
\ No newline at end of file
From f801bb98cd11c1d43881ab3d23af5fd1226cca13 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 14:31:28 +0200
Subject: [PATCH 15/29] feat: mysql, mariadb
---
app/Actions/Database/StartDatabaseProxy.php | 8 +-
app/Actions/Database/StartMariadb.php | 158 ++++++++++++++++++
app/Actions/Database/StartMysql.php | 158 ++++++++++++++++++
app/Actions/Database/StopDatabase.php | 4 +-
app/Actions/Database/StopDatabaseProxy.php | 4 +-
app/Http/Controllers/ProjectController.php | 8 +-
app/Http/Livewire/Project/CloneProject.php | 4 +
.../Project/Database/BackupExecution.php | 23 ---
.../Project/Database/BackupExecutions.php | 15 +-
.../Database/CreateScheduledBackup.php | 4 +
.../Livewire/Project/Database/Heading.php | 14 +-
.../Project/Database/Mariadb/General.php | 95 +++++++++++
.../Project/Database/Mongodb/General.php | 11 +-
.../Project/Database/Mysql/General.php | 95 +++++++++++
.../Project/Database/Postgresql/General.php | 9 +-
.../Project/Database/Redis/General.php | 11 +-
.../Shared/EnvironmentVariable/All.php | 6 +
app/Http/Livewire/Project/Shared/Logs.php | 13 +-
app/Jobs/DatabaseBackupJob.php | 72 +++++++-
app/Jobs/StopResourceJob.php | 10 +-
app/Models/Environment.php | 12 +-
app/Models/Project.php | 12 ++
app/Models/Server.php | 13 +-
app/Models/StandaloneDocker.php | 18 ++
app/Models/StandaloneMariadb.php | 106 ++++++++++++
app/Models/StandaloneMysql.php | 106 ++++++++++++
app/Models/StandalonePostgresql.php | 2 -
bootstrap/helpers/constants.php | 2 +-
bootstrap/helpers/databases.php | 32 ++++
..._103548_create_standalone_mysqls_table.php | 57 +++++++
...20523_create_standalone_mariadbs_table.php | 57 +++++++
...e_mysql_to_environment_variables_table.php | 30 ++++
.../components/databases/navbar.blade.php | 6 +-
.../project/database/backup-edit.blade.php | 8 +
.../database/backup-execution.blade.php | 8 -
.../database/backup-executions.blade.php | 26 ++-
.../database/mariadb/general.blade.php | 58 +++++++
.../project/database/mysql/general.blade.php | 58 +++++++
.../livewire/project/new/select.blade.php | 20 +++
.../database/backups/executions.blade.php | 2 +-
.../project/database/configuration.blade.php | 42 +++--
41 files changed, 1309 insertions(+), 88 deletions(-)
create mode 100644 app/Actions/Database/StartMariadb.php
create mode 100644 app/Actions/Database/StartMysql.php
delete mode 100644 app/Http/Livewire/Project/Database/BackupExecution.php
create mode 100644 app/Http/Livewire/Project/Database/Mariadb/General.php
create mode 100644 app/Http/Livewire/Project/Database/Mysql/General.php
create mode 100644 app/Models/StandaloneMariadb.php
create mode 100644 app/Models/StandaloneMysql.php
create mode 100644 database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php
create mode 100644 database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php
create mode 100644 database/migrations/2023_10_24_120524_add_standalone_mysql_to_environment_variables_table.php
delete mode 100644 resources/views/livewire/project/database/backup-execution.blade.php
create mode 100644 resources/views/livewire/project/database/mariadb/general.blade.php
create mode 100644 resources/views/livewire/project/database/mysql/general.blade.php
diff --git a/app/Actions/Database/StartDatabaseProxy.php b/app/Actions/Database/StartDatabaseProxy.php
index 15009019d..ffc91b86a 100644
--- a/app/Actions/Database/StartDatabaseProxy.php
+++ b/app/Actions/Database/StartDatabaseProxy.php
@@ -2,7 +2,9 @@
namespace App\Actions\Database;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -12,7 +14,7 @@ class StartDatabaseProxy
{
use AsAction;
- public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
+ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
{
$internalPort = null;
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
@@ -21,6 +23,10 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $d
$internalPort = 5432;
} else if ($database->getMorphClass() === 'App\Models\StandaloneMongodb') {
$internalPort = 27017;
+ } else if ($database->getMorphClass() === 'App\Models\StandaloneMysql') {
+ $internalPort = 3306;
+ } else if ($database->getMorphClass() === 'App\Models\StandaloneMariadb') {
+ $internalPort = 3306;
}
$containerName = "{$database->uuid}-proxy";
$configuration_dir = database_proxy_dir($database->uuid);
diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php
new file mode 100644
index 000000000..75fd69adc
--- /dev/null
+++ b/app/Actions/Database/StartMariadb.php
@@ -0,0 +1,158 @@
+database = $database;
+
+ $container_name = $this->database->uuid;
+ $this->configuration_dir = database_configuration_dir() . '/' . $container_name;
+
+ $this->commands = [
+ "echo '####### Starting {$database->name}.'",
+ "mkdir -p $this->configuration_dir",
+ ];
+
+ $persistent_storages = $this->generate_local_persistent_volumes();
+ $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
+ $environment_variables = $this->generate_environment_variables();
+ $this->add_custom_mysql();
+ $docker_compose = [
+ 'version' => '3.8',
+ 'services' => [
+ $container_name => [
+ 'image' => $this->database->image,
+ 'container_name' => $container_name,
+ 'environment' => $environment_variables,
+ 'restart' => RESTART_MODE,
+ 'networks' => [
+ $this->database->destination->network,
+ ],
+ 'labels' => [
+ 'coolify.managed' => 'true',
+ ],
+ 'healthcheck' => [
+ 'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"],
+ 'interval' => '5s',
+ 'timeout' => '5s',
+ 'retries' => 10,
+ 'start_period' => '5s'
+ ],
+ 'mem_limit' => $this->database->limits_memory,
+ 'memswap_limit' => $this->database->limits_memory_swap,
+ 'mem_swappiness' => $this->database->limits_memory_swappiness,
+ 'mem_reservation' => $this->database->limits_memory_reservation,
+ 'cpus' => $this->database->limits_cpus,
+ 'cpuset' => $this->database->limits_cpuset,
+ 'cpu_shares' => $this->database->limits_cpu_shares,
+ ]
+ ],
+ 'networks' => [
+ $this->database->destination->network => [
+ 'external' => true,
+ 'name' => $this->database->destination->network,
+ 'attachable' => true,
+ ]
+ ]
+ ];
+ if (count($this->database->ports_mappings_array) > 0) {
+ $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
+ }
+ if (count($persistent_storages) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
+ }
+ if (count($volume_names) > 0) {
+ $docker_compose['volumes'] = $volume_names;
+ }
+ if (!is_null($this->database->mariadb_conf)) {
+ $docker_compose['services'][$container_name]['volumes'][] = [
+ 'type' => 'bind',
+ 'source' => $this->configuration_dir . '/custom-config.cnf',
+ 'target' => '/etc/mysql/conf.d/custom-config.cnf',
+ 'read_only' => true,
+ ];
+ }
+ $docker_compose = Yaml::dump($docker_compose, 10);
+ $docker_compose_base64 = base64_encode($docker_compose);
+ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
+ $readme = generate_readme_file($this->database->name, now());
+ $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
+ $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
+ $this->commands[] = "echo '####### {$database->name} started.'";
+ return remote_process($this->commands, $database->destination->server);
+ }
+
+ private function generate_local_persistent_volumes()
+ {
+ $local_persistent_volumes = [];
+ foreach ($this->database->persistentStorages as $persistentStorage) {
+ $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
+ $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
+ }
+ return $local_persistent_volumes;
+ }
+
+ private function generate_local_persistent_volumes_only_volume_names()
+ {
+ $local_persistent_volumes_names = [];
+ foreach ($this->database->persistentStorages as $persistentStorage) {
+ if ($persistentStorage->host_path) {
+ continue;
+ }
+ $name = $persistentStorage->name;
+ $local_persistent_volumes_names[$name] = [
+ 'name' => $name,
+ 'external' => false,
+ ];
+ }
+ return $local_persistent_volumes_names;
+ }
+
+ private function generate_environment_variables()
+ {
+ $environment_variables = collect();
+ foreach ($this->database->runtime_environment_variables as $env) {
+ $environment_variables->push("$env->key=$env->value");
+ }
+
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
+ $environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
+ }
+
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
+ $environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
+ }
+
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_USER'))->isEmpty()) {
+ $environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
+ }
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
+ $environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
+ }
+ return $environment_variables->all();
+ }
+ private function add_custom_mysql()
+ {
+ if (is_null($this->database->mariadb_conf)) {
+ return;
+ }
+ $filename = 'custom-config.cnf';
+ $content = $this->database->mariadb_conf;
+ $content_base64 = base64_encode($content);
+ $this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
+ }
+}
diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php
new file mode 100644
index 000000000..8ee0db6e9
--- /dev/null
+++ b/app/Actions/Database/StartMysql.php
@@ -0,0 +1,158 @@
+database = $database;
+
+ $container_name = $this->database->uuid;
+ $this->configuration_dir = database_configuration_dir() . '/' . $container_name;
+
+ $this->commands = [
+ "echo '####### Starting {$database->name}.'",
+ "mkdir -p $this->configuration_dir",
+ ];
+
+ $persistent_storages = $this->generate_local_persistent_volumes();
+ $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
+ $environment_variables = $this->generate_environment_variables();
+ $this->add_custom_mysql();
+ $docker_compose = [
+ 'version' => '3.8',
+ 'services' => [
+ $container_name => [
+ 'image' => $this->database->image,
+ 'container_name' => $container_name,
+ 'environment' => $environment_variables,
+ 'restart' => RESTART_MODE,
+ 'networks' => [
+ $this->database->destination->network,
+ ],
+ 'labels' => [
+ 'coolify.managed' => 'true',
+ ],
+ 'healthcheck' => [
+ 'test' => ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p{$this->database->mysql_root_password}"],
+ 'interval' => '5s',
+ 'timeout' => '5s',
+ 'retries' => 10,
+ 'start_period' => '5s'
+ ],
+ 'mem_limit' => $this->database->limits_memory,
+ 'memswap_limit' => $this->database->limits_memory_swap,
+ 'mem_swappiness' => $this->database->limits_memory_swappiness,
+ 'mem_reservation' => $this->database->limits_memory_reservation,
+ 'cpus' => $this->database->limits_cpus,
+ 'cpuset' => $this->database->limits_cpuset,
+ 'cpu_shares' => $this->database->limits_cpu_shares,
+ ]
+ ],
+ 'networks' => [
+ $this->database->destination->network => [
+ 'external' => true,
+ 'name' => $this->database->destination->network,
+ 'attachable' => true,
+ ]
+ ]
+ ];
+ if (count($this->database->ports_mappings_array) > 0) {
+ $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
+ }
+ if (count($persistent_storages) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
+ }
+ if (count($volume_names) > 0) {
+ $docker_compose['volumes'] = $volume_names;
+ }
+ if (!is_null($this->database->mysql_conf)) {
+ $docker_compose['services'][$container_name]['volumes'][] = [
+ 'type' => 'bind',
+ 'source' => $this->configuration_dir . '/custom-config.cnf',
+ 'target' => '/etc/mysql/conf.d/custom-config.cnf',
+ 'read_only' => true,
+ ];
+ }
+ $docker_compose = Yaml::dump($docker_compose, 10);
+ $docker_compose_base64 = base64_encode($docker_compose);
+ $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
+ $readme = generate_readme_file($this->database->name, now());
+ $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
+ $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
+ $this->commands[] = "echo '####### {$database->name} started.'";
+ return remote_process($this->commands, $database->destination->server);
+ }
+
+ private function generate_local_persistent_volumes()
+ {
+ $local_persistent_volumes = [];
+ foreach ($this->database->persistentStorages as $persistentStorage) {
+ $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
+ $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
+ }
+ return $local_persistent_volumes;
+ }
+
+ private function generate_local_persistent_volumes_only_volume_names()
+ {
+ $local_persistent_volumes_names = [];
+ foreach ($this->database->persistentStorages as $persistentStorage) {
+ if ($persistentStorage->host_path) {
+ continue;
+ }
+ $name = $persistentStorage->name;
+ $local_persistent_volumes_names[$name] = [
+ 'name' => $name,
+ 'external' => false,
+ ];
+ }
+ return $local_persistent_volumes_names;
+ }
+
+ private function generate_environment_variables()
+ {
+ $environment_variables = collect();
+ foreach ($this->database->runtime_environment_variables as $env) {
+ $environment_variables->push("$env->key=$env->value");
+ }
+
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
+ $environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
+ }
+
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
+ $environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
+ }
+
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_USER'))->isEmpty()) {
+ $environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
+ }
+ if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
+ $environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
+ }
+ return $environment_variables->all();
+ }
+ private function add_custom_mysql()
+ {
+ if (is_null($this->database->mysql_conf)) {
+ return;
+ }
+ $filename = 'custom-config.cnf';
+ $content = $this->database->mysql_conf;
+ $content_base64 = base64_encode($content);
+ $this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
+ }
+}
diff --git a/app/Actions/Database/StopDatabase.php b/app/Actions/Database/StopDatabase.php
index 7e3f5f4c2..4f6a8c6c2 100644
--- a/app/Actions/Database/StopDatabase.php
+++ b/app/Actions/Database/StopDatabase.php
@@ -2,7 +2,9 @@
namespace App\Actions\Database;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -11,7 +13,7 @@ class StopDatabase
{
use AsAction;
- public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
+ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
{
$server = $database->destination->server;
instant_remote_process(
diff --git a/app/Actions/Database/StopDatabaseProxy.php b/app/Actions/Database/StopDatabaseProxy.php
index 840e8ed56..d52d1961c 100644
--- a/app/Actions/Database/StopDatabaseProxy.php
+++ b/app/Actions/Database/StopDatabaseProxy.php
@@ -2,7 +2,9 @@
namespace App\Actions\Database;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -11,7 +13,7 @@ class StopDatabaseProxy
{
use AsAction;
- public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
+ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
{
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server);
$database->is_public = false;
diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php
index 1d1a5b14e..0e3983f7e 100644
--- a/app/Http/Controllers/ProjectController.php
+++ b/app/Http/Controllers/ProjectController.php
@@ -63,8 +63,12 @@ public function new()
$database = create_standalone_postgresql($environment->id, $destination_uuid);
} else if ($type->value() === 'redis') {
$database = create_standalone_redis($environment->id, $destination_uuid);
- } else if ($type->value() === 'mongodb') {
+ } else if ($type->value() === 'mongodb') {
$database = create_standalone_mongodb($environment->id, $destination_uuid);
+ } else if ($type->value() === 'mysql') {
+ $database = create_standalone_mysql($environment->id, $destination_uuid);
+ }else if ($type->value() === 'mariadb') {
+ $database = create_standalone_mariadb($environment->id, $destination_uuid);
}
return redirect()->route('project.database.configuration', [
'project_uuid' => $project->uuid,
@@ -72,7 +76,7 @@ public function new()
'database_uuid' => $database->uuid,
]);
}
- if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
+ if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
$oneClickServiceName = $type->after('one-click-service-')->value();
$oneClickService = data_get($services, "$oneClickServiceName.compose");
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
diff --git a/app/Http/Livewire/Project/CloneProject.php b/app/Http/Livewire/Project/CloneProject.php
index e12138d10..735bbc0da 100644
--- a/app/Http/Livewire/Project/CloneProject.php
+++ b/app/Http/Livewire/Project/CloneProject.php
@@ -117,6 +117,10 @@ public function clone()
$payload['standalone_redis_id'] = $newDatabase->id;
} else if ($database->type() === 'standalone_mongodb') {
$payload['standalone_mongodb_id'] = $newDatabase->id;
+ } else if ($database->type() === 'standalone_mysql') {
+ $payload['standalone_mysql_id'] = $newDatabase->id;
+ }else if ($database->type() === 'standalone_mariadb') {
+ $payload['standalone_mariadb_id'] = $newDatabase->id;
}
$newEnvironmentVariable = $environmentVarible->replicate()->fill($payload);
$newEnvironmentVariable->save();
diff --git a/app/Http/Livewire/Project/Database/BackupExecution.php b/app/Http/Livewire/Project/Database/BackupExecution.php
deleted file mode 100644
index 2f9d7dcb5..000000000
--- a/app/Http/Livewire/Project/Database/BackupExecution.php
+++ /dev/null
@@ -1,23 +0,0 @@
-execution->filename, $this->execution->scheduledDatabaseBackup->database->destination->server);
- $this->execution->delete();
- $this->emit('success', 'Backup deleted successfully.');
- $this->emit('refreshBackupExecutions');
- }
-}
diff --git a/app/Http/Livewire/Project/Database/BackupExecutions.php b/app/Http/Livewire/Project/Database/BackupExecutions.php
index 93da317f7..2f808d992 100644
--- a/app/Http/Livewire/Project/Database/BackupExecutions.php
+++ b/app/Http/Livewire/Project/Database/BackupExecutions.php
@@ -8,8 +8,21 @@ class BackupExecutions extends Component
{
public $backup;
public $executions;
- protected $listeners = ['refreshBackupExecutions'];
+ public $setDeletableBackup;
+ protected $listeners = ['refreshBackupExecutions', 'deleteBackup'];
+ public function deleteBackup($exeuctionId)
+ {
+ $execution = $this->backup->executions()->where('id', $exeuctionId)->first();
+ if (is_null($execution)) {
+ $this->emit('error', 'Backup execution not found.');
+ return;
+ }
+ delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
+ $execution->delete();
+ $this->emit('success', 'Backup deleted successfully.');
+ $this->emit('refreshBackupExecutions');
+ }
public function refreshBackupExecutions(): void
{
$this->executions = $this->backup->executions;
diff --git a/app/Http/Livewire/Project/Database/CreateScheduledBackup.php b/app/Http/Livewire/Project/Database/CreateScheduledBackup.php
index ac34e93bd..f804c389d 100644
--- a/app/Http/Livewire/Project/Database/CreateScheduledBackup.php
+++ b/app/Http/Livewire/Project/Database/CreateScheduledBackup.php
@@ -48,6 +48,10 @@ public function submit(): void
];
if ($this->database->type() === 'standalone-postgresql') {
$payload['databases_to_backup'] = $this->database->postgres_db;
+ } else if ($this->database->type() === 'standalone-mysql') {
+ $payload['databases_to_backup'] = $this->database->mysql_database;
+ }else if ($this->database->type() === 'standalone-mariadb') {
+ $payload['databases_to_backup'] = $this->database->mariadb_database;
}
ScheduledDatabaseBackup::create($payload);
$this->emit('refreshScheduledBackups');
diff --git a/app/Http/Livewire/Project/Database/Heading.php b/app/Http/Livewire/Project/Database/Heading.php
index 6045e2b7f..7b14e5368 100644
--- a/app/Http/Livewire/Project/Database/Heading.php
+++ b/app/Http/Livewire/Project/Database/Heading.php
@@ -2,7 +2,9 @@
namespace App\Http\Livewire\Project\Database;
+use App\Actions\Database\StartMariadb;
use App\Actions\Database\StartMongodb;
+use App\Actions\Database\StartMysql;
use App\Actions\Database\StartPostgresql;
use App\Actions\Database\StartRedis;
use App\Actions\Database\StopDatabase;
@@ -49,14 +51,18 @@ public function start()
if ($this->database->type() === 'standalone-postgresql') {
$activity = StartPostgresql::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
- }
- if ($this->database->type() === 'standalone-redis') {
+ } else if ($this->database->type() === 'standalone-redis') {
$activity = StartRedis::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
- }
- if ($this->database->type() === 'standalone-mongodb') {
+ } else if ($this->database->type() === 'standalone-mongodb') {
$activity = StartMongodb::run($this->database);
$this->emit('newMonitorActivity', $activity->id);
+ } else if ($this->database->type() === 'standalone-mysql') {
+ $activity = StartMysql::run($this->database);
+ $this->emit('newMonitorActivity', $activity->id);
+ } else if ($this->database->type() === 'standalone-mariadb') {
+ $activity = StartMariadb::run($this->database);
+ $this->emit('newMonitorActivity', $activity->id);
}
}
}
diff --git a/app/Http/Livewire/Project/Database/Mariadb/General.php b/app/Http/Livewire/Project/Database/Mariadb/General.php
new file mode 100644
index 000000000..4d04371d0
--- /dev/null
+++ b/app/Http/Livewire/Project/Database/Mariadb/General.php
@@ -0,0 +1,95 @@
+ 'required',
+ 'database.description' => 'nullable',
+ 'database.mariadb_root_password' => 'required',
+ 'database.mariadb_user' => 'required',
+ 'database.mariadb_password' => 'required',
+ 'database.mariadb_database' => 'required',
+ 'database.mariadb_conf' => 'nullable',
+ 'database.image' => 'required',
+ 'database.ports_mappings' => 'nullable',
+ 'database.is_public' => 'nullable|boolean',
+ 'database.public_port' => 'nullable|integer',
+ ];
+ protected $validationAttributes = [
+ 'database.name' => 'Name',
+ 'database.description' => 'Description',
+ 'database.mariadb_root_password' => 'Root Password',
+ 'database.mariadb_user' => 'User',
+ 'database.mariadb_password' => 'Password',
+ 'database.mariadb_database' => 'Database',
+ 'database.mariadb_conf' => 'MariaDB Configuration',
+ 'database.image' => 'Image',
+ 'database.ports_mappings' => 'Port Mapping',
+ 'database.is_public' => 'Is Public',
+ 'database.public_port' => 'Public Port',
+ ];
+ public function submit()
+ {
+ try {
+ $this->validate();
+ $this->database->save();
+ $this->emit('success', 'Database updated successfully.');
+ } catch (Exception $e) {
+ return handleError($e, $this);
+ }
+ }
+ public function instantSave()
+ {
+ try {
+ if ($this->database->is_public && !$this->database->public_port) {
+ $this->emit('error', 'Public port is required.');
+ $this->database->is_public = false;
+ return;
+ }
+ if ($this->database->is_public) {
+ if (!str($this->database->status)->startsWith('running')) {
+ $this->emit('error', 'Database must be started to be publicly accessible.');
+ $this->database->is_public = false;
+ return;
+ }
+ StartDatabaseProxy::run($this->database);
+ $this->emit('success', 'Database is now publicly accessible.');
+ } else {
+ StopDatabaseProxy::run($this->database);
+ $this->emit('success', 'Database is no longer publicly accessible.');
+ }
+ $this->db_url = $this->database->getDbUrl();
+ $this->database->save();
+ } catch (\Throwable $e) {
+ $this->database->is_public = !$this->database->is_public;
+ return handleError($e, $this);
+ }
+ }
+ public function refresh(): void
+ {
+ $this->database->refresh();
+ }
+
+ public function mount()
+ {
+ $this->db_url = $this->database->getDbUrl();
+ }
+
+ public function render()
+ {
+ return view('livewire.project.database.mariadb.general');
+ }
+}
diff --git a/app/Http/Livewire/Project/Database/Mongodb/General.php b/app/Http/Livewire/Project/Database/Mongodb/General.php
index e0fc3c277..2e6bed4a4 100644
--- a/app/Http/Livewire/Project/Database/Mongodb/General.php
+++ b/app/Http/Livewire/Project/Database/Mongodb/General.php
@@ -39,7 +39,8 @@ class General extends Component
'database.is_public' => 'Is Public',
'database.public_port' => 'Public Port',
];
- public function submit() {
+ public function submit()
+ {
try {
$this->validate();
if ($this->database->mongo_conf === "") {
@@ -60,7 +61,11 @@ public function instantSave()
return;
}
if ($this->database->is_public) {
- $this->emit('success', 'Starting TCP proxy...');
+ if (!str($this->database->status)->startsWith('running')) {
+ $this->emit('error', 'Database must be started to be publicly accessible.');
+ $this->database->is_public = false;
+ return;
+ }
StartDatabaseProxy::run($this->database);
$this->emit('success', 'Database is now publicly accessible.');
} else {
@@ -69,7 +74,7 @@ public function instantSave()
}
$this->db_url = $this->database->getDbUrl();
$this->database->save();
- } catch(\Throwable $e) {
+ } catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
return handleError($e, $this);
}
diff --git a/app/Http/Livewire/Project/Database/Mysql/General.php b/app/Http/Livewire/Project/Database/Mysql/General.php
new file mode 100644
index 000000000..ca7eb6ebe
--- /dev/null
+++ b/app/Http/Livewire/Project/Database/Mysql/General.php
@@ -0,0 +1,95 @@
+ 'required',
+ 'database.description' => 'nullable',
+ 'database.mysql_root_password' => 'required',
+ 'database.mysql_user' => 'required',
+ 'database.mysql_password' => 'required',
+ 'database.mysql_database' => 'required',
+ 'database.mysql_conf' => 'nullable',
+ 'database.image' => 'required',
+ 'database.ports_mappings' => 'nullable',
+ 'database.is_public' => 'nullable|boolean',
+ 'database.public_port' => 'nullable|integer',
+ ];
+ protected $validationAttributes = [
+ 'database.name' => 'Name',
+ 'database.description' => 'Description',
+ 'database.mysql_root_password' => 'Root Password',
+ 'database.mysql_user' => 'User',
+ 'database.mysql_password' => 'Password',
+ 'database.mysql_database' => 'Database',
+ 'database.mysql_conf' => 'MySQL Configuration',
+ 'database.image' => 'Image',
+ 'database.ports_mappings' => 'Port Mapping',
+ 'database.is_public' => 'Is Public',
+ 'database.public_port' => 'Public Port',
+ ];
+ public function submit()
+ {
+ try {
+ $this->validate();
+ $this->database->save();
+ $this->emit('success', 'Database updated successfully.');
+ } catch (Exception $e) {
+ return handleError($e, $this);
+ }
+ }
+ public function instantSave()
+ {
+ try {
+ if ($this->database->is_public && !$this->database->public_port) {
+ $this->emit('error', 'Public port is required.');
+ $this->database->is_public = false;
+ return;
+ }
+ if ($this->database->is_public) {
+ if (!str($this->database->status)->startsWith('running')) {
+ $this->emit('error', 'Database must be started to be publicly accessible.');
+ $this->database->is_public = false;
+ return;
+ }
+ StartDatabaseProxy::run($this->database);
+ $this->emit('success', 'Database is now publicly accessible.');
+ } else {
+ StopDatabaseProxy::run($this->database);
+ $this->emit('success', 'Database is no longer publicly accessible.');
+ }
+ $this->db_url = $this->database->getDbUrl();
+ $this->database->save();
+ } catch (\Throwable $e) {
+ $this->database->is_public = !$this->database->is_public;
+ return handleError($e, $this);
+ }
+ }
+ public function refresh(): void
+ {
+ $this->database->refresh();
+ }
+
+ public function mount()
+ {
+ $this->db_url = $this->database->getDbUrl();
+ }
+
+ public function render()
+ {
+ return view('livewire.project.database.mysql.general');
+ }
+}
diff --git a/app/Http/Livewire/Project/Database/Postgresql/General.php b/app/Http/Livewire/Project/Database/Postgresql/General.php
index df1f0da85..4e3bda418 100644
--- a/app/Http/Livewire/Project/Database/Postgresql/General.php
+++ b/app/Http/Livewire/Project/Database/Postgresql/General.php
@@ -60,7 +60,11 @@ public function instantSave()
return;
}
if ($this->database->is_public) {
- $this->emit('success', 'Starting TCP proxy...');
+ if (!str($this->database->status)->startsWith('running')) {
+ $this->emit('error', 'Database must be started to be publicly accessible.');
+ $this->database->is_public = false;
+ return;
+ }
StartDatabaseProxy::run($this->database);
$this->emit('success', 'Database is now publicly accessible.');
} else {
@@ -69,11 +73,10 @@ public function instantSave()
}
$this->db_url = $this->database->getDbUrl();
$this->database->save();
- } catch(\Throwable $e) {
+ } catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
return handleError($e, $this);
}
-
}
public function save_init_script($script)
{
diff --git a/app/Http/Livewire/Project/Database/Redis/General.php b/app/Http/Livewire/Project/Database/Redis/General.php
index 6f33ae30a..dd2e8151d 100644
--- a/app/Http/Livewire/Project/Database/Redis/General.php
+++ b/app/Http/Livewire/Project/Database/Redis/General.php
@@ -35,7 +35,8 @@ class General extends Component
'database.is_public' => 'Is Public',
'database.public_port' => 'Public Port',
];
- public function submit() {
+ public function submit()
+ {
try {
$this->validate();
if ($this->database->redis_conf === "") {
@@ -56,7 +57,11 @@ public function instantSave()
return;
}
if ($this->database->is_public) {
- $this->emit('success', 'Starting TCP proxy...');
+ if (!str($this->database->status)->startsWith('running')) {
+ $this->emit('error', 'Database must be started to be publicly accessible.');
+ $this->database->is_public = false;
+ return;
+ }
StartDatabaseProxy::run($this->database);
$this->emit('success', 'Database is now publicly accessible.');
} else {
@@ -65,7 +70,7 @@ public function instantSave()
}
$this->db_url = $this->database->getDbUrl();
$this->database->save();
- } catch(\Throwable $e) {
+ } catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
return handleError($e, $this);
}
diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
index f453b4bf3..9b714a590 100644
--- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -81,6 +81,12 @@ public function saveVariables($isPreview)
case 'standalone-mongodb':
$environment->standalone_mongodb_id = $this->resource->id;
break;
+ case 'standalone-mysql':
+ $environment->standalone_mysql_id = $this->resource->id;
+ break;
+ case 'standalone-mariadb':
+ $environment->standalone_mariadb_id = $this->resource->id;
+ break;
case 'service':
$environment->service_id = $this->resource->id;
break;
diff --git a/app/Http/Livewire/Project/Shared/Logs.php b/app/Http/Livewire/Project/Shared/Logs.php
index 80cdf82c4..2b0561800 100644
--- a/app/Http/Livewire/Project/Shared/Logs.php
+++ b/app/Http/Livewire/Project/Shared/Logs.php
@@ -5,7 +5,9 @@
use App\Models\Application;
use App\Models\Server;
use App\Models\Service;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Livewire\Component;
@@ -13,7 +15,7 @@
class Logs extends Component
{
public ?string $type = null;
- public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb $resource;
+ public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
public Server $server;
public ?string $container = null;
public $parameters;
@@ -41,11 +43,16 @@ public function mount()
if (is_null($resource)) {
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
if (is_null($resource)) {
- abort(404);
+ $resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
+ if (is_null($resource)) {
+ $resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
+ if (is_null($resource)) {
+ abort(404);
+ }
+ }
}
}
}
-
$this->resource = $resource;
$this->status = $this->resource->status;
$this->server = $this->resource->destination->server;
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index d73474018..da660c449 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -6,7 +6,9 @@
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledDatabaseBackupExecution;
use App\Models\Server;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\Team;
use App\Notifications\Database\BackupFailed;
@@ -28,7 +30,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
public ?Team $team = null;
public Server $server;
public ScheduledDatabaseBackup $backup;
- public StandalonePostgresql|StandaloneMongodb $database;
+ public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database;
public ?string $container_name = null;
public ?ScheduledDatabaseBackupExecution $backup_log = null;
@@ -75,6 +77,10 @@ public function handle(): void
$databasesToBackup = [$this->database->postgres_db];
} else if ($databaseType === 'standalone-mongodb') {
$databasesToBackup = ['*'];
+ } else if ($databaseType === 'standalone-mysql') {
+ $databasesToBackup = [$this->database->mysql_database];
+ } else if ($databaseType === 'standalone-mariadb') {
+ $databasesToBackup = [$this->database->mariadb_database];
} else {
return;
}
@@ -88,6 +94,14 @@ public function handle(): void
$databasesToBackup = explode('|', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup);
ray($databasesToBackup);
+ } else if ($databaseType === 'standalone-mysql') {
+ // Format: db1,db2,db3
+ $databasesToBackup = explode(',', $databasesToBackup);
+ $databasesToBackup = array_map('trim', $databasesToBackup);
+ } else if ($databaseType === 'standalone-mariadb') {
+ // Format: db1,db2,db3
+ $databasesToBackup = explode(',', $databasesToBackup);
+ $databasesToBackup = array_map('trim', $databasesToBackup);
} else {
return;
}
@@ -124,7 +138,6 @@ public function handle(): void
} else {
$databaseName = $database;
}
- ray($databaseName);
}
$this->backup_file = "/mongo-dump-$databaseName-" . Carbon::now()->timestamp . ".tar.gz";
$this->backup_location = $this->backup_dir . $this->backup_file;
@@ -134,6 +147,24 @@ public function handle(): void
'scheduled_database_backup_id' => $this->backup->id,
]);
$this->backup_standalone_mongodb($database);
+ } else if ($databaseType === 'standalone-mysql') {
+ $this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp";
+ $this->backup_location = $this->backup_dir . $this->backup_file;
+ $this->backup_log = ScheduledDatabaseBackupExecution::create([
+ 'database_name' => $database,
+ 'filename' => $this->backup_location,
+ 'scheduled_database_backup_id' => $this->backup->id,
+ ]);
+ $this->backup_standalone_mysql($database);
+ } else if ($databaseType === 'standalone-mariadb') {
+ $this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp";
+ $this->backup_location = $this->backup_dir . $this->backup_file;
+ $this->backup_log = ScheduledDatabaseBackupExecution::create([
+ 'database_name' => $database,
+ 'filename' => $this->backup_location,
+ 'scheduled_database_backup_id' => $this->backup->id,
+ ]);
+ $this->backup_standalone_mariadb($database);
} else {
throw new \Exception('Unsupported database type');
}
@@ -218,7 +249,42 @@ private function backup_standalone_postgresql(string $database): void
throw $e;
}
}
-
+ private function backup_standalone_mysql(string $database): void
+ {
+ try {
+ $commands[] = "mkdir -p " . $this->backup_dir;
+ $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
+ ray($commands);
+ $this->backup_output = instant_remote_process($commands, $this->server);
+ $this->backup_output = trim($this->backup_output);
+ if ($this->backup_output === '') {
+ $this->backup_output = null;
+ }
+ ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
+ } catch (\Throwable $e) {
+ $this->add_to_backup_output($e->getMessage());
+ ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
+ throw $e;
+ }
+ }
+ private function backup_standalone_mariadb(string $database): void
+ {
+ try {
+ $commands[] = "mkdir -p " . $this->backup_dir;
+ $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
+ ray($commands);
+ $this->backup_output = instant_remote_process($commands, $this->server);
+ $this->backup_output = trim($this->backup_output);
+ if ($this->backup_output === '') {
+ $this->backup_output = null;
+ }
+ ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
+ } catch (\Throwable $e) {
+ $this->add_to_backup_output($e->getMessage());
+ ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
+ throw $e;
+ }
+ }
private function add_to_backup_output($output): void
{
if ($this->backup_output) {
diff --git a/app/Jobs/StopResourceJob.php b/app/Jobs/StopResourceJob.php
index 721f7f698..76c5588b8 100644
--- a/app/Jobs/StopResourceJob.php
+++ b/app/Jobs/StopResourceJob.php
@@ -7,7 +7,9 @@
use App\Actions\Service\StopService;
use App\Models\Application;
use App\Models\Service;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Bus\Queueable;
@@ -21,7 +23,7 @@ class StopResourceJob implements ShouldQueue, ShouldBeEncrypted
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
- public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb $resource)
+ public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource)
{
}
@@ -45,6 +47,12 @@ public function handle()
case 'standalone-mongodb':
StopDatabase::run($this->resource);
break;
+ case 'standalone-mysql':
+ StopDatabase::run($this->resource);
+ break;
+ case 'standalone-mariadb':
+ StopDatabase::run($this->resource);
+ break;
case 'service':
StopService::run($this->resource);
break;
diff --git a/app/Models/Environment.php b/app/Models/Environment.php
index 55dbeee94..430a02cdb 100644
--- a/app/Models/Environment.php
+++ b/app/Models/Environment.php
@@ -34,13 +34,23 @@ public function mongodbs()
{
return $this->hasMany(StandaloneMongodb::class);
}
+ public function mysqls()
+ {
+ return $this->hasMany(StandaloneMysql::class);
+ }
+ public function mariadbs()
+ {
+ return $this->hasMany(StandaloneMariadb::class);
+ }
public function databases()
{
$postgresqls = $this->postgresqls;
$redis = $this->redis;
$mongodbs = $this->mongodbs;
- return $postgresqls->concat($redis)->concat($mongodbs);
+ $mysqls = $this->mysqls;
+ $mariadbs = $this->mariadbs;
+ return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
}
public function project()
diff --git a/app/Models/Project.php b/app/Models/Project.php
index a910348b4..1668d4059 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -56,4 +56,16 @@ public function redis()
{
return $this->hasManyThrough(StandaloneRedis::class, Environment::class);
}
+ public function mongodbs()
+ {
+ return $this->hasManyThrough(StandaloneMongodb::class, Environment::class);
+ }
+ public function mysqls()
+ {
+ return $this->hasMany(StandaloneMysql::class, Environment::class);
+ }
+ public function mariadbs()
+ {
+ return $this->hasMany(StandaloneMariadb::class, Environment::class);
+ }
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 7ff517ef6..11be55764 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -122,10 +122,12 @@ public function isEmpty()
public function databases()
{
return $this->destinations()->map(function ($standaloneDocker) {
- $postgresqls = data_get($standaloneDocker,'postgresqls',collect([]));
- $redis = data_get($standaloneDocker,'redis',collect([]));
- $mongodbs = data_get($standaloneDocker,'mongodbs',collect([]));
- return $postgresqls->concat($redis)->concat($mongodbs);
+ $postgresqls = data_get($standaloneDocker, 'postgresqls', collect([]));
+ $redis = data_get($standaloneDocker, 'redis', collect([]));
+ $mongodbs = data_get($standaloneDocker, 'mongodbs', collect([]));
+ $mysqls = data_get($standaloneDocker, 'mysqls', collect([]));
+ $mariadbs = data_get($standaloneDocker, 'mariadbs', collect([]));
+ return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
})->flatten();
}
public function applications()
@@ -258,7 +260,8 @@ public function validateDockerEngineVersion()
$this->settings->save();
return true;
}
- public function validateCoolifyNetwork() {
+ public function validateCoolifyNetwork()
+ {
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
}
}
diff --git a/app/Models/StandaloneDocker.php b/app/Models/StandaloneDocker.php
index 9e70b7514..277a250c9 100644
--- a/app/Models/StandaloneDocker.php
+++ b/app/Models/StandaloneDocker.php
@@ -24,6 +24,14 @@ public function mongodbs()
{
return $this->morphMany(StandaloneMongodb::class, 'destination');
}
+ public function mysqls()
+ {
+ return $this->morphMany(StandaloneMysql::class, 'destination');
+ }
+ public function mariadbs()
+ {
+ return $this->morphMany(StandaloneMariadb::class, 'destination');
+ }
public function server()
{
@@ -35,6 +43,16 @@ public function services()
return $this->morphMany(Service::class, 'destination');
}
+ public function databases()
+ {
+ $postgresqls = $this->postgresqls;
+ $redis = $this->redis;
+ $mongodbs = $this->mongodbs;
+ $mysqls = $this->mysqls;
+ $mariadbs = $this->mariadbs;
+ return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
+ }
+
public function attachedTo()
{
return $this->applications?->count() > 0 || $this->databases?->count() > 0;
diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php
new file mode 100644
index 000000000..5e721857b
--- /dev/null
+++ b/app/Models/StandaloneMariadb.php
@@ -0,0 +1,106 @@
+ 'encrypted',
+ ];
+
+ protected static function booted()
+ {
+ static::created(function ($database) {
+ LocalPersistentVolume::create([
+ 'name' => 'mariadb-data-' . $database->uuid,
+ 'mount_path' => '/var/lib/mysql',
+ 'host_path' => null,
+ 'resource_id' => $database->id,
+ 'resource_type' => $database->getMorphClass(),
+ 'is_readonly' => true
+ ]);
+ });
+ static::deleting(function ($database) {
+ $storages = $database->persistentStorages()->get();
+ foreach ($storages as $storage) {
+ instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
+ }
+ $database->scheduledBackups()->delete();
+ $database->persistentStorages()->delete();
+ $database->environment_variables()->delete();
+ });
+ }
+ public function type(): string
+ {
+ return 'standalone-mariadb';
+ }
+
+ public function portsMappings(): Attribute
+ {
+ return Attribute::make(
+ set: fn ($value) => $value === "" ? null : $value,
+ );
+ }
+
+ public function portsMappingsArray(): Attribute
+ {
+ return Attribute::make(
+ get: fn () => is_null($this->ports_mappings)
+ ? []
+ : explode(',', $this->ports_mappings),
+
+ );
+ }
+
+ public function getDbUrl(bool $useInternal = false): string
+ {
+ if ($this->is_public && !$useInternal) {
+ return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
+ } else {
+ return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}";
+ }
+ }
+
+ public function environment()
+ {
+ return $this->belongsTo(Environment::class);
+ }
+
+ public function fileStorages()
+ {
+ return $this->morphMany(LocalFileVolume::class, 'resource');
+ }
+
+ public function destination()
+ {
+ return $this->morphTo();
+ }
+
+ public function environment_variables(): HasMany
+ {
+ return $this->hasMany(EnvironmentVariable::class);
+ }
+
+ public function runtime_environment_variables(): HasMany
+ {
+ return $this->hasMany(EnvironmentVariable::class);
+ }
+
+ public function persistentStorages()
+ {
+ return $this->morphMany(LocalPersistentVolume::class, 'resource');
+ }
+
+ public function scheduledBackups()
+ {
+ return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
+ }
+}
diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php
new file mode 100644
index 000000000..53a797d49
--- /dev/null
+++ b/app/Models/StandaloneMysql.php
@@ -0,0 +1,106 @@
+ 'encrypted',
+ 'mysql_root_password' => 'encrypted',
+ ];
+
+ protected static function booted()
+ {
+ static::created(function ($database) {
+ LocalPersistentVolume::create([
+ 'name' => 'mysql-data-' . $database->uuid,
+ 'mount_path' => '/var/lib/mysql',
+ 'host_path' => null,
+ 'resource_id' => $database->id,
+ 'resource_type' => $database->getMorphClass(),
+ 'is_readonly' => true
+ ]);
+ });
+ static::deleting(function ($database) {
+ $storages = $database->persistentStorages()->get();
+ foreach ($storages as $storage) {
+ instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
+ }
+ $database->scheduledBackups()->delete();
+ $database->persistentStorages()->delete();
+ $database->environment_variables()->delete();
+ });
+ }
+ public function type(): string
+ {
+ return 'standalone-mysql';
+ }
+
+ public function portsMappings(): Attribute
+ {
+ return Attribute::make(
+ set: fn ($value) => $value === "" ? null : $value,
+ );
+ }
+
+ public function portsMappingsArray(): Attribute
+ {
+ return Attribute::make(
+ get: fn () => is_null($this->ports_mappings)
+ ? []
+ : explode(',', $this->ports_mappings),
+
+ );
+ }
+
+ public function getDbUrl(bool $useInternal = false): string
+ {
+ if ($this->is_public && !$useInternal) {
+ return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";
+ } else {
+ return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}";
+ }
+ }
+
+ public function environment()
+ {
+ return $this->belongsTo(Environment::class);
+ }
+
+ public function fileStorages()
+ {
+ return $this->morphMany(LocalFileVolume::class, 'resource');
+ }
+
+ public function destination()
+ {
+ return $this->morphTo();
+ }
+
+ public function environment_variables(): HasMany
+ {
+ return $this->hasMany(EnvironmentVariable::class);
+ }
+
+ public function runtime_environment_variables(): HasMany
+ {
+ return $this->hasMany(EnvironmentVariable::class);
+ }
+
+ public function persistentStorages()
+ {
+ return $this->morphMany(LocalPersistentVolume::class, 'resource');
+ }
+
+ public function scheduledBackups()
+ {
+ return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
+ }
+}
diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php
index 3a0432180..bbfabbf67 100644
--- a/app/Models/StandalonePostgresql.php
+++ b/app/Models/StandalonePostgresql.php
@@ -46,8 +46,6 @@ public function portsMappings(): Attribute
);
}
- // Normal Deployments
-
public function portsMappingsArray(): Attribute
{
return Attribute::make(
diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php
index 586ba531d..10c1353d3 100644
--- a/bootstrap/helpers/constants.php
+++ b/bootstrap/helpers/constants.php
@@ -1,6 +1,6 @@
'* * * * *',
'hourly' => '0 * * * *',
diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php
index 0c5c8898e..007c414bd 100644
--- a/bootstrap/helpers/databases.php
+++ b/bootstrap/helpers/databases.php
@@ -2,7 +2,9 @@
use App\Models\Server;
use App\Models\StandaloneDocker;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Visus\Cuid2\Cuid2;
@@ -58,6 +60,36 @@ function create_standalone_mongodb($environment_id, $destination_uuid): Standalo
'destination_type' => $destination->getMorphClass(),
]);
}
+function create_standalone_mysql($environment_id, $destination_uuid): StandaloneMysql
+{
+ $destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
+ if (!$destination) {
+ throw new Exception('Destination not found');
+ }
+ return StandaloneMysql::create([
+ 'name' => generate_database_name('mysql'),
+ 'mysql_root_password' => \Illuminate\Support\Str::password(symbols: false),
+ 'mysql_password' => \Illuminate\Support\Str::password(symbols: false),
+ 'environment_id' => $environment_id,
+ 'destination_id' => $destination->id,
+ 'destination_type' => $destination->getMorphClass(),
+ ]);
+}
+function create_standalone_mariadb($environment_id, $destination_uuid): StandaloneMariadb
+{
+ $destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
+ if (!$destination) {
+ throw new Exception('Destination not found');
+ }
+ return StandaloneMariadb::create([
+ 'name' => generate_database_name('mariadb'),
+ 'mariadb_root_password' => \Illuminate\Support\Str::password(symbols: false),
+ 'mariadb_password' => \Illuminate\Support\Str::password(symbols: false),
+ 'environment_id' => $environment_id,
+ 'destination_id' => $destination->id,
+ 'destination_type' => $destination->getMorphClass(),
+ ]);
+}
/**
* Delete file locally on the filesystem.
diff --git a/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php b/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php
new file mode 100644
index 000000000..2b069424a
--- /dev/null
+++ b/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php
@@ -0,0 +1,57 @@
+id();
+ $table->string('uuid')->unique();
+ $table->string('name');
+ $table->string('description')->nullable();
+
+ $table->text('mysql_root_password');
+ $table->string('mysql_user')->default('mysql');
+ $table->text('mysql_password');
+ $table->string('mysql_database')->default('default');
+ $table->longText('mysql_conf')->nullable();
+
+ $table->string('status')->default('exited');
+
+ $table->string('image')->default('mysql:8');
+ $table->boolean('is_public')->default(false);
+ $table->integer('public_port')->nullable();
+ $table->text('ports_mappings')->nullable();
+
+ $table->string('limits_memory')->default("0");
+ $table->string('limits_memory_swap')->default("0");
+ $table->integer('limits_memory_swappiness')->default(60);
+ $table->string('limits_memory_reservation')->default("0");
+
+ $table->string('limits_cpus')->default("0");
+ $table->string('limits_cpuset')->nullable()->default("0");
+ $table->integer('limits_cpu_shares')->default(1024);
+
+ $table->timestamp('started_at')->nullable();
+ $table->morphs('destination');
+
+ $table->foreignId('environment_id')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('standalone_mysqls');
+ }
+};
diff --git a/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php b/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php
new file mode 100644
index 000000000..4d7b89f4c
--- /dev/null
+++ b/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php
@@ -0,0 +1,57 @@
+id();
+ $table->string('uuid')->unique();
+ $table->string('name');
+ $table->string('description')->nullable();
+
+ $table->text('mariadb_root_password');
+ $table->string('mariadb_user')->default('mariadb');
+ $table->text('mariadb_password');
+ $table->string('mariadb_database')->default('default');
+ $table->longText('mariadb_conf')->nullable();
+
+ $table->string('status')->default('exited');
+
+ $table->string('image')->default('mariadb:11');
+ $table->boolean('is_public')->default(false);
+ $table->integer('public_port')->nullable();
+ $table->text('ports_mappings')->nullable();
+
+ $table->string('limits_memory')->default("0");
+ $table->string('limits_memory_swap')->default("0");
+ $table->integer('limits_memory_swappiness')->default(60);
+ $table->string('limits_memory_reservation')->default("0");
+
+ $table->string('limits_cpus')->default("0");
+ $table->string('limits_cpuset')->nullable()->default("0");
+ $table->integer('limits_cpu_shares')->default(1024);
+
+ $table->timestamp('started_at')->nullable();
+ $table->morphs('destination');
+
+ $table->foreignId('environment_id')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('standalone_mariadbs');
+ }
+};
diff --git a/database/migrations/2023_10_24_120524_add_standalone_mysql_to_environment_variables_table.php b/database/migrations/2023_10_24_120524_add_standalone_mysql_to_environment_variables_table.php
new file mode 100644
index 000000000..a511e9b21
--- /dev/null
+++ b/database/migrations/2023_10_24_120524_add_standalone_mysql_to_environment_variables_table.php
@@ -0,0 +1,30 @@
+foreignId('standalone_mysql_id')->nullable();
+ $table->foreignId('standalone_mariadb_id')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('environment_variables', function (Blueprint $table) {
+ $table->dropColumn('standalone_mysql_id');
+ $table->dropColumn('standalone_mariadb_id');
+ });
+ }
+};
diff --git a/resources/views/components/databases/navbar.blade.php b/resources/views/components/databases/navbar.blade.php
index 64dc1d288..ecf4ca573 100644
--- a/resources/views/components/databases/navbar.blade.php
+++ b/resources/views/components/databases/navbar.blade.php
@@ -7,7 +7,11 @@
href="{{ route('project.database.logs', $parameters) }}">
Logs
- @if ($database->getMorphClass() === 'App\Models\StandalonePostgresql' || $database->getMorphClass() === 'App\Models\StandaloneMongodb')
+ @if (
+ $database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
+ $database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
+ $database->getMorphClass() === 'App\Models\StandaloneMysql' ||
+ $database->getMorphClass() === 'App\Models\StandaloneMariadb')
Backups
diff --git a/resources/views/livewire/project/database/backup-edit.blade.php b/resources/views/livewire/project/database/backup-edit.blade.php
index 7d634a37b..0542ee087 100644
--- a/resources/views/livewire/project/database/backup-edit.blade.php
+++ b/resources/views/livewire/project/database/backup-edit.blade.php
@@ -35,6 +35,14 @@
+ @elseif($backup->database_type === 'App\Models\StandaloneMysql')
+
+ @elseif($backup->database_type === 'App\Models\StandaloneMariadb')
+
@endif
diff --git a/resources/views/livewire/project/database/backup-execution.blade.php b/resources/views/livewire/project/database/backup-execution.blade.php
deleted file mode 100644
index cf306858d..000000000
--- a/resources/views/livewire/project/database/backup-execution.blade.php
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
- {{-- @if (data_get($execution, 'status') !== 'failed') --}}
- {{--
Download --}}
- {{-- @endif --}}
-
Delete
-
diff --git a/resources/views/livewire/project/database/backup-executions.blade.php b/resources/views/livewire/project/database/backup-executions.blade.php
index 30af9e93e..7b0a2ca51 100644
--- a/resources/views/livewire/project/database/backup-executions.blade.php
+++ b/resources/views/livewire/project/database/backup-executions.blade.php
@@ -1,9 +1,10 @@
@forelse($executions as $execution)
-
Location: {{ data_get($execution, 'filename', 'N/A') }}
-
+
+
+
+ {{-- @if (data_get($execution, 'status') !== 'failed') --}}
+ {{--
Download --}}
+ {{-- @endif --}}
+
Delete
+
@empty
No executions found.
@endforelse
+
diff --git a/resources/views/livewire/project/database/mariadb/general.blade.php b/resources/views/livewire/project/database/mariadb/general.blade.php
new file mode 100644
index 000000000..6b0205cbe
--- /dev/null
+++ b/resources/views/livewire/project/database/mariadb/general.blade.php
@@ -0,0 +1,58 @@
+
diff --git a/resources/views/livewire/project/database/mysql/general.blade.php b/resources/views/livewire/project/database/mysql/general.blade.php
new file mode 100644
index 000000000..09a23a694
--- /dev/null
+++ b/resources/views/livewire/project/database/mysql/general.blade.php
@@ -0,0 +1,58 @@
+
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php
index 6ebefda41..a18d00fc5 100644
--- a/resources/views/livewire/project/new/select.blade.php
+++ b/resources/views/livewire/project/new/select.blade.php
@@ -114,6 +114,26 @@
+
+
+
+ New MySQL
+
+
+ MySQL
+
+
+
+
+
+
+ New Mariadb
+
+
+ MySQL
+
+
+
{{--
diff --git a/resources/views/project/database/backups/executions.blade.php b/resources/views/project/database/backups/executions.blade.php
index 26d4aabd0..8c4625dcd 100644
--- a/resources/views/project/database/backups/executions.blade.php
+++ b/resources/views/project/database/backups/executions.blade.php
@@ -12,7 +12,7 @@
-
+
Executions
diff --git a/resources/views/project/database/configuration.blade.php b/resources/views/project/database/configuration.blade.php
index 8fbfa8f0c..3ad6e6df5 100644
--- a/resources/views/project/database/configuration.blade.php
+++ b/resources/views/project/database/configuration.blade.php
@@ -13,36 +13,48 @@
@if ($database->type() === 'standalone-postgresql')
- @endif
- @if ($database->type() === 'standalone-redis')
+ @elseif ($database->type() === 'standalone-redis')
+ @elseif ($database->type() === 'standalone-mongodb')
+
+ @elseif ($database->type() === 'standalone-mysql')
+
+ @elseif ($database->type() === 'standalone-mariadb')
+
@endif
- @if ($database->type() === 'standalone-mongodb')
-
- @endif
From 72421d692befed5d32f77e1427c06d4bc8983074 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 14:36:43 +0200
Subject: [PATCH 16/29] add slogans
---
resources/views/livewire/project/new/select.blade.php | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php
index a18d00fc5..145e7a152 100644
--- a/resources/views/livewire/project/new/select.blade.php
+++ b/resources/views/livewire/project/new/select.blade.php
@@ -90,7 +90,7 @@
New PostgreSQL
- The most loved relational database in the world.
+ PostgreSQL is an open-source, object-relational database management system known for its robustness, advanced features, and strong standards compliance.
@@ -100,7 +100,7 @@
New Redis
- The open source, in-memory data store for cache, streaming engine, and message broker.
+ Redis is an open-source, in-memory data structure store used as a database, cache, and message broker, known for its high performance, flexibility, and rich data structures.
@@ -110,7 +110,7 @@
New MongoDB
- MongoDB is a source-available cross-platform document-oriented database program.
+ MongoDB is a source-available, NoSQL database program that uses JSON-like documents with optional schemas, known for its flexibility, scalability, and wide range of application use cases.
@@ -120,7 +120,7 @@
New MySQL
- MySQL
+ MySQL is an open-source relational database management system known for its speed, reliability, and flexibility in managing and accessing data.
@@ -130,7 +130,7 @@
New Mariadb
- MySQL
+ MariaDB is an open-source relational database management system that serves as a drop-in replacement for MySQL, offering more robust, scalable, and reliable SQL server capabilities.
From 6e73f7f2e49992a8abb5fc93af9f2d3df7bbf353 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 15:40:29 +0200
Subject: [PATCH 17/29] fix: encrypt mongodb password
---
app/Models/StandaloneMongodb.php | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php
index 6d9158a64..06a6cb537 100644
--- a/app/Models/StandaloneMongodb.php
+++ b/app/Models/StandaloneMongodb.php
@@ -42,6 +42,20 @@ protected static function booted()
});
}
+ public function mongoInitdbRootPassword(): Attribute
+ {
+ return Attribute::make(
+ get: function ($value) {
+ try {
+ return decrypt($value);
+ } catch (\Throwable $th) {
+ $this->mongo_initdb_root_password = encrypt($value);
+ $this->save();
+ return $value;
+ }
+ }
+ );
+ }
public function portsMappings(): Attribute
{
return Attribute::make(
@@ -63,7 +77,8 @@ public function type(): string
{
return 'standalone-mongodb';
}
- public function getDbUrl(bool $useInternal = false) {
+ public function getDbUrl(bool $useInternal = false)
+ {
if ($this->is_public && !$useInternal) {
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
} else {
From 0232cf5b4c4b80e1d90fc0cbb95f04a941e810b2 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 15:41:21 +0200
Subject: [PATCH 18/29] feat: lock environment variables
---
.../Shared/EnvironmentVariable/All.php | 24 ++++++--
.../Shared/EnvironmentVariable/Show.php | 33 +++++++---
app/Models/EnvironmentVariable.php | 16 +++--
...wn_once_to_environment_variables_table.php | 28 +++++++++
.../shared/environment-variable/all.blade.php | 3 +-
.../environment-variable/show.blade.php | 60 ++++++++++++-------
6 files changed, 121 insertions(+), 43 deletions(-)
create mode 100644 database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php
diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
index 9b714a590..b1fa237e0 100644
--- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -31,11 +31,17 @@ public function mount()
public function getDevView()
{
$this->variables = $this->resource->environment_variables->map(function ($item) {
+ if ($item->is_shown_once) {
+ return "$item->key=(locked secret)";
+ }
return "$item->key=$item->value";
})->sort()->join('
');
if ($this->showPreview) {
$this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
+ if ($item->is_shown_once) {
+ return "$item->key=(locked secret)";
+ }
return "$item->key=$item->value";
})->sort()->join('
');
@@ -49,19 +55,27 @@ public function saveVariables($isPreview)
{
if ($isPreview) {
$variables = parseEnvFormatToArray($this->variablesPreview);
- $existingVariables = $this->resource->environment_variables_preview();
- $this->resource->environment_variables_preview()->delete();
} else {
$variables = parseEnvFormatToArray($this->variables);
- $existingVariables = $this->resource->environment_variables();
- $this->resource->environment_variables()->delete();
}
foreach ($variables as $key => $variable) {
- $found = $existingVariables->where('key', $key)->first();
+ $found = $this->resource->environment_variables()->where('key', $key)->first();
+ $foundPreview = $this->resource->environment_variables_preview()->where('key', $key)->first();
if ($found) {
+ if ($found->is_shown_once) {
+ continue;
+ }
$found->value = $variable;
$found->save();
continue;
+ }
+ if ($foundPreview) {
+ if ($foundPreview->is_shown_once) {
+ continue;
+ }
+ $foundPreview->value = $variable;
+ $foundPreview->save();
+ continue;
} else {
$environment = new EnvironmentVariable();
$environment->key = $key;
diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php
index 0ad197f1a..eed0f7052 100644
--- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -5,7 +5,6 @@
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
-use Illuminate\Support\Str;
class Show extends Component
{
@@ -13,29 +12,45 @@ class Show extends Component
public ModelsEnvironmentVariable $env;
public ?string $modalId = null;
public bool $isDisabled = false;
+ public bool $isLocked = false;
public string $type;
protected $rules = [
'env.key' => 'required|string',
'env.value' => 'nullable',
'env.is_build_time' => 'required|boolean',
+ 'env.is_shown_once' => 'required|boolean',
];
protected $validationAttributes = [
- 'key' => 'key',
- 'value' => 'value',
- 'is_build_time' => 'build',
+ 'key' => 'Key',
+ 'value' => 'Value',
+ 'is_build_time' => 'Build Time',
+ 'is_shown_once' => 'Shown Once',
];
public function mount()
{
- $this->isDisabled = false;
- if (Str::of($this->env->key)->startsWith('SERVICE_FQDN') || Str::of($this->env->key)->startsWith('SERVICE_URL')) {
- $this->isDisabled = true;
- }
$this->modalId = new Cuid2(7);
$this->parameters = get_route_parameters();
+ $this->checkEnvs();
+ }
+ public function checkEnvs()
+ {
+ $this->isDisabled = false;
+ if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) {
+ $this->isDisabled = true;
+ }
+ if ($this->env->is_shown_once) {
+ $this->isLocked = true;
+ }
+ }
+ public function lock()
+ {
+ $this->env->is_shown_once = true;
+ $this->env->save();
+ $this->checkEnvs();
+ $this->emit('refreshEnvs');
}
-
public function instantSave()
{
$this->submit();
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 37619d190..5450f0127 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -11,7 +11,7 @@ class EnvironmentVariable extends Model
{
protected $guarded = [];
protected $casts = [
- "key" => 'string',
+ 'key' => 'string',
'value' => 'encrypted',
'is_build_time' => 'boolean',
];
@@ -21,6 +21,10 @@ protected static function booted()
static::created(function ($environment_variable) {
if ($environment_variable->application_id && !$environment_variable->is_preview) {
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first();
+ $application = Application::find($environment_variable->application_id);
+ if ($application->build_pack === 'dockerfile') {
+ return;
+ }
if (!$found) {
ModelsEnvironmentVariable::create([
'key' => $environment_variable->key,
@@ -33,7 +37,8 @@ protected static function booted()
}
});
}
- public function service() {
+ public function service()
+ {
return $this->belongsTo(Service::class);
}
protected function value(): Attribute
@@ -55,9 +60,9 @@ private function get_environment_variables(?string $environment_variable = null)
$variable = Str::after($environment_variable, 'global.');
$variable = Str::before($variable, '}}');
$variable = Str::of($variable)->trim()->value;
- // $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
- ray('global env variable');
- return $environment_variable;
+ // $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
+ ray('global env variable');
+ return $environment_variable;
}
return $environment_variable;
}
@@ -77,5 +82,4 @@ protected function key(): Attribute
set: fn (string $value) => Str::of($value)->trim(),
);
}
-
}
diff --git a/database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php b/database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php
new file mode 100644
index 000000000..e0df21186
--- /dev/null
+++ b/database/migrations/2023_10_24_124934_add_is_shown_once_to_environment_variables_table.php
@@ -0,0 +1,28 @@
+boolean('is_shown_once')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('environment_variables', function (Blueprint $table) {
+ $table->dropColumn('is_shown_once');
+ });
+ }
+};
diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php
index 6297f3822..ec1480e05 100644
--- a/resources/views/livewire/project/shared/environment-variable/all.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php
@@ -28,8 +28,7 @@
@endif
@else
@if ($showPreview)
diff --git a/resources/views/livewire/project/shared/environment-variable/show.blade.php b/resources/views/livewire/project/shared/environment-variable/show.blade.php
index 6663dc12a..f41cf8bef 100644
--- a/resources/views/livewire/project/shared/environment-variable/show.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php
@@ -6,36 +6,54 @@ class="font-bold text-warning">({{ $env->key }})?
From dc86170ef5e3d3b2ff8d7b67a13048bc5401d8eb Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 24 Oct 2023 15:41:44 +0200
Subject: [PATCH 19/29] version++
---
config/sentry.php | 2 +-
config/version.php | 2 +-
versions.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/config/sentry.php b/config/sentry.php
index 25d04008a..32e27e081 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.100',
+ 'release' => '4.0.0-beta.101',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index 042813982..d54064579 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Tue, 24 Oct 2023 15:47:29 +0200
Subject: [PATCH 20/29] fix: mongodb healtcheck command
---
app/Actions/Database/StartMongodb.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php
index 645ed6ee9..8bfb9a982 100644
--- a/app/Actions/Database/StartMongodb.php
+++ b/app/Actions/Database/StartMongodb.php
@@ -52,7 +52,7 @@ public function handle(StandaloneMongodb $database)
'healthcheck' => [
'test' => [
'CMD-SHELL',
- 'mongo --eval "printjson(db.serverStatus())" | grep uptime | grep -v grep'
+ 'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
],
'interval' => '5s',
'timeout' => '5s',
From d5cc2a2eedc644746049a04626ed886dbb4fcb5f Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 09:28:26 +0200
Subject: [PATCH 21/29] feat: download local backups
---
.../Project/Database/BackupExecutions.php | 24 +++++++
composer.json | 1 +
composer.lock | 62 ++++++++++++++++++-
config/sentry.php | 2 +-
config/version.php | 2 +-
.../database/backup-executions.blade.php | 8 +--
versions.json | 2 +-
7 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/app/Http/Livewire/Project/Database/BackupExecutions.php b/app/Http/Livewire/Project/Database/BackupExecutions.php
index 2f808d992..f8ec4efbe 100644
--- a/app/Http/Livewire/Project/Database/BackupExecutions.php
+++ b/app/Http/Livewire/Project/Database/BackupExecutions.php
@@ -2,6 +2,7 @@
namespace App\Http\Livewire\Project\Database;
+use Illuminate\Support\Facades\Storage;
use Livewire\Component;
class BackupExecutions extends Component
@@ -23,6 +24,29 @@ public function deleteBackup($exeuctionId)
$this->emit('success', 'Backup deleted successfully.');
$this->emit('refreshBackupExecutions');
}
+ public function download($exeuctionId)
+ {
+ try {
+ $execution = $this->backup->executions()->where('id', $exeuctionId)->first();
+ if (is_null($execution)) {
+ $this->emit('error', 'Backup execution not found.');
+ return;
+ }
+ $filename = data_get($execution, 'filename');
+ $server = $execution->scheduledDatabaseBackup->database->destination->server;
+ $privateKeyLocation = savePrivateKeyToFs($server);
+ $disk = Storage::build([
+ 'driver' => 'sftp',
+ 'host' => $server->ip,
+ 'port' => $server->port,
+ 'username' => $server->user,
+ 'privateKey' => $privateKeyLocation,
+ ]);
+ return $disk->download($filename);
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
public function refreshBackupExecutions(): void
{
$this->executions = $this->backup->executions;
diff --git a/composer.json b/composer.json
index 217560b57..9937ee5b9 100644
--- a/composer.json
+++ b/composer.json
@@ -21,6 +21,7 @@
"laravel/ui": "^4.2",
"lcobucci/jwt": "^5.0.0",
"league/flysystem-aws-s3-v3": "^3.0",
+ "league/flysystem-sftp-v3": "^3.0",
"livewire/livewire": "^v2.12.3",
"lorisleiva/laravel-actions": "^2.7",
"masmerise/livewire-toaster": "^1.2",
diff --git a/composer.lock b/composer.lock
index 9f9e8d658..62d4f27ff 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "de2c45be3f03d43430549d963778dc4a",
+ "content-hash": "21ed976753483557403be75318585442",
"packages": [
{
"name": "aws/aws-crt-php",
@@ -2938,6 +2938,66 @@
],
"time": "2023-08-30T10:23:59+00:00"
},
+ {
+ "name": "league/flysystem-sftp-v3",
+ "version": "3.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-sftp-v3.git",
+ "reference": "1ba682def8e87fd7fa00883629553c0200d2e974"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/1ba682def8e87fd7fa00883629553c0200d2e974",
+ "reference": "1ba682def8e87fd7fa00883629553c0200d2e974",
+ "shasum": ""
+ },
+ "require": {
+ "league/flysystem": "^3.0.14",
+ "league/mime-type-detection": "^1.0.0",
+ "php": "^8.0.2",
+ "phpseclib/phpseclib": "^3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\PhpseclibV3\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "SFTP filesystem adapter for Flysystem.",
+ "keywords": [
+ "Flysystem",
+ "file",
+ "files",
+ "filesystem",
+ "sftp"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-sftp-v3/issues",
+ "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.16.0"
+ },
+ "funding": [
+ {
+ "url": "https://ecologi.com/frankdejonge",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-30T10:25:05+00:00"
+ },
{
"name": "league/mime-type-detection",
"version": "1.13.0",
diff --git a/config/sentry.php b/config/sentry.php
index 32e27e081..16f184229 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.101',
+ 'release' => '4.0.0-beta.102',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index d54064579..004c77c3b 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Location: {{ data_get($execution, 'filename', 'N/A') }}
-
- {{-- @if (data_get($execution, 'status') !== 'failed') --}}
- {{--
Download --}}
- {{-- @endif --}}
+ @if (data_get($execution, 'status') === 'success')
+
Download
+ @endif
Delete
diff --git a/versions.json b/versions.json
index 4c8e06b9d..cd5da1110 100644
--- a/versions.json
+++ b/versions.json
@@ -4,7 +4,7 @@
"version": "3.12.36"
},
"v4": {
- "version": "4.0.0-beta.101"
+ "version": "4.0.0-beta.102"
}
}
}
From 70ecb92e82c69aecbe912ef48d1aa7e3dea83884 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 09:41:41 +0200
Subject: [PATCH 22/29] cleanup ssh dir on start
---
app/Console/Commands/Init.php | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php
index 6abac7029..31d7639be 100644
--- a/app/Console/Commands/Init.php
+++ b/app/Console/Commands/Init.php
@@ -10,6 +10,7 @@
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Storage;
class Init extends Command
{
@@ -21,8 +22,23 @@ public function handle()
ray()->clearAll();
$this->cleanup_in_progress_application_deployments();
$this->cleanup_stucked_resources();
+ $this->cleanup_ssh();
}
+ private function cleanup_ssh() {
+ try {
+ $files = Storage::allFiles('ssh/keys');
+ foreach ($files as $file) {
+ Storage::delete($file);
+ }
+ $files = Storage::allFiles('ssh/mux');
+ foreach ($files as $file) {
+ Storage::delete($file);
+ }
+ } catch (\Throwable $e) {
+ echo "Error: {$e->getMessage()}\n";
+ }
+ }
private function cleanup_in_progress_application_deployments()
{
// Cleanup any failed deployments
From aa02b8d4332a671bd2c430f11b85fa53254b84e7 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 09:56:58 +0200
Subject: [PATCH 23/29] fix: rate limit for api + add mariadb + mysql
---
app/Providers/RouteServiceProvider.php | 2 +-
routes/api.php | 34 +++++++++++++++++++++++---
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php
index f60994c61..79b214502 100644
--- a/app/Providers/RouteServiceProvider.php
+++ b/app/Providers/RouteServiceProvider.php
@@ -48,7 +48,7 @@ protected function configureRateLimiting(): void
if ($request->path() === 'api/health') {
return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip());
}
- return Limit::perMinute(30)->by($request->user()?->id ?: $request->ip());
+ return Limit::perMinute(200)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('5', function (Request $request) {
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
diff --git a/routes/api.php b/routes/api.php
index 77c000576..53ae1bd09 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1,6 +1,8 @@
query->get('uuid');
$force = $request->query->get('force') ?? false;
-
if (is_null($teamId)) {
return response()->json(['error' => 'Invalid token.'], 400);
}
@@ -50,29 +51,56 @@
);
return response()->json(['message' => 'Deployment queued.'], 200);
} else if ($type === 'App\Models\StandalonePostgresql') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
StartPostgresql::run($resource);
$resource->update([
'started_at' => now(),
]);
return response()->json(['message' => 'Database started.'], 200);
} else if ($type === 'App\Models\StandaloneRedis') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
StartRedis::run($resource);
$resource->update([
'started_at' => now(),
]);
return response()->json(['message' => 'Database started.'], 200);
} else if ($type === 'App\Models\StandaloneMongodb') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
StartMongodb::run($resource);
$resource->update([
'started_at' => now(),
]);
return response()->json(['message' => 'Database started.'], 200);
- }else if ($type === 'App\Models\Service') {
+ } else if ($type === 'App\Models\StandaloneMysql') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
+ StartMysql::run($resource);
+ $resource->update([
+ 'started_at' => now(),
+ ]);
+ return response()->json(['message' => 'Database started.'], 200);
+ } else if ($type === 'App\Models\StandaloneMariadb') {
+ if (str($resource->status)->startsWith('running')) {
+ return response()->json(['message' => 'Database already running.'], 200);
+ }
+ StartMariadb::run($resource);
+ $resource->update([
+ 'started_at' => now(),
+ ]);
+ return response()->json(['message' => 'Database started.'], 200);
+ } else if ($type === 'App\Models\Service') {
StartService::run($resource);
return response()->json(['message' => 'Service started.'], 200);
}
}
- return response()->json(['error' => 'No resource found.'], 404);
+ return response()->json(['error' => "No resource found with {$uuid}."], 404);
});
});
From 379f4b9dff1310e165844fa356f989b293286d24 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 10:43:07 +0200
Subject: [PATCH 24/29] feat: show webhook on ui feat: n8n service
---
app/Http/Livewire/Project/Shared/Webhooks.php | 19 ++++++++++
bootstrap/helpers/shared.php | 15 ++++++++
.../livewire/project/service/index.blade.php | 25 ++++++++----
.../project/shared/webhooks.blade.php | 10 +++++
.../application/configuration.blade.php | 6 +++
.../project/database/configuration.blade.php | 6 +++
routes/api.php | 2 +-
templates/compose/appsmith.yaml | 2 +
templates/compose/n8n-with-postgresql.yaml | 38 +++++++++++++++++++
templates/compose/n8n.yaml | 15 ++++++++
templates/service-templates.json | 30 ++++++++++++++-
11 files changed, 159 insertions(+), 9 deletions(-)
create mode 100644 app/Http/Livewire/Project/Shared/Webhooks.php
create mode 100644 resources/views/livewire/project/shared/webhooks.blade.php
create mode 100644 templates/compose/n8n-with-postgresql.yaml
create mode 100644 templates/compose/n8n.yaml
diff --git a/app/Http/Livewire/Project/Shared/Webhooks.php b/app/Http/Livewire/Project/Shared/Webhooks.php
new file mode 100644
index 000000000..a943347b1
--- /dev/null
+++ b/app/Http/Livewire/Project/Shared/Webhooks.php
@@ -0,0 +1,19 @@
+deploywebhook = generateDeployWebhook($this->resource);
+ }
+ public function render()
+ {
+ return view('livewire.project.shared.webhooks');
+ }
+}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 3348ce1ae..8e8b2ec3e 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -4,7 +4,9 @@
use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\Service;
+use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use App\Models\Team;
@@ -484,5 +486,18 @@ function queryResourcesByUuid(string $uuid)
if ($redis) return $redis;
$mongodb = StandaloneMongodb::whereUuid($uuid)->first();
if ($mongodb) return $mongodb;
+ $mysql = StandaloneMysql::whereUuid($uuid)->first();
+ if ($mysql) return $mysql;
+ $mariadb = StandaloneMariadb::whereUuid($uuid)->first();
+ if ($mariadb) return $mariadb;
return $resource;
}
+
+function generateDeployWebhook($resource) {
+ $baseUrl = base_url();
+ $api = Url::fromString($baseUrl) . '/api/v1';
+ $endpoint = '/deploy';
+ $uuid = data_get($resource, 'uuid');
+ $url = $api . $endpoint . "?uuid=$uuid&force=false";
+ return $url;
+}
diff --git a/resources/views/livewire/project/service/index.blade.php b/resources/views/livewire/project/service/index.blade.php
index a966c56d4..933c89ecd 100644
--- a/resources/views/livewire/project/service/index.blade.php
+++ b/resources/views/livewire/project/service/index.blade.php
@@ -4,16 +4,25 @@
@@ -100,7 +109,9 @@ class="hover:text-warning">Logs
@foreach ($databases as $database)
@endforeach
-
+
+
+
+
+
+
diff --git a/routes/api.php b/routes/api.php
index 53ae1bd09..ef233e37e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -97,7 +97,7 @@
return response()->json(['message' => 'Database started.'], 200);
} else if ($type === 'App\Models\Service') {
StartService::run($resource);
- return response()->json(['message' => 'Service started.'], 200);
+ return response()->json(['message' => 'Service started. It could take a while, be patient.'], 200);
}
}
return response()->json(['error' => "No resource found with {$uuid}."], 404);
diff --git a/templates/compose/appsmith.yaml b/templates/compose/appsmith.yaml
index a30f4c4e5..81ac5fe3d 100644
--- a/templates/compose/appsmith.yaml
+++ b/templates/compose/appsmith.yaml
@@ -14,3 +14,5 @@ services:
- APPSMITH_SMART_LOOK_ID=
volumes:
- stacks-data:/appsmith-stacks
+ healthcheck:
+ test: ["NONE"]
diff --git a/templates/compose/n8n-with-postgresql.yaml b/templates/compose/n8n-with-postgresql.yaml
new file mode 100644
index 000000000..25955583d
--- /dev/null
+++ b/templates/compose/n8n-with-postgresql.yaml
@@ -0,0 +1,38 @@
+# documentation: https://docs.n8n.io/hosting/
+# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.
+# tags: n8n,workflow,automation,open,source,low,code
+
+services:
+ n8n:
+ image: docker.n8n.io/n8nio/n8n
+ environment:
+ - SERVICE_FQDN_N8N
+ - N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
+ - N8N_HOST=${SERVICE_FQDN_N8N}
+ - GENERIC_TIMEZONE="Europe/Berlin"
+ - TZ="Europe/Berlin"
+ - DB_TYPE=postgresdb
+ - DB_POSTGRESDB_DATABASE=${POSTGRES_DB:-umami}
+ - DB_POSTGRESDB_HOST=postgresql
+ - DB_POSTGRESDB_PORT=5432
+ - DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES
+ - DB_POSTGRESDB_SCHEMA=public
+ - DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
+ volumes:
+ - n8n-data:/home/node/.n8n
+ depends_on:
+ - postgresql
+ postgresql:
+ image: postgres:15-alpine
+ volumes:
+ - postgresql-data:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_USER=$SERVICE_USER_POSTGRES
+ - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
+ - POSTGRES_DB=${POSTGRES_DB:-umami}
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
+
diff --git a/templates/compose/n8n.yaml b/templates/compose/n8n.yaml
new file mode 100644
index 000000000..c8613cf03
--- /dev/null
+++ b/templates/compose/n8n.yaml
@@ -0,0 +1,15 @@
+# documentation: https://docs.n8n.io/hosting/
+# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.
+# tags: n8n,workflow,automation,open,source,low,code
+
+services:
+ n8n:
+ image: docker.n8n.io/n8nio/n8n
+ environment:
+ - SERVICE_FQDN_N8N
+ - N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
+ - N8N_HOST=${SERVICE_FQDN_N8N}
+ - GENERIC_TIMEZONE="Europe/Berlin"
+ - TZ="Europe/Berlin"
+ volumes:
+ - n8n-data:/home/node/.n8n
diff --git a/templates/service-templates.json b/templates/service-templates.json
index d5af2b37c..4c06f76c0 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -2,7 +2,7 @@
"appsmith": {
"documentation": "https:\/\/docs.appsmith.com",
"slogan": "Appsmith is an open-source, self-hosted application development platform that enables you to build powerful web applications with ease.",
- "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycK",
+ "compose": "c2VydmljZXM6CiAgYXBwc21pdGg6CiAgICBpbWFnZTogJ2luZGV4LmRvY2tlci5pby9hcHBzbWl0aC9hcHBzbWl0aC1jZTpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE4KICAgICAgLSBBUFBTTUlUSF9NQUlMX0VOQUJMRUQ9ZmFsc2UKICAgICAgLSBBUFBTTUlUSF9ESVNBQkxFX1RFTEVNRVRSWT10cnVlCiAgICAgIC0gQVBQU01JVEhfRElTQUJMRV9JTlRFUkNPTT10cnVlCiAgICAgIC0gQVBQU01JVEhfU0VOVFJZX0RTTj0KICAgICAgLSBBUFBTTUlUSF9TTUFSVF9MT09LX0lEPQogICAgdm9sdW1lczoKICAgICAgLSAnc3RhY2tzLWRhdGE6L2FwcHNtaXRoLXN0YWNrcycKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gTk9ORQo=",
"tags": [
"lowcode",
"nocode",
@@ -110,6 +110,34 @@
"api"
]
},
+ "n8n-with-postgresql": {
+ "documentation": "https:\/\/docs.n8n.io\/hosting\/",
+ "slogan": "n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.",
+ "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnR0VORVJJQ19USU1FWk9ORT0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gJ1RaPSJFdXJvcGUvQmVybGluIicKICAgICAgLSBEQl9UWVBFPXBvc3RncmVzZGIKICAgICAgLSAnREJfUE9TVEdSRVNEQl9EQVRBQkFTRT0ke1BPU1RHUkVTX0RCOi11bWFtaX0nCiAgICAgIC0gREJfUE9TVEdSRVNEQl9IT1NUPXBvc3RncmVzcWwKICAgICAgLSBEQl9QT1NUR1JFU0RCX1BPUlQ9NTQzMgogICAgICAtIERCX1BPU1RHUkVTREJfVVNFUj0kU0VSVklDRV9VU0VSX1BPU1RHUkVTCiAgICAgIC0gREJfUE9TVEdSRVNEQl9TQ0hFTUE9cHVibGljCiAgICAgIC0gREJfUE9TVEdSRVNEQl9QQVNTV09SRD0kU0VSVklDRV9QQVNTV09SRF9QT1NUR1JFUwogICAgdm9sdW1lczoKICAgICAgLSAnbjhuLWRhdGE6L2hvbWUvbm9kZS8ubjhuJwogICAgZGVwZW5kc19vbjoKICAgICAgLSBwb3N0Z3Jlc3FsCiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXNxbC1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JFNFUlZJQ0VfVVNFUl9QT1NUR1JFUwogICAgICAtIFBPU1RHUkVTX1BBU1NXT1JEPSRTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LXVtYW1pfScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAkJHtQT1NUR1JFU19VU0VSfSAtZCAkJHtQT1NUR1JFU19EQn0nCiAgICAgIGludGVydmFsOiA1cwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiAxMAo=",
+ "tags": [
+ "n8n",
+ "workflow",
+ "automation",
+ "open",
+ "source",
+ "low",
+ "code"
+ ]
+ },
+ "n8n": {
+ "documentation": "https:\/\/docs.n8n.io\/hosting\/",
+ "slogan": "n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.",
+ "compose": "c2VydmljZXM6CiAgbjhuOgogICAgaW1hZ2U6IGRvY2tlci5uOG4uaW8vbjhuaW8vbjhuCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTjhOCiAgICAgIC0gJ044Tl9FRElUT1JfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnTjhOX0hPU1Q9JHtTRVJWSUNFX0ZRRE5fTjhOfScKICAgICAgLSAnR0VORVJJQ19USU1FWk9ORT0iRXVyb3BlL0JlcmxpbiInCiAgICAgIC0gJ1RaPSJFdXJvcGUvQmVybGluIicKICAgIHZvbHVtZXM6CiAgICAgIC0gJ244bi1kYXRhOi9ob21lL25vZGUvLm44bicK",
+ "tags": [
+ "n8n",
+ "workflow",
+ "automation",
+ "open",
+ "source",
+ "low",
+ "code"
+ ]
+ },
"pairdrop": {
"documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop\/blob\/master\/docs\/faq.md",
"slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.",
From 50fc05ab5243766f6f03ea939dc9f80ebeab1656 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 11:43:18 +0200
Subject: [PATCH 25/29] update init script
---
app/Console/Commands/Init.php | 41 ++++++++++++++++++++++++++++-------
config/sentry.php | 2 +-
config/version.php | 2 +-
versions.json | 2 +-
4 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php
index 31d7639be..0efd9b025 100644
--- a/app/Console/Commands/Init.php
+++ b/app/Console/Commands/Init.php
@@ -7,6 +7,7 @@
use App\Models\ApplicationDeploymentQueue;
use App\Models\Service;
use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Console\Command;
@@ -22,10 +23,11 @@ public function handle()
ray()->clearAll();
$this->cleanup_in_progress_application_deployments();
$this->cleanup_stucked_resources();
- $this->cleanup_ssh();
+ // $this->cleanup_ssh();
}
- private function cleanup_ssh() {
+ private function cleanup_ssh()
+ {
try {
$files = Storage::allFiles('ssh/keys');
foreach ($files as $file) {
@@ -53,11 +55,12 @@ private function cleanup_in_progress_application_deployments()
echo "Error: {$e->getMessage()}\n";
}
}
- private function cleanup_stucked_resources() {
+ private function cleanup_stucked_resources()
+ {
// Cleanup any resources that are not attached to any environment or destination or server
try {
$applications = Application::all();
- foreach($applications as $application) {
+ foreach ($applications as $application) {
if (!$application->environment) {
ray('Application without environment', $application->name);
$application->delete();
@@ -68,7 +71,7 @@ private function cleanup_stucked_resources() {
}
}
$postgresqls = StandalonePostgresql::all();
- foreach($postgresqls as $postgresql) {
+ foreach ($postgresqls as $postgresql) {
if (!$postgresql->environment) {
ray('Postgresql without environment', $postgresql->name);
$postgresql->delete();
@@ -79,7 +82,7 @@ private function cleanup_stucked_resources() {
}
}
$redis = StandaloneRedis::all();
- foreach($redis as $redis) {
+ foreach ($redis as $redis) {
if (!$redis->environment) {
ray('Redis without environment', $redis->name);
$redis->delete();
@@ -90,7 +93,7 @@ private function cleanup_stucked_resources() {
}
}
$mongodbs = StandaloneMongodb::all();
- foreach($mongodbs as $mongodb) {
+ foreach ($mongodbs as $mongodb) {
if (!$mongodb->environment) {
ray('Mongodb without environment', $mongodb->name);
$mongodb->delete();
@@ -100,8 +103,30 @@ private function cleanup_stucked_resources() {
$mongodb->delete();
}
}
+ $mysqls = StandaloneMysql::all();
+ foreach ($mysqls as $mysql) {
+ if (!$mysql->environment) {
+ ray('Mysql without environment', $mysql->name);
+ $mysql->delete();
+ }
+ if (!$mysql->destination()) {
+ ray('Mysql without destination', $mysql->name);
+ $mysql->delete();
+ }
+ }
+ $mariadbs = StandaloneMysql::all();
+ foreach ($mariadbs as $mariadb) {
+ if (!$mariadb->environment) {
+ ray('Mariadb without environment', $mariadb->name);
+ $mariadb->delete();
+ }
+ if (!$mariadb->destination()) {
+ ray('Mariadb without destination', $mariadb->name);
+ $mariadb->delete();
+ }
+ }
$services = Service::all();
- foreach($services as $service) {
+ foreach ($services as $service) {
if (!$service->environment) {
ray('Service without environment', $service->name);
$service->delete();
diff --git a/config/sentry.php b/config/sentry.php
index 16f184229..b4f412e41 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.102',
+ 'release' => '4.0.0-beta.103',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index 004c77c3b..14a39b0fa 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
Date: Wed, 25 Oct 2023 11:50:22 +0200
Subject: [PATCH 26/29] fix: server settings guarded
---
app/Models/ServerSetting.php | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php
index 198600735..9235848ee 100644
--- a/app/Models/ServerSetting.php
+++ b/app/Models/ServerSetting.php
@@ -6,10 +6,7 @@
class ServerSetting extends Model
{
- protected $fillable = [
- 'server_id',
- 'is_usable',
- ];
+ protected $guarded = [];
public function server()
{
From ead1edc2b98639b489c0eb79c8e7274273dbf870 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 15:44:34 +0200
Subject: [PATCH 27/29] Services
---
templates/compose/dashboard.yaml | 1 +
templates/compose/emby.yaml | 1 +
templates/compose/embystat.yaml | 1 +
templates/compose/grocy.yaml | 1 +
templates/compose/pairdrop.yaml | 2 +-
templates/compose/snapdrop.yaml | 2 +-
templates/compose/wordpress-with-mariadb.yaml | 2 +-
templates/compose/wordpress-with-mysql.yaml | 2 +-
.../compose/wordpress-without-database.yaml | 2 +-
templates/service-templates.json | 57 +++++++++++++++++--
10 files changed, 61 insertions(+), 10 deletions(-)
diff --git a/templates/compose/dashboard.yaml b/templates/compose/dashboard.yaml
index eeca63a5b..ea2ae3489 100644
--- a/templates/compose/dashboard.yaml
+++ b/templates/compose/dashboard.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/phntxx/dashboard/wiki/Installation#installation-using-docker
# slogan: A dashboard. Inspired by SUI, it offers simple customization through JSON-files and a handy search bar to help you browse the internet more efficiently.
+# tags: dashboard, web, search, bookmarks
services:
dashboard:
diff --git a/templates/compose/emby.yaml b/templates/compose/emby.yaml
index 2bb7b1538..b5e35b4d7 100644
--- a/templates/compose/emby.yaml
+++ b/templates/compose/emby.yaml
@@ -1,5 +1,6 @@
# documentation: https://emby.media/support/articles/Home.html
# slogan: A media server software that allows you to organize, stream, and access your multimedia content effortlessly, making it easy to enjoy your favorite movies, TV shows, music, and more.
+# tags: media, server, movies, tv, music
services:
emby:
diff --git a/templates/compose/embystat.yaml b/templates/compose/embystat.yaml
index ae0b20eea..c7dac0029 100644
--- a/templates/compose/embystat.yaml
+++ b/templates/compose/embystat.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/mregni/EmbyStat/wiki/docker
# slogan: EmyStat is an open-source, self-hosted web analytics tool, designed to provide insight into website traffic and user behavior, of your local Emby deployement, all within your control.
+# tags: media, server, movies, tv, music
services:
embystat:
diff --git a/templates/compose/grocy.yaml b/templates/compose/grocy.yaml
index 3c980efb1..46edd984e 100644
--- a/templates/compose/grocy.yaml
+++ b/templates/compose/grocy.yaml
@@ -1,5 +1,6 @@
# documentation: https://github.com/grocy/grocy
# slogan: Grocy is a self-hosted, web-based household management and grocery list application, designed to simplify your household chores and grocery shopping.
+# tags: groceries, household, management, grocery, shopping
services:
grocy:
diff --git a/templates/compose/pairdrop.yaml b/templates/compose/pairdrop.yaml
index 924dae0d8..57e32afc0 100644
--- a/templates/compose/pairdrop.yaml
+++ b/templates/compose/pairdrop.yaml
@@ -1,4 +1,4 @@
-# documentation: https://github.com/schlagmichdoch/PairDrop/blob/master/docs/faq.md
+# documentation: https://github.com/schlagmichdoch/PairDrop
# slogan: Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.
# tags: file, sharing, collaboration, teamwork
diff --git a/templates/compose/snapdrop.yaml b/templates/compose/snapdrop.yaml
index 066fb2013..652eb1bbb 100644
--- a/templates/compose/snapdrop.yaml
+++ b/templates/compose/snapdrop.yaml
@@ -1,4 +1,4 @@
-# documentation: https://github.com/RobinLinus/snapdrop/blob/master/docs/faq.md
+# documentation: https://github.com/RobinLinus/snapdrop
# slogan: A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.
# tags: file, sharing, transfer, local, network, internet
diff --git a/templates/compose/wordpress-with-mariadb.yaml b/templates/compose/wordpress-with-mariadb.yaml
index 4d59daf32..b0205f952 100644
--- a/templates/compose/wordpress-with-mariadb.yaml
+++ b/templates/compose/wordpress-with-mariadb.yaml
@@ -1,5 +1,5 @@
# documentation: https://wordpress.org/documentation/
-# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
+# slogan: WordPress with MariaDB. Wordpress is open source software you can use to create a beautiful website, blog, or app.
# tags: cms, blog, content, management, mariadb
services:
diff --git a/templates/compose/wordpress-with-mysql.yaml b/templates/compose/wordpress-with-mysql.yaml
index c264ecbf3..a64952150 100644
--- a/templates/compose/wordpress-with-mysql.yaml
+++ b/templates/compose/wordpress-with-mysql.yaml
@@ -1,5 +1,5 @@
# documentation: https://wordpress.org/documentation/
-# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
+# slogan: WordPress with MySQL. Wordpress is open source software you can use to create a beautiful website, blog, or app.
# tags: cms, blog, content, management, mysql
services:
diff --git a/templates/compose/wordpress-without-database.yaml b/templates/compose/wordpress-without-database.yaml
index ddcefe90d..0891e6c11 100644
--- a/templates/compose/wordpress-without-database.yaml
+++ b/templates/compose/wordpress-without-database.yaml
@@ -1,5 +1,5 @@
# documentation: https://wordpress.org/documentation/
-# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
+# slogan: WordPress with external database. Wordpress is open source software you can use to create a beautiful website, blog, or app.
# tags: cms, blog, content, management
services:
diff --git a/templates/service-templates.json b/templates/service-templates.json
index 4c06f76c0..8fd76c18e 100644
--- a/templates/service-templates.json
+++ b/templates/service-templates.json
@@ -44,6 +44,17 @@
"collaboration"
]
},
+ "dashboard": {
+ "documentation": "https:\/\/github.com\/phntxx\/dashboard\/wiki\/Installation#installation-using-docker",
+ "slogan": "A dashboard. Inspired by SUI, it offers simple customization through JSON-files and a handy search bar to help you browse the internet more efficiently.",
+ "compose": "c2VydmljZXM6CiAgZGFzaGJvYXJkOgogICAgaW1hZ2U6ICdwaG50eHgvZGFzaGJvYXJkOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9EQVNIQk9BUkQKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2Rhc2hib2FyZC1kYXRhOi9hcHAvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MDgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
+ "tags": [
+ "dashboard",
+ "web",
+ "search",
+ "bookmarks"
+ ]
+ },
"dokuwiki": {
"documentation": "https:\/\/www.dokuwiki.org\/faq",
"slogan": "A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases with simplicity and flexibility.",
@@ -55,6 +66,30 @@
"base"
]
},
+ "emby": {
+ "documentation": "https:\/\/emby.media\/support\/articles\/Home.html",
+ "slogan": "A media server software that allows you to organize, stream, and access your multimedia content effortlessly, making it easy to enjoy your favorite movies, TV shows, music, and more.",
+ "compose": "c2VydmljZXM6CiAgZW1ieToKICAgIGltYWdlOiAnbHNjci5pby9saW51eHNlcnZlci9lbWJ5OmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FTUJZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZW1ieS1jb25maWc6L2NvbmZpZycKICAgICAgLSAnZW1ieS10dnNob3dzOi90dnNob3dzJwogICAgICAtICdlbWJ5LW1vdmllczovbW92aWVzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwOTYnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "media",
+ "server",
+ "movies",
+ "tv",
+ "music"
+ ]
+ },
+ "embystat": {
+ "documentation": "https:\/\/github.com\/mregni\/EmbyStat\/wiki\/docker",
+ "slogan": "EmyStat is an open-source, self-hosted web analytics tool, designed to provide insight into website traffic and user behavior, of your local Emby deployement, all within your control.",
+ "compose": "c2VydmljZXM6CiAgZW1ieXN0YXQ6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZW1ieXN0YXQ6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0VNQllTVEFUCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZW1ieXN0YXQtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6NjU1NScKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
+ "tags": [
+ "media",
+ "server",
+ "movies",
+ "tv",
+ "music"
+ ]
+ },
"fider": {
"documentation": "https:\/\/fider.io\/doc",
"slogan": "Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.",
@@ -76,6 +111,18 @@
"system"
]
},
+ "grocy": {
+ "documentation": "https:\/\/github.com\/grocy\/grocy",
+ "slogan": "Grocy is a self-hosted, web-based household management and grocery list application, designed to simplify your household chores and grocery shopping.",
+ "compose": "c2VydmljZXM6CiAgZ3JvY3k6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvZ3JvY3k6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0dST0NZCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnZ3JvY3ktY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
+ "tags": [
+ "groceries",
+ "household",
+ "management",
+ "grocery",
+ "shopping"
+ ]
+ },
"heimdall": {
"documentation": "https:\/\/github.com\/linuxserver\/Heimdall",
"slogan": "Heimdall is a self-hosted dashboard for managing and organizing your server applications, providing a centralized and efficient interface.",
@@ -139,7 +186,7 @@
]
},
"pairdrop": {
- "documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop\/blob\/master\/docs\/faq.md",
+ "documentation": "https:\/\/github.com\/schlagmichdoch\/PairDrop",
"slogan": "Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.",
"compose": "c2VydmljZXM6CiAgcGFpcmRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvcGFpcmRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1BBSVJEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgICAtIERFQlVHX01PREU9ZmFsc2UKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDozMDAwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
"tags": [
@@ -150,7 +197,7 @@
]
},
"snapdrop": {
- "documentation": "https:\/\/github.com\/RobinLinus\/snapdrop\/blob\/master\/docs\/faq.md",
+ "documentation": "https:\/\/github.com\/RobinLinus\/snapdrop",
"slogan": "A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.",
"compose": "c2VydmljZXM6CiAgc25hcGRyb3A6CiAgICBpbWFnZTogJ2xzY3IuaW8vbGludXhzZXJ2ZXIvc25hcGRyb3A6bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1NOQVBEUk9QCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXVyb3BlL01hZHJpZAogICAgdm9sdW1lczoKICAgICAgLSAnc25hcGRyb3AtY29uZmlnOi9jb25maWcnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [
@@ -188,7 +235,7 @@
},
"wordpress-with-mariadb": {
"documentation": "https:\/\/wordpress.org\/documentation\/",
- "slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"",
+ "slogan": "WordPress with MariaDB. Wordpress is open source software you can use to create a beautiful website, blog, or app.",
"compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBtYXJpYWRiCiAgICAgIFdPUkRQUkVTU19EQl9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1dPUkRQUkVTUwogICAgICBXT1JEUFJFU1NfREJfTkFNRTogd29yZHByZXNzCiAgICBkZXBlbmRzX29uOgogICAgICAtIG1hcmlhZGIKICBtYXJpYWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjExJwogICAgdm9sdW1lczoKICAgICAgLSAnbWFyaWFkYi1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==",
"tags": [
"cms",
@@ -200,7 +247,7 @@
},
"wordpress-with-mysql": {
"documentation": "https:\/\/wordpress.org\/documentation\/",
- "slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"",
+ "slogan": "WordPress with MySQL. Wordpress is open source software you can use to create a beautiful website, blog, or app.",
"compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCiAgICAgIFdPUkRQUkVTU19EQl9IT1NUOiBteXNxbAogICAgICBXT1JEUFJFU1NfREJfVVNFUjogJFNFUlZJQ0VfVVNFUl9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX1BBU1NXT1JEOiAkU0VSVklDRV9QQVNTV09SRF9XT1JEUFJFU1MKICAgICAgV09SRFBSRVNTX0RCX05BTUU6IHdvcmRwcmVzcwogICAgZGVwZW5kc19vbjoKICAgICAgLSBteXNxbAogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo1LjcnCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIE1ZU1FMX1JPT1RfUEFTU1dPUkQ6ICRTRVJWSUNFX1BBU1NXT1JEX1JPT1QKICAgICAgTVlTUUxfREFUQUJBU0U6IHdvcmRwcmVzcwogICAgICBNWVNRTF9VU0VSOiAkU0VSVklDRV9VU0VSX1dPUkRQUkVTUwogICAgICBNWVNRTF9QQVNTV09SRDogJFNFUlZJQ0VfUEFTU1dPUkRfV09SRFBSRVNTCg==",
"tags": [
"cms",
@@ -212,7 +259,7 @@
},
"wordpress-without-database": {
"documentation": "https:\/\/wordpress.org\/documentation\/",
- "slogan": "\"WordPress is open source software you can use to create a beautiful website, blog, or app.\"",
+ "slogan": "WordPress with external database. Wordpress is open source software you can use to create a beautiful website, blog, or app.",
"compose": "c2VydmljZXM6CiAgd29yZHByZXNzOgogICAgaW1hZ2U6ICd3b3JkcHJlc3M6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnd29yZHByZXNzLWZpbGVzOi92YXIvd3d3L2h0bWwnCiAgICBlbnZpcm9ubWVudDoKICAgICAgU0VSVklDRV9GUUROOiBudWxsCg==",
"tags": [
"cms",
From 6e98fd94039696e78ff8564b19fe59ffeecbe305 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 20:13:45 +0200
Subject: [PATCH 28/29] grafana + openblocks
---
.../compose/grafana-with-postgresql.yaml | 40 +++++++++++++++++++
templates/compose/grafana.yaml | 19 +++++++++
templates/compose/openblocks.yaml | 27 +++++++++++++
3 files changed, 86 insertions(+)
create mode 100644 templates/compose/grafana-with-postgresql.yaml
create mode 100644 templates/compose/grafana.yaml
create mode 100644 templates/compose/openblocks.yaml
diff --git a/templates/compose/grafana-with-postgresql.yaml b/templates/compose/grafana-with-postgresql.yaml
new file mode 100644
index 000000000..78c44db32
--- /dev/null
+++ b/templates/compose/grafana-with-postgresql.yaml
@@ -0,0 +1,40 @@
+# documentation: https://grafana.com/docs/grafana/latest/installation/docker/
+# slogan: Grafana is the open source analytics & monitoring solution for every database.
+# tags: grafana,analytics,monitoring,dashboard
+
+services:
+ grafana:
+ image: grafana/grafana-oss
+ environment:
+ - SERVICE_FQDN_GRAFANA
+ - GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA}
+ - GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA}
+ - GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA}
+ - GF_DATABASE_TYPE=postgres
+ - GF_DATABASE_HOST=postgresql
+ - GF_DATABASE_USER=$SERVICE_USER_POSTGRES
+ - GF_DATABASE_PASSWORD=$SERVICE_PASSWORD_POSTGRES
+ - GF_DATABASE_NAME=${POSTGRES_DB:-grafana}
+ volumes:
+ - grafana-data:/var/lib/grafana
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
+ depends_on:
+ - postgresql
+ postgresql:
+ image: postgres:15-alpine
+ volumes:
+ - postgresql-data:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_USER=$SERVICE_USER_POSTGRES
+ - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
+ - POSTGRES_DB=${POSTGRES_DB:-grafana}
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
+
diff --git a/templates/compose/grafana.yaml b/templates/compose/grafana.yaml
new file mode 100644
index 000000000..eb39909be
--- /dev/null
+++ b/templates/compose/grafana.yaml
@@ -0,0 +1,19 @@
+# documentation: https://grafana.com/docs/grafana/latest/installation/docker/
+# slogan: Grafana is the open source analytics & monitoring solution for every database.
+# tags: grafana,analytics,monitoring,dashboard
+
+services:
+ grafana:
+ image: grafana/grafana-oss
+ environment:
+ - SERVICE_FQDN_GRAFANA
+ - GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA}
+ - GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA}
+ - GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA}
+ volumes:
+ - grafana-data:/var/lib/grafana
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
diff --git a/templates/compose/openblocks.yaml b/templates/compose/openblocks.yaml
new file mode 100644
index 000000000..dda798628
--- /dev/null
+++ b/templates/compose/openblocks.yaml
@@ -0,0 +1,27 @@
+# documentation: https://docs.openblocks.dev/self-hosting
+# slogan: OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.
+# tags: openblocks,low,code,platform,open,source,low,code
+
+services:
+ openblocks:
+ image: openblocksdev/openblocks-ce
+ environment:
+ - SERVICE_FQDN_OPENBLOCKS
+ - REDIS_ENABLED=true
+ - MONGODB_ENABLED=true
+ - API_SERVICE_ENABLED=true
+ - NODE_SERVICE_ENABLED=true
+ - PUID=1000
+ - PGID=1000
+ - MONGODB_URI=mongodb://localhost:27017/openblocks?authSource=admin
+ - REDIS_URL=redis://localhost:6379
+ - JS_EXECUTOR_URI=http://localhost:6060
+ - ENABLE_USER_SIGN_UP=${ENABLE_USER_SIGN_UP:-true}
+ - ENCRYPTION_PASSWORD=$SERVICE_
+ volumes:
+ - openblocks-data:/openblocks-stacks
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
From 21795cf788341ee8e993ff12b5d852efbdaf7fcd Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Wed, 25 Oct 2023 20:19:38 +0200
Subject: [PATCH 29/29] fix: space in build args
---
app/Jobs/ApplicationDeploymentJob.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 3091bc339..8b590dd2b 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -885,14 +885,14 @@ private function start_by_compose_file()
private function generate_build_env_variables()
{
- $this->build_args = collect(["--build-arg SOURCE_COMMIT={$this->commit}"]);
+ $this->build_args = collect(["--build-arg SOURCE_COMMIT=\"{$this->commit}\""]);
if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) {
- $this->build_args->push("--build-arg {$env->key}={$env->value}");
+ $this->build_args->push("--build-arg {$env->key}=\"{$env->value}\"");
}
} else {
foreach ($this->application->build_environment_variables_preview as $env) {
- $this->build_args->push("--build-arg {$env->key}={$env->value}");
+ $this->build_args->push("--build-arg {$env->key}=\"{$env->value}\"");
}
}