commit
ea64e9d5ad
22
.tinkerwell/snippets/DeleteUser.php
Normal file
22
.tinkerwell/snippets/DeleteUser.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
$email = 'test@example.com';
|
||||||
|
$user = User::whereEmail($email)->first();
|
||||||
|
$teams = $user->teams;
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
$servers = $team->servers;
|
||||||
|
if ($servers->count() > 0) {
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
dump($server);
|
||||||
|
$server->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dump($team);
|
||||||
|
$team->delete();
|
||||||
|
}
|
||||||
|
if ($user) {
|
||||||
|
dump($user);
|
||||||
|
$user->delete();
|
||||||
|
}
|
@ -7,17 +7,34 @@
|
|||||||
class StackForm extends Component
|
class StackForm extends Component
|
||||||
{
|
{
|
||||||
public $service;
|
public $service;
|
||||||
public $isConfigurationRequired = false;
|
public $fields = [];
|
||||||
protected $listeners = ["saveCompose"];
|
protected $listeners = ["saveCompose"];
|
||||||
protected $rules = [
|
public $rules = [
|
||||||
'service.docker_compose_raw' => 'required',
|
'service.docker_compose_raw' => 'required',
|
||||||
'service.docker_compose' => 'required',
|
'service.docker_compose' => 'required',
|
||||||
'service.name' => 'required',
|
'service.name' => 'required',
|
||||||
'service.description' => 'nullable',
|
'service.description' => 'nullable',
|
||||||
];
|
];
|
||||||
public function mount () {
|
public $validationAttributes = [];
|
||||||
if ($this->service->applications->filter(fn($app) => str($app->image)->contains('minio/minio'))->count() > 0) {
|
public function mount()
|
||||||
$this->isConfigurationRequired = true;
|
{
|
||||||
|
$extraFields = $this->service->extraFields();
|
||||||
|
foreach ($extraFields as $serviceName => $fields) {
|
||||||
|
foreach ($fields as $fieldKey => $field) {
|
||||||
|
$key = data_get($field, 'key');
|
||||||
|
$value = data_get($field, 'value');
|
||||||
|
$rules = data_get($field, 'rules');
|
||||||
|
$isPassword = data_get($field, 'isPassword');
|
||||||
|
$this->fields[$key] = [
|
||||||
|
"serviceName" => $serviceName,
|
||||||
|
"key" => $key,
|
||||||
|
"name" => $fieldKey,
|
||||||
|
"value" => $value,
|
||||||
|
"isPassword" => $isPassword,
|
||||||
|
];
|
||||||
|
$this->rules["fields.$key.value"] = $rules;
|
||||||
|
$this->validationAttributes["fields.$key.value"] = $fieldKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function saveCompose($raw)
|
public function saveCompose($raw)
|
||||||
@ -32,6 +49,7 @@ public function submit()
|
|||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->service->save();
|
$this->service->save();
|
||||||
|
$this->service->saveExtraFields($this->fields);
|
||||||
$this->service->parse();
|
$this->service->parse();
|
||||||
$this->service->refresh();
|
$this->service->refresh();
|
||||||
$this->service->saveComposeConfigs();
|
$this->service->saveComposeConfigs();
|
||||||
|
@ -934,7 +934,16 @@ private function generate_healthcheck_commands()
|
|||||||
}
|
}
|
||||||
return implode(' ', $generated_healthchecks_commands);
|
return implode(' ', $generated_healthchecks_commands);
|
||||||
}
|
}
|
||||||
|
private function pull_latest_image($image)
|
||||||
|
{
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Pulling latest image ($image) from the registry.'"],
|
||||||
|
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "docker pull {$image}"), "hidden" => true
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
private function build_image()
|
private function build_image()
|
||||||
{
|
{
|
||||||
if ($this->application->build_pack === 'static') {
|
if ($this->application->build_pack === 'static') {
|
||||||
@ -948,6 +957,9 @@ private function build_image()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
|
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
|
||||||
|
if ($this->application->static_image) {
|
||||||
|
$this->pull_latest_image($this->application->static_image);
|
||||||
|
}
|
||||||
if ($this->application->build_pack === 'static') {
|
if ($this->application->build_pack === 'static') {
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
WORKDIR /usr/share/nginx/html/
|
WORKDIR /usr/share/nginx/html/
|
||||||
@ -1012,8 +1024,9 @@ private function build_image()
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
// Pure Dockerfile based deployment
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1049,6 +1062,17 @@ private function stop_running_container(bool $force = false)
|
|||||||
|
|
||||||
private function start_by_compose_file()
|
private function start_by_compose_file()
|
||||||
{
|
{
|
||||||
|
if (
|
||||||
|
!$this->application->dockerfile &&
|
||||||
|
(
|
||||||
|
$this->application->build_pack === 'dockerimage' ||
|
||||||
|
$this->application->build_pack === 'dockerfile')
|
||||||
|
) {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Pulling latest images from the registry.'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting application (could take a while).'"],
|
["echo -n 'Starting application (could take a while).'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
||||||
|
@ -45,7 +45,168 @@ public function type()
|
|||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
|
public function extraFields()
|
||||||
|
{
|
||||||
|
$fields = collect([]);
|
||||||
|
$applications = $this->applications()->get();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$image = str($application->image)->before(':')->value();
|
||||||
|
switch ($image) {
|
||||||
|
case str($image)->contains('minio'):
|
||||||
|
$console_url = $this->environment_variables()->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
|
||||||
|
$s3_api_url = $this->environment_variables()->where('key', 'MINIO_SERVER_URL')->first();
|
||||||
|
$admin_user = $this->environment_variables()->where('key', 'SERVICE_USER_MINIO')->first();
|
||||||
|
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_MINIO')->first();
|
||||||
|
$fields->put('MinIO', [
|
||||||
|
'Console URL' => [
|
||||||
|
'key' => data_get($console_url, 'key'),
|
||||||
|
'value' => data_get($console_url, 'value'),
|
||||||
|
'rules' => 'required|url',
|
||||||
|
],
|
||||||
|
'S3 API URL' => [
|
||||||
|
'key' => data_get($s3_api_url, 'key'),
|
||||||
|
'value' => data_get($s3_api_url, 'value'),
|
||||||
|
'rules' => 'required|url',
|
||||||
|
],
|
||||||
|
'Admin User' => [
|
||||||
|
'key' => data_get($admin_user, 'key'),
|
||||||
|
'value' => data_get($admin_user, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
'Admin Password' => [
|
||||||
|
'key' => data_get($admin_password, 'key'),
|
||||||
|
'value' => data_get($admin_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$databases = $this->databases()->get();
|
||||||
|
|
||||||
|
foreach ($databases as $database) {
|
||||||
|
$image = str($database->image)->before(':')->value();
|
||||||
|
switch ($image) {
|
||||||
|
case str($image)->contains('postgres'):
|
||||||
|
$userVariables = ['SERVICE_USER_POSTGRES', 'SERVICE_USER_POSTGRESQL'];
|
||||||
|
$passwordVariables = ['SERVICE_PASSWORD_POSTGRES', 'SERVICE_PASSWORD_POSTGRESQL'];
|
||||||
|
$dbNameVariables = ['POSTGRESQL_DATABASE', 'POSTGRES_DB'];
|
||||||
|
$postgres_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||||
|
$postgres_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
|
||||||
|
$postgres_db_name = $this->environment_variables()->whereIn('key', $dbNameVariables)->first();
|
||||||
|
$fields->put('PostgreSQL', [
|
||||||
|
'User' => [
|
||||||
|
'key' => data_get($postgres_user, 'key'),
|
||||||
|
'value' => data_get($postgres_user, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
'Password' => [
|
||||||
|
'key' => data_get($postgres_password, 'key'),
|
||||||
|
'value' => data_get($postgres_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
'Database Name' => [
|
||||||
|
'key' => data_get($postgres_db_name, 'key'),
|
||||||
|
'value' => data_get($postgres_db_name, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case str($image)->contains('mysql'):
|
||||||
|
$userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS'];
|
||||||
|
$passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS'];
|
||||||
|
$rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT'];
|
||||||
|
$dbNameVariables = ['MYSQL_DATABASE'];
|
||||||
|
$mysql_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||||
|
$mysql_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
|
||||||
|
$mysql_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
|
||||||
|
$mysql_db_name = $this->environment_variables()->whereIn('key', $dbNameVariables)->first();
|
||||||
|
$fields->put('MySQL', [
|
||||||
|
'User' => [
|
||||||
|
'key' => data_get($mysql_user, 'key'),
|
||||||
|
'value' => data_get($mysql_user, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
'Password' => [
|
||||||
|
'key' => data_get($mysql_password, 'key'),
|
||||||
|
'value' => data_get($mysql_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
'Root Password' => [
|
||||||
|
'key' => data_get($mysql_root_password, 'key'),
|
||||||
|
'value' => data_get($mysql_root_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
'Database Name' => [
|
||||||
|
'key' => data_get($mysql_db_name, 'key'),
|
||||||
|
'value' => data_get($mysql_db_name, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case str($image)->contains('mariadb'):
|
||||||
|
$userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER'];
|
||||||
|
$passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS'];
|
||||||
|
$rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS'];
|
||||||
|
$dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA'];
|
||||||
|
$mariadb_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||||
|
$mariadb_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
|
||||||
|
$mariadb_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
|
||||||
|
$mariadb_db_name = $this->environment_variables()->whereIn('key', $dbNameVariables)->first();
|
||||||
|
$fields->put('MariaDB', [
|
||||||
|
'User' => [
|
||||||
|
'key' => data_get($mariadb_user, 'key'),
|
||||||
|
'value' => data_get($mariadb_user, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
'Password' => [
|
||||||
|
'key' => data_get($mariadb_password, 'key'),
|
||||||
|
'value' => data_get($mariadb_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
'Root Password' => [
|
||||||
|
'key' => data_get($mariadb_root_password, 'key'),
|
||||||
|
'value' => data_get($mariadb_root_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
'Database Name' => [
|
||||||
|
'key' => data_get($mariadb_db_name, 'key'),
|
||||||
|
'value' => data_get($mariadb_db_name, 'value'),
|
||||||
|
'rules' => data_get($mariadb_db_name, 'value') && 'required',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
public function saveExtraFields($fields)
|
||||||
|
{
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$key = data_get($field, 'key');
|
||||||
|
$value = data_get($field, 'value');
|
||||||
|
$found = $this->environment_variables()->where('key', $key)->first();
|
||||||
|
if ($found) {
|
||||||
|
$found->value = $value;
|
||||||
|
$found->save();
|
||||||
|
} else {
|
||||||
|
$this->environment_variables()->create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $value,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'service_id' => $this->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public function documentation()
|
public function documentation()
|
||||||
{
|
{
|
||||||
$services = getServiceTemplates();
|
$services = getServiceTemplates();
|
||||||
@ -564,14 +725,20 @@ public function parse(bool $isNew = false): Collection
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add labels to the service
|
// Add labels to the service
|
||||||
|
if (!$isDatabase) {
|
||||||
|
if ($savedService->serviceType()) {
|
||||||
|
$fqdns = generateServiceSpecificFqdns($savedService, forTraefik: true);
|
||||||
|
} else {
|
||||||
$fqdns = collect(data_get($savedService, 'fqdns'));
|
$fqdns = collect(data_get($savedService, 'fqdns'));
|
||||||
|
}
|
||||||
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if (!$isDatabase && $fqdns->count() > 0) {
|
if ($fqdns->count() > 0) {
|
||||||
if ($fqdns) {
|
if ($fqdns) {
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
data_set($service, 'labels', $serviceLabels->toArray());
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
data_forget($service, 'is_database');
|
data_forget($service, 'is_database');
|
||||||
data_set($service, 'restart', RESTART_MODE);
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
|
@ -22,6 +22,16 @@ public function type()
|
|||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
|
public function serviceType()
|
||||||
|
{
|
||||||
|
$found = str(collect(SPECIFIC_SERVICES)->filter(function ($service) {
|
||||||
|
return str($this->image)->before(':')->value() === $service;
|
||||||
|
})->first());
|
||||||
|
if ($found->isNotEmpty()) {
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public function service()
|
public function service()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Service::class);
|
return $this->belongsTo(Service::class);
|
||||||
|
@ -29,7 +29,6 @@ public function databaseType()
|
|||||||
return "standalone-$image";
|
return "standalone-$image";
|
||||||
}
|
}
|
||||||
public function getServiceDatabaseUrl() {
|
public function getServiceDatabaseUrl() {
|
||||||
// $type = $this->databaseType();
|
|
||||||
$port = $this->public_port;
|
$port = $this->public_port;
|
||||||
$realIp = $this->service->server->ip;
|
$realIp = $this->service->server->ip;
|
||||||
if ($realIp === 'host.docker.internal' || isDev()) {
|
if ($realIp === 'host.docker.internal' || isDev()) {
|
||||||
|
@ -16,6 +16,11 @@ public function __construct(public Service $service)
|
|||||||
{
|
{
|
||||||
$this->links = collect([]);
|
$this->links = collect([]);
|
||||||
$service->applications()->get()->map(function ($application) {
|
$service->applications()->get()->map(function ($application) {
|
||||||
|
$type = $application->serviceType();
|
||||||
|
if ($type) {
|
||||||
|
$links = generateServiceSpecificFqdns($application, false);
|
||||||
|
$this->links = $this->links->merge($links);
|
||||||
|
} else {
|
||||||
if ($application->fqdn) {
|
if ($application->fqdn) {
|
||||||
$fqdns = collect(Str::of($application->fqdn)->explode(','));
|
$fqdns = collect(Str::of($application->fqdn)->explode(','));
|
||||||
$fqdns->map(function ($fqdn) {
|
$fqdns->map(function ($fqdn) {
|
||||||
@ -33,6 +38,7 @@ public function __construct(public Service $service)
|
|||||||
$this->links->push(base_url(withPort: false) . ":{$hostPort}");
|
$this->links->push(base_url(withPort: false) . ":{$hostPort}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,3 +23,6 @@
|
|||||||
'influxdb',
|
'influxdb',
|
||||||
'clickhouse/clickhouse-server'
|
'clickhouse/clickhouse-server'
|
||||||
];
|
];
|
||||||
|
const SPECIFIC_SERVICES = [
|
||||||
|
'quay.io/minio/minio',
|
||||||
|
];
|
||||||
|
@ -144,6 +144,39 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
|||||||
}
|
}
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
|
function generateServiceSpecificFqdns($service, $forTraefik = false)
|
||||||
|
{
|
||||||
|
$variables = collect($service->service->environment_variables);
|
||||||
|
$type = $service->serviceType();
|
||||||
|
$payload = collect([]);
|
||||||
|
switch ($type) {
|
||||||
|
case $type->contains('minio'):
|
||||||
|
$MINIO_BROWSER_REDIRECT_URL = $variables->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
|
||||||
|
if (is_null($MINIO_BROWSER_REDIRECT_URL?->value)) {
|
||||||
|
$MINIO_BROWSER_REDIRECT_URL->update([
|
||||||
|
"value" => generateFqdn($service->service->server, 'console-' . $service->uuid)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$MINIO_SERVER_URL = $variables->where('key', 'MINIO_SERVER_URL')->first();
|
||||||
|
if (is_null($MINIO_SERVER_URL?->value)) {
|
||||||
|
$MINIO_SERVER_URL->update([
|
||||||
|
"value" => generateFqdn($service->service->server, 'minio-' . $service->uuid)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($forTraefik) {
|
||||||
|
$payload = collect([
|
||||||
|
$MINIO_BROWSER_REDIRECT_URL->value . ':9001',
|
||||||
|
$MINIO_SERVER_URL->value . ':9000',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$payload = collect([
|
||||||
|
$MINIO_BROWSER_REDIRECT_URL->value,
|
||||||
|
$MINIO_SERVER_URL->value,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
|
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
|
||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
|
@ -126,7 +126,6 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneS
|
|||||||
function updateCompose($resource)
|
function updateCompose($resource)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ray($resource);
|
|
||||||
$name = data_get($resource, 'name');
|
$name = data_get($resource, 'name');
|
||||||
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
|
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
|
||||||
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Stringable;
|
use Illuminate\Support\Stringable;
|
||||||
use Nubs\RandomNameGenerator\All;
|
|
||||||
use Poliander\Cron\CronExpression;
|
use Poliander\Cron\CronExpression;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
use phpseclib3\Crypt\RSA;
|
use phpseclib3\Crypt\RSA;
|
||||||
@ -173,7 +172,11 @@ function get_latest_version_of_coolify(): string
|
|||||||
|
|
||||||
function generate_random_name(?string $cuid = null): string
|
function generate_random_name(?string $cuid = null): string
|
||||||
{
|
{
|
||||||
$generator = All::create();
|
$generator = new \Nubs\RandomNameGenerator\All(
|
||||||
|
[
|
||||||
|
new \Nubs\RandomNameGenerator\Alliteration(),
|
||||||
|
]
|
||||||
|
);
|
||||||
if (is_null($cuid)) {
|
if (is_null($cuid)) {
|
||||||
$cuid = new Cuid2(7);
|
$cuid = new Cuid2(7);
|
||||||
}
|
}
|
||||||
@ -444,20 +447,25 @@ function getServiceTemplates()
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$services = File::get(base_path('templates/service-templates.json'));
|
$services = File::get(base_path('templates/service-templates.json'));
|
||||||
$services = collect(json_decode($services))->sortKeys();
|
$services = collect(json_decode($services))->sortKeys();
|
||||||
$version = config('version');
|
|
||||||
$services = $services->map(function ($service) use ($version) {
|
|
||||||
if (version_compare($version, data_get($service, 'minVersion', '0.0.0'), '<')) {
|
|
||||||
$service->disabled = true;
|
|
||||||
}
|
|
||||||
return $service;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
$services = Http::get(config('constants.services.official'));
|
try {
|
||||||
if ($services->failed()) {
|
$response = Http::retry(3, 50)->get(config('constants.services.official'));
|
||||||
throw new \Exception($services->body());
|
if ($response->failed()) {
|
||||||
|
return collect([]);
|
||||||
}
|
}
|
||||||
$services = collect($services->json())->sortKeys();
|
$services = $response->json();
|
||||||
|
$services = collect($services)->sortKeys();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$services = collect([]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// $version = config('version');
|
||||||
|
// $services = $services->map(function ($service) use ($version) {
|
||||||
|
// if (version_compare($version, data_get($service, 'minVersion', '0.0.0'), '<')) {
|
||||||
|
// $service->disabled = true;
|
||||||
|
// }
|
||||||
|
// return $service;
|
||||||
|
// });
|
||||||
return $services;
|
return $services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,7 +501,8 @@ function queryResourcesByUuid(string $uuid)
|
|||||||
return $resource;
|
return $resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDeployWebhook($resource) {
|
function generateDeployWebhook($resource)
|
||||||
|
{
|
||||||
$baseUrl = base_url();
|
$baseUrl = base_url();
|
||||||
$api = Url::fromString($baseUrl) . '/api/v1';
|
$api = Url::fromString($baseUrl) . '/api/v1';
|
||||||
$endpoint = '/deploy';
|
$endpoint = '/deploy';
|
||||||
@ -501,6 +510,7 @@ function generateDeployWebhook($resource) {
|
|||||||
$url = $api . $endpoint . "?uuid=$uuid&force=false";
|
$url = $api . $endpoint . "?uuid=$uuid&force=false";
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
function removeAnsiColors($text) {
|
function removeAnsiColors($text)
|
||||||
|
{
|
||||||
return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text);
|
return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.123',
|
'release' => '4.0.0-beta.124',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.123';
|
return '4.0.0-beta.124';
|
||||||
|
@ -44,6 +44,7 @@ services:
|
|||||||
- STRIPE_PRICE_ID_PRO_YEARLY
|
- STRIPE_PRICE_ID_PRO_YEARLY
|
||||||
- STRIPE_PRICE_ID_ULTIMATE_MONTHLY
|
- STRIPE_PRICE_ID_ULTIMATE_MONTHLY
|
||||||
- STRIPE_PRICE_ID_ULTIMATE_YEARLY
|
- STRIPE_PRICE_ID_ULTIMATE_YEARLY
|
||||||
|
- STRIPE_EXCLUDED_PLANS
|
||||||
- PADDLE_VENDOR_ID
|
- PADDLE_VENDOR_ID
|
||||||
- PADDLE_WEBHOOK_SECRET
|
- PADDLE_WEBHOOK_SECRET
|
||||||
- PADDLE_VENDOR_AUTH_CODE
|
- PADDLE_VENDOR_AUTH_CODE
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
href="{{ route('project.service', $parameters) }}">
|
href="{{ route('project.service', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
<x-services.links :service="$service" />
|
<x-services.links />
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
@if (serviceStatus($service) === 'degraded')
|
@if (serviceStatus($service) === 'degraded')
|
||||||
<button wire:click='deploy' onclick="startService.showModal()"
|
<button wire:click='deploy' onclick="startService.showModal()"
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<div class="fixed z-50 top-[4.5rem] left-4" id="vue">
|
<div class="fixed z-50 top-[4.5rem] left-4" id="vue">
|
||||||
<magic-bar></magic-bar>
|
<magic-bar></magic-bar>
|
||||||
</div>
|
</div>
|
||||||
<main class="main max-w-screen-2xl">
|
<main class="pb-10 main max-w-screen-2xl">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</main>
|
</main>
|
||||||
@endsection
|
@endsection
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
services, called resources. Any CPU intensive process will use the server's CPU where you
|
services, called resources. Any CPU intensive process will use the server's CPU where you
|
||||||
are deploying your resources.</p>
|
are deploying your resources.</p>
|
||||||
<p>Localhost is the server where Coolify is running on. It is not recommended to use one server
|
<p>Localhost is the server where Coolify is running on. It is not recommended to use one server
|
||||||
for everyting.</p>
|
for everything.</p>
|
||||||
<p>Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud
|
<p>Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud
|
||||||
provider.</p>
|
provider.</p>
|
||||||
</x-slot:explanation>
|
</x-slot:explanation>
|
||||||
|
@ -182,7 +182,7 @@ class="w-full text-white rounded input input-sm bg-coolgray-200 disabled:bg-cool
|
|||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@empty
|
@empty
|
||||||
<div>No service found.</div>
|
<div>No service found. Please try to reload the list!</div>
|
||||||
@endforelse
|
@endforelse
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,19 +16,21 @@
|
|||||||
<x-forms.input label="Description" id="application.description"></x-forms.input>
|
<x-forms.input label="Description" id="application.description"></x-forms.input>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
@if (!$application->serviceType()?->contains(str($application->image)->before(':')))
|
||||||
@if ($application->required_fqdn)
|
@if ($application->required_fqdn)
|
||||||
<x-forms.input required placeholder="https://app.coolify.io" label="Domains"
|
<x-forms.input required placeholder="https://app.coolify.io" label="Domains"
|
||||||
id="application.fqdn" helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
id="application.fqdn"
|
||||||
|
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||||
@else
|
@else
|
||||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
<x-forms.input placeholder="https://app.coolify.io" label="Domains" id="application.fqdn"
|
||||||
id="application.fqdn" helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
<x-forms.input required
|
<x-forms.input required
|
||||||
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||||
label="Image" id="application.image"></x-forms.input>
|
label="Image" id="application.image"></x-forms.input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="pt-2">Advanced</h3>
|
<h3 class="pt-2">Advanced</h3>
|
||||||
<div class="w-64">
|
<div class="w-64">
|
||||||
<x-forms.checkbox instantSave label="Exclude from service status"
|
<x-forms.checkbox instantSave label="Exclude from service status"
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<div class="w-full pl-8">
|
<div class="w-full pl-8">
|
||||||
<div x-cloak x-show="activeTab === 'service-stack'">
|
<div x-cloak x-show="activeTab === 'service-stack'">
|
||||||
<livewire:project.service.stack-form :service="$service" />
|
<livewire:project.service.stack-form :service="$service" />
|
||||||
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-3">
|
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-1">
|
||||||
@foreach ($applications as $application)
|
@foreach ($applications as $application)
|
||||||
<div @class([
|
<div @class([
|
||||||
'border-l border-dashed border-red-500' => Str::of(
|
'border-l border-dashed border-red-500' => Str::of(
|
||||||
@ -58,7 +58,7 @@
|
|||||||
@endif
|
@endif
|
||||||
<div class="text-xs">{{ $application->status }}</div>
|
<div class="text-xs">{{ $application->status }}</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="flex gap-2 p-1 mx-4 font-bold rounded group-hover:text-white hover:no-underline"
|
<a class="flex items-center gap-2 p-1 mx-4 font-bold rounded group-hover:text-white hover:no-underline"
|
||||||
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $application->name]) }}"><span
|
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $application->name]) }}"><span
|
||||||
class="hover:text-warning">Logs</span></a>
|
class="hover:text-warning">Logs</span></a>
|
||||||
</div>
|
</div>
|
||||||
@ -88,7 +88,7 @@ class="hover:text-warning">Logs</span></a>
|
|||||||
@endif
|
@endif
|
||||||
<div class="text-xs">{{ $database->status }}</div>
|
<div class="text-xs">{{ $database->status }}</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="flex gap-2 p-1 mx-4 font-bold rounded hover:no-underline group-hover:text-white"
|
<a class="flex items-center gap-2 p-1 mx-4 font-bold rounded hover:no-underline group-hover:text-white"
|
||||||
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $database->name]) }}"><span
|
href="{{ route('project.service.logs', [...$parameters, 'service_name' => $database->name]) }}"><span
|
||||||
class="hover:text-warning">Logs</span></a>
|
class="hover:text-warning">Logs</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,9 +12,17 @@
|
|||||||
<x-forms.input id="service.name" required label="Service Name" placeholder="My super wordpress site" />
|
<x-forms.input id="service.name" required label="Service Name" placeholder="My super wordpress site" />
|
||||||
<x-forms.input id="service.description" label="Description" />
|
<x-forms.input id="service.description" label="Description" />
|
||||||
</div>
|
</div>
|
||||||
{{-- @if ($isConfigurationRequired)
|
@if ($fields)
|
||||||
<div class="text-warning">This service requires additional confiugration. Please check our <a
|
<div>
|
||||||
href="https://coolify.io/docs" class="text-white underline">documentation</a> for further information.
|
<h3>Service Specific Configuration</h3>
|
||||||
</div>
|
</div>
|
||||||
@endif --}}
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
@foreach ($fields as $serviceName => $fields)
|
||||||
|
<x-forms.input type="{{ data_get($fields, 'isPassword') ? 'password' : 'text' }}" required
|
||||||
|
helper="Variable name: {{ $serviceName }}"
|
||||||
|
label="{{ data_get($fields, 'serviceName') }} {{ data_get($fields, 'name') }}"
|
||||||
|
id="fields.{{ $serviceName }}.value"></x-forms.input>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# documentation: https://fider.io/doc
|
# documentation: https://fider.io/docs
|
||||||
# slogan: Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.
|
# 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
|
# tags: feedback, user-feedback
|
||||||
|
|
||||||
|
@ -13,29 +13,3 @@ services:
|
|||||||
- MINIO_ROOT_PASSWORD=$SERVICE_PASSWORD_MINIO
|
- MINIO_ROOT_PASSWORD=$SERVICE_PASSWORD_MINIO
|
||||||
volumes:
|
volumes:
|
||||||
- minio-data:/data
|
- minio-data:/data
|
||||||
|
|
||||||
# services:
|
|
||||||
# minio:
|
|
||||||
# image: minio/minio
|
|
||||||
# command: server /data --address ":9000" --console-address ":9001"
|
|
||||||
# networks:
|
|
||||||
# - coolify
|
|
||||||
# environment:
|
|
||||||
# - MINIO_SERVER_URL=http://minio.65.21.189.27.sslip.io
|
|
||||||
# - MINIO_BROWSER_REDIRECT_URL=http://console.65.21.189.27.sslip.io
|
|
||||||
# - MINIO_BROWSER=on
|
|
||||||
# - MINIO_ROOT_USER=asd
|
|
||||||
# - MINIO_ROOT_PASSWORD=asdasdasd
|
|
||||||
# labels:
|
|
||||||
# - "traefik.enable=true"
|
|
||||||
# - "traefik.http.routers.minio-console.rule=Host(`console.65.21.189.27.sslip.io`)"
|
|
||||||
# - "traefik.http.routers.minio-console.entrypoints=http"
|
|
||||||
# - "traefik.http.routers.minio-console.service=minio-console"
|
|
||||||
# - "traefik.http.services.minio-console.loadbalancer.server.port=9001"
|
|
||||||
# - "traefik.http.routers.minio.rule=Host(`minio.65.21.189.27.sslip.io`)"
|
|
||||||
# - "traefik.http.routers.minio.entrypoints=http"
|
|
||||||
# - "traefik.http.routers.minio.service=minio"
|
|
||||||
# - "traefik.http.services.minio.loadbalancer.server.port=9000"
|
|
||||||
# networks:
|
|
||||||
# coolify:
|
|
||||||
# external: true
|
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"fider": {
|
"fider": {
|
||||||
"documentation": "https:\/\/fider.io\/doc",
|
"documentation": "https:\/\/fider.io\/docs",
|
||||||
"slogan": "Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.",
|
"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": [
|
"tags": [
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"version": "3.12.36"
|
"version": "3.12.36"
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.123"
|
"version": "4.0.0-beta.124"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user