Compare commits

..

47 Commits

Author SHA1 Message Date
Andras Bacsai
e9158b7305 Merge pull request #2678 from coollabsio/next
v4.0.0-beta.306
2024-06-25 14:04:44 +02:00
Andras Bacsai
0f5690db85 fix: run container commands on high priority 2024-06-25 13:59:39 +02:00
Andras Bacsai
3ebb35a5cd fix: remove lemon + paddle things 2024-06-25 13:54:58 +02:00
Andras Bacsai
f557cd0933 fix: load js locally 2024-06-25 13:54:44 +02:00
Andras Bacsai
063aa702b1 chore: Add log1x/laravel-webfonts package 2024-06-25 13:44:46 +02:00
Andras Bacsai
4f1070083a chore: Update version to 4.0.0-beta.306 2024-06-25 13:36:02 +02:00
Andras Bacsai
5e625f71c5 feat: local fonts 2024-06-25 13:35:58 +02:00
Andras Bacsai
cc36a0ecd1 Merge pull request #2677 from coollabsio/next
v4.0.0-beta.305
2024-06-25 12:53:11 +02:00
Andras Bacsai
a849c25672 improve: update process 2024-06-25 12:48:56 +02:00
Andras Bacsai
8b95b93c72 refactor: Add is_build_time property to nixpacks_php_fallback_path and nixpacks_php_root_dir 2024-06-25 12:43:16 +02:00
Andras Bacsai
3d9557bc50 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-25 10:52:52 +02:00
Andras Bacsai
01f2f56be6 refactor: Improve formatting and readability of source.blade.php 2024-06-25 10:52:50 +02:00
andrasbacsai
e658ed993a Fix styling 2024-06-25 08:52:24 +00:00
Andras Bacsai
58fc897ea5 fix: run user commands on high prio queue 2024-06-25 10:51:32 +02:00
Andras Bacsai
f8c5a35b56 chore: Update version to 4.0.0-beta.305 2024-06-25 10:37:45 +02:00
Andras Bacsai
2c92cc40e1 refactor: Update code to use str() instead of Str::of() for string manipulation 2024-06-25 10:37:10 +02:00
Andras Bacsai
1266810c4d fix: better parsign performance for huge compose files
fix: env parsing
2024-06-25 10:34:56 +02:00
Andras Bacsai
1de8657a56 refactor: Update stack-form.blade.php to include wire:target attribute for submit button 2024-06-25 10:18:21 +02:00
Andras Bacsai
3a1e5f7f0c Merge pull request #2675 from coollabsio/next
v4.0.0-beta.304
2024-06-24 23:49:16 +02:00
Andras Bacsai
8d85976ac0 revert savecomposeconfig 2024-06-24 23:47:55 +02:00
Andras Bacsai
fe7eaa594f Merge pull request #2672 from coollabsio/next
v4.0.0-beta.303
2024-06-24 22:47:53 +02:00
Andras Bacsai
5500a1edb3 refactor: Remove commented out code in Service model's saveComposeConfigs method 2024-06-24 22:46:19 +02:00
Andras Bacsai
eb27e34972 chore: Update version to 4.0.0-beta.303 2024-06-24 22:46:16 +02:00
Andras Bacsai
8335c299b8 Merge pull request #2670 from coollabsio/next
fix fix fix
2024-06-24 21:00:03 +02:00
Andras Bacsai
feadc60b14 refactor: Improve handling of default environment in Service model's saveComposeConfigs method 2024-06-24 20:59:24 +02:00
Andras Bacsai
b59799dc2b refactor: Add default environment to Service model's saveComposeConfigs method 2024-06-24 20:58:54 +02:00
Andras Bacsai
f0b2f6eb00 Merge pull request #2669 from coollabsio/next
v4.0.0-beta.302
2024-06-24 20:56:48 +02:00
Andras Bacsai
c3fb126a0a refactor: Update Service model's saveComposeConfigs method 2024-06-24 20:55:12 +02:00
Andras Bacsai
f6708f6e47 chore: Update version to 4.0.0-beta.302 2024-06-24 20:54:53 +02:00
Andras Bacsai
d48b5a9079 Merge pull request #2664 from coollabsio/next
Refactor default_environment method to handle projects with 0 environ…
2024-06-24 18:42:21 +02:00
Andras Bacsai
99354f0d7d Refactor default_environment method to handle projects with 0 environments 2024-06-24 18:41:44 +02:00
Andras Bacsai
0fa8552aa3 Merge pull request #2663 from coollabsio/next
v4.0.0-beta.301
2024-06-24 18:35:18 +02:00
Andras Bacsai
4869c388b2 Merge pull request #2659 from kubatron117/cs-lang
CS localization
2024-06-24 18:34:54 +02:00
Andras Bacsai
2adade0927 Merge pull request #2661 from ari-party/main
Small CSS change
2024-06-24 18:34:28 +02:00
Andras Bacsai
87b7337d9e fix: projects with 0 envs 2024-06-24 18:33:01 +02:00
Andras Bacsai
351c9c1ad3 chore: Update version to 4.0.0-beta.301 2024-06-24 18:31:15 +02:00
kubatron 117
7759fe2cdf Add czech localization 2024-06-24 17:24:08 +02:00
Astrid
5d2651afc1 Add margin top to button 2024-06-24 17:20:56 +02:00
Andras Bacsai
ce3b2de5e7 Merge pull request #2656 from coollabsio/next
v4.0.0-beta.300
2024-06-24 15:40:01 +02:00
Andras Bacsai
1782f59a96 fix: MB is % lol 2024-06-24 15:38:37 +02:00
Andras Bacsai
3612096b56 Merge pull request #2655 from coollabsio/next
v4.0.0-beta.299
2024-06-24 15:22:31 +02:00
Andras Bacsai
97aa6139ea fix: get envs before sortby 2024-06-24 15:20:52 +02:00
Andras Bacsai
e5d915a7a9 chore: Move server delete component to the bottom of the page 2024-06-24 14:55:25 +02:00
Andras Bacsai
5bc31c305c chore: Update version to 4.0.0-beta.299 2024-06-24 14:48:18 +02:00
Andras Bacsai
8c7590a249 fix: remove zoom from modals 2024-06-24 14:47:55 +02:00
Andras Bacsai
d54d524cef Update blacksmith logo size in README.md 2024-06-24 14:47:46 +02:00
Andras Bacsai
a3d3ada500 fix: app deployment should be in high queue 2024-06-24 14:47:39 +02:00
98 changed files with 510 additions and 736 deletions

View File

@@ -48,7 +48,7 @@ Special thanks to our biggest sponsors!
<a href="https://fractalnetworks.co/?ref=coolify.io" target="_blank"><img src="./other/logos/fractal.svg" alt="fractal logo" width="180"/></a>
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></a>
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.png" alt="blacksmith logo" width="180"/></a>
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.svg" alt="blacksmith logo" width="200"/></a>
## Github Sponsors ($40+)
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>

View File

@@ -3,6 +3,7 @@
namespace App\Actions\CoolifyTask;
use App\Data\CoolifyTaskArgs;
use App\Enums\ActivityTypes;
use App\Jobs\CoolifyTask;
use Spatie\Activitylog\Models\Activity;
@@ -40,8 +41,18 @@ class PrepareCoolifyTask
public function __invoke(): Activity
{
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data);
dispatch($job);
$job = new CoolifyTask(
activity: $this->activity,
ignore_errors: $this->remoteProcessArgs->ignore_errors,
call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish,
call_event_data: $this->remoteProcessArgs->call_event_data,
);
if ($this->remoteProcessArgs->type === ActivityTypes::COMMAND->value) {
ray('Dispatching a high priority job');
dispatch($job)->onQueue('high');
} else {
dispatch($job);
}
$this->activity->refresh();
return $this->activity;

View File

@@ -39,7 +39,7 @@ class RunRemoteProcess
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null)
{
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value && $activity->getExtraProperty('type') !== ActivityTypes::COMMAND->value) {
throw new \RuntimeException('Incompatible Activity to run a remote command.');
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Database;
use App\Models\StandaloneClickhouse;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -155,11 +154,11 @@ class StartClickhouse
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Database;
use App\Models\StandaloneDragonfly;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -155,7 +154,7 @@ class StartDragonfly
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
}

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Database;
use App\Models\StandaloneKeydb;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -163,7 +162,7 @@ class StartKeydb
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Database;
use App\Models\StandaloneMariadb;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -157,18 +156,18 @@ class StartMariadb
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($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()) {
if ($environment_variables->filter(fn ($env) => str($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()) {
if ($environment_variables->filter(fn ($env) => str($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()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Database;
use App\Models\StandaloneMongodb;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -174,15 +173,15 @@ class StartMongodb
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
$environment_variables->push("MONGO_INITDB_ROOT_USERNAME={$this->database->mongo_initdb_root_username}");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
$environment_variables->push("MONGO_INITDB_ROOT_PASSWORD={$this->database->mongo_initdb_root_password}");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Database;
use App\Models\StandaloneMysql;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -157,18 +156,18 @@ class StartMysql
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($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()) {
if ($environment_variables->filter(fn ($env) => str($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()) {
if ($environment_variables->filter(fn ($env) => str($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()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Database;
use App\Models\StandalonePostgresql;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -179,18 +178,18 @@ class StartPostgresql
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('POSTGRES_USER'))->isEmpty()) {
$environment_variables->push("POSTGRES_USER={$this->database->postgres_user}");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PGUSER'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('PGUSER'))->isEmpty()) {
$environment_variables->push("PGUSER={$this->database->postgres_user}");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('POSTGRES_PASSWORD'))->isEmpty()) {
$environment_variables->push("POSTGRES_PASSWORD={$this->database->postgres_password}");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_DB'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('POSTGRES_DB'))->isEmpty()) {
$environment_variables->push("POSTGRES_DB={$this->database->postgres_db}");
}

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Database;
use App\Models\StandaloneRedis;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -167,7 +166,7 @@ class StartRedis
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Proxy;
use App\Models\Server;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
class CheckConfiguration
@@ -24,7 +23,7 @@ class CheckConfiguration
$proxy_configuration = instant_remote_process($payload, $server, false);
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
}
if (! $proxy_configuration || is_null($proxy_configuration)) {
throw new \Exception('Could not generate proxy configuration');

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Proxy;
use App\Models\Server;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
class SaveConfiguration
@@ -18,7 +17,7 @@ class SaveConfiguration
$proxy_path = $server->proxyPath();
$docker_compose_yml_base64 = base64_encode($proxy_settings);
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$server->proxy->last_saved_settings = str($docker_compose_yml_base64)->pipe('md5')->value;
$server->save();
return instant_remote_process([

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Proxy;
use App\Events\ProxyStarted;
use App\Models\Server;
use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
use Spatie\Activitylog\Models\Activity;
@@ -27,7 +26,7 @@ class StartProxy
}
SaveConfiguration::run($server, $configuration);
$docker_compose_yml_base64 = base64_encode($configuration);
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value;
$server->save();
if ($server->isSwarm()) {
$commands = $commands->merge([

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Actions\Server;
use App\Enums\ActivityTypes;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class RunCommand
{
use AsAction;
public function handle(Server $server, $command)
{
$activity = remote_process(command: [$command], server: $server, ignore_errors: true, type: ActivityTypes::COMMAND->value);
return $activity;
}
}

View File

@@ -25,7 +25,7 @@ class UpdateCoolify
if (! $this->server) {
return;
}
CleanupDocker::run($this->server, false);
CleanupDocker::dispatch($this->server, false)->onQueue('high');
$this->latestVersion = get_latest_version_of_coolify();
$this->currentVersion = config('version');
if (! $manual_update) {
@@ -48,6 +48,7 @@ class UpdateCoolify
private function update()
{
if (isDev()) {
ray('Running in dev mode');
remote_process([
'sleep 10',
], $this->server);

View File

@@ -82,7 +82,7 @@ class WaitlistInvite extends Command
if (! $already_registered) {
$this->password = Str::password();
User::create([
'name' => Str::of($this->next_patient->email)->before('@'),
'name' => str($this->next_patient->email)->before('@'),
'email' => $this->next_patient->email,
'password' => Hash::make($this->password),
'force_password_reset' => true,

View File

@@ -5,4 +5,5 @@ namespace App\Enums;
enum ActivityTypes: string
{
case INLINE = 'inline';
case COMMAND = 'command';
}

View File

@@ -81,8 +81,8 @@ class Controller extends BaseController
$token = request()->get('token');
if ($token) {
$decrypted = Crypt::decryptString($token);
$email = Str::of($decrypted)->before('@@@');
$password = Str::of($decrypted)->after('@@@');
$email = str($decrypted)->before('@@@');
$password = str($decrypted)->after('@@@');
$user = User::whereEmail($email)->first();
if (! $user) {
return redirect()->route('login');

View File

@@ -965,6 +965,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$nixpacks_php_fallback_path = new EnvironmentVariable();
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
$nixpacks_php_fallback_path->value = '/index.php';
$nixpacks_php_fallback_path->is_build_time = false;
$nixpacks_php_fallback_path->application_id = $this->application->id;
$nixpacks_php_fallback_path->save();
}
@@ -972,6 +973,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$nixpacks_php_root_dir = new EnvironmentVariable();
$nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR';
$nixpacks_php_root_dir->value = '/app/public';
$nixpacks_php_root_dir->is_build_time = false;
$nixpacks_php_root_dir->application_id = $this->application->id;
$nixpacks_php_root_dir->save();
}
@@ -1076,13 +1078,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->application_deployment_queue->addLogEntry("Healthcheck logs: {$health_check_logs} | Return code: {$health_check_return_code}");
}
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
if (str($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
$this->newVersionIsHealthy = true;
$this->application->update(['status' => 'running']);
$this->application_deployment_queue->addLogEntry('New container is healthy.');
break;
}
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') {
if (str($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') {
$this->newVersionIsHealthy = false;
$this->query_logs();
break;
@@ -1094,7 +1096,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$sleeptime++;
}
}
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'starting') {
if (str($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'starting') {
$this->query_logs();
}
}
@@ -1535,7 +1537,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile_from_repo', 'ignore_errors' => true,
]);
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
$dockerfile = collect(str($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
$this->application->parseHealthcheckFromDockerfile($dockerfile);
}
$docker_compose = [
@@ -2107,7 +2109,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile',
]);
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) {
if (data_get($env, 'is_multiline') === true) {

View File

@@ -20,9 +20,9 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue
*/
public function __construct(
public Activity $activity,
public bool $ignore_errors = false,
public $call_event_on_finish = null,
public $call_event_data = null
public bool $ignore_errors,
public $call_event_on_finish,
public $call_event_data,
) {}
/**

View File

@@ -98,7 +98,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
return;
}
$status = Str::of(data_get($this->database, 'status'));
$status = str(data_get($this->database, 'status'));
if (! $status->startsWith('running') && $this->database->id !== 0) {
ray('database not running');
@@ -236,7 +236,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
return;
}
}
$this->backup_dir = backup_dir().'/databases/'.Str::of($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
if ($this->database->name === 'coolify-db') {
$databasesToBackup = ['coolify'];

View File

@@ -5,7 +5,6 @@ namespace App\Livewire\Project\Application;
use App\Models\Application;
use App\Models\LocalFileVolume;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
@@ -199,8 +198,8 @@ class General extends Component
return str($volume)->startsWith('/data/coolify');
})->unique()->values();
foreach ($volumes as $volume) {
$source = Str::of($volume)->before(':');
$target = Str::of($volume)->after(':')->beforeLast(':');
$source = str($volume)->before(':');
$target = str($volume)->after(':')->beforeLast(':');
LocalFileVolume::updateOrCreate(
[

View File

@@ -3,7 +3,6 @@
namespace App\Livewire\Project\Application\Preview;
use App\Models\Application;
use Illuminate\Support\Str;
use Livewire\Component;
use Spatie\Url\Url;
@@ -32,10 +31,10 @@ class Form extends Component
public function generate_real_url()
{
if (data_get($this->application, 'fqdn')) {
$firstFqdn = Str::of($this->application->fqdn)->before(',');
$firstFqdn = str($this->application->fqdn)->before(',');
$url = Url::fromString($firstFqdn);
$host = $url->getHost();
$this->preview_url_template = Str::of($this->application->preview_url_template)->replace('{{domain}}', $host);
$this->preview_url_template = str($this->application->preview_url_template)->replace('{{domain}}', $host);
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Livewire\Project\Application;
use App\Models\Application;
use Illuminate\Support\Str;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
@@ -50,16 +49,16 @@ class Rollback extends Component
$output = instant_remote_process([
"docker inspect --format='{{.Config.Image}}' {$this->application->uuid}",
], $this->application->destination->server, throwError: false);
$current_tag = Str::of($output)->trim()->explode(':');
$current_tag = str($output)->trim()->explode(':');
$this->current = data_get($current_tag, 1);
$output = instant_remote_process([
"docker images --format '{{.Repository}}#{{.Tag}}#{{.CreatedAt}}'",
], $this->application->destination->server);
$this->images = Str::of($output)->trim()->explode("\n")->filter(function ($item) use ($image) {
return Str::of($item)->contains($image);
$this->images = str($output)->trim()->explode("\n")->filter(function ($item) use ($image) {
return str($item)->contains($image);
})->map(function ($item) {
$item = Str::of($item)->explode('#');
$item = str($item)->explode('#');
if ($item[1] === $this->current) {
// $is_current = true;
}

View File

@@ -6,7 +6,6 @@ use App\Models\Application;
use App\Models\Project;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Illuminate\Support\Str;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
@@ -29,9 +28,9 @@ class DockerImage extends Component
$this->validate([
'dockerImage' => 'required',
]);
$image = Str::of($this->dockerImage)->before(':');
if (Str::of($this->dockerImage)->contains(':')) {
$tag = Str::of($this->dockerImage)->after(':');
$image = str($this->dockerImage)->before(':');
if (str($this->dockerImage)->contains(':')) {
$tag = str($this->dockerImage)->after(':');
} else {
$tag = 'latest';
}

View File

@@ -193,7 +193,7 @@ class GithubPrivateRepositoryDeployKey extends Component
return;
}
if (Str::of($this->repository_url)->startsWith('http')) {
if (str($this->repository_url)->startsWith('http')) {
$this->git_host = $this->repository_url_parsed->getHost();
$this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2);
$this->git_repository = Str::finish("git@$this->git_host:$this->git_repository", '.git');

View File

@@ -11,7 +11,7 @@ class EditCompose extends Component
public $serviceId;
protected $listeners = ['refreshEnvs' => 'mount'];
protected $listeners = ['refreshEnvs', 'envsUpdated'];
protected $rules = [
'service.docker_compose_raw' => 'required',
@@ -19,6 +19,17 @@ class EditCompose extends Component
'service.is_container_label_escape_enabled' => 'required',
];
public function envsUpdated()
{
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
$this->refreshEnvs();
}
public function refreshEnvs()
{
$this->service = Service::find($this->serviceId);
}
public function mount()
{
$this->service = Service::find($this->serviceId);

View File

@@ -14,7 +14,6 @@ use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Support\Str;
use Livewire\Component;
class FileStorage extends Component
@@ -37,9 +36,9 @@ class FileStorage extends Component
public function mount()
{
$this->resource = $this->fileStorage->service;
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
if (str($this->fileStorage->fs_path)->startsWith('.')) {
$this->workdir = $this->resource->service?->workdir();
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
$this->fs_path = str($this->fileStorage->fs_path)->after('.');
} else {
$this->workdir = null;
$this->fs_path = $this->fileStorage->fs_path;

View File

@@ -81,7 +81,6 @@ class StackForm extends Component
return handleError($e, $this);
} finally {
if (is_null($this->service->config_hash)) {
ray('asdf');
$this->service->isConfigurationChanged(true);
} else {
$this->dispatch('configurationChanged');

View File

@@ -112,7 +112,7 @@ class Show extends Component
$this->serialize();
$this->env->save();
$this->dispatch('success', 'Environment variable updated.');
$this->dispatch('refreshEnvs');
$this->dispatch('envsUpdated');
} catch (\Exception $e) {
return handleError($e);
}

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Shared;
use App\Actions\Server\RunCommand;
use App\Models\Application;
use App\Models\Server;
use App\Models\Service;
@@ -137,7 +138,7 @@ class ExecuteContainerCommand extends Component
} else {
$exec = "docker exec {$container_name} {$cmd}";
}
$activity = remote_process([$exec], $server, ignore_errors: true);
$activity = RunCommand::run(server: $server, command: $exec);
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@@ -2,6 +2,7 @@
namespace App\Livewire;
use App\Actions\Server\RunCommand as ServerRunCommand;
use App\Models\Server;
use Livewire\Component;
@@ -33,7 +34,7 @@ class RunCommand extends Component
{
$this->validate();
try {
$activity = remote_process([$this->command], Server::where('uuid', $this->server)->first(), ignore_errors: true);
$activity = ServerRunCommand::run(server: Server::where('uuid', $this->server)->first(), command: $this->command);
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@@ -6,7 +6,6 @@ use App\Actions\Proxy\CheckConfiguration;
use App\Actions\Proxy\SaveConfiguration;
use App\Actions\Proxy\StartProxy;
use App\Models\Server;
use Illuminate\Support\Str;
use Livewire\Component;
class Proxy extends Component
@@ -79,7 +78,7 @@ class Proxy extends Component
{
try {
$this->proxy_settings = CheckConfiguration::run($this->server);
if (Str::of($this->proxy_settings)->contains('--api.dashboard=true') && Str::of($this->proxy_settings)->contains('--api.insecure=true')) {
if (str($this->proxy_settings)->contains('--api.dashboard=true') && str($this->proxy_settings)->contains('--api.insecure=true')) {
$this->dispatch('traefikDashboardAvailable', true);
} else {
$this->dispatch('traefikDashboardAvailable', false);

View File

@@ -3,7 +3,6 @@
namespace App\Livewire\Subscription;
use App\Models\Team;
use Illuminate\Support\Facades\Http;
use Livewire\Component;
class Actions extends Component
@@ -15,70 +14,6 @@ class Actions extends Component
$this->server_limits = Team::serverLimit();
}
public function cancel()
{
try {
$subscription_id = currentTeam()->subscription->lemon_subscription_id;
if (! $subscription_id) {
throw new \Exception('No subscription found');
}
$response = Http::withHeaders([
'Accept' => 'application/vnd.api+json',
'Content-Type' => 'application/vnd.api+json',
'Authorization' => 'Bearer '.config('subscription.lemon_squeezy_api_key'),
])->delete('https://api.lemonsqueezy.com/v1/subscriptions/'.$subscription_id);
$json = $response->json();
if ($response->failed()) {
$error = data_get($json, 'errors.0.status');
if ($error === '404') {
throw new \Exception('Subscription not found.');
}
throw new \Exception(data_get($json, 'errors.0.title', 'Something went wrong. Please try again later.'));
} else {
$this->dispatch('success', 'Subscription cancelled successfully. Reloading in 5s.');
$this->dispatch('reloadWindow', 5000);
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function resume()
{
try {
$subscription_id = currentTeam()->subscription->lemon_subscription_id;
if (! $subscription_id) {
throw new \Exception('No subscription found');
}
$response = Http::withHeaders([
'Accept' => 'application/vnd.api+json',
'Content-Type' => 'application/vnd.api+json',
'Authorization' => 'Bearer '.config('subscription.lemon_squeezy_api_key'),
])->patch('https://api.lemonsqueezy.com/v1/subscriptions/'.$subscription_id, [
'data' => [
'type' => 'subscriptions',
'id' => $subscription_id,
'attributes' => [
'cancelled' => false,
],
],
]);
$json = $response->json();
if ($response->failed()) {
$error = data_get($json, 'errors.0.status');
if ($error === '404') {
throw new \Exception('Subscription not found.');
}
throw new \Exception(data_get($json, 'errors.0.title', 'Something went wrong. Please try again later.'));
} else {
$this->dispatch('success', 'Subscription resumed successfully. Reloading in 5s.');
$this->dispatch('reloadWindow', 5000);
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function stripeCustomerPortal()
{
$session = getStripeCustomerPortalSession(currentTeam());

View File

@@ -52,7 +52,7 @@ class InviteLink extends Component
if (is_null($user)) {
$password = Str::password();
$user = User::create([
'name' => Str::of($this->email)->before('@'),
'name' => str($this->email)->before('@'),
'email' => $this->email,
'password' => Hash::make($password),
'force_password_reset' => true,

View File

@@ -28,11 +28,11 @@ class Application extends BaseModel
}
$application->forceFill([
'fqdn' => $application->fqdn,
'install_command' => Str::of($application->install_command)->trim(),
'build_command' => Str::of($application->build_command)->trim(),
'start_command' => Str::of($application->start_command)->trim(),
'base_directory' => Str::of($application->base_directory)->trim(),
'publish_directory' => Str::of($application->publish_directory)->trim(),
'install_command' => str($application->install_command)->trim(),
'build_command' => str($application->build_command)->trim(),
'start_command' => str($application->start_command)->trim(),
'base_directory' => str($application->base_directory)->trim(),
'publish_directory' => str($application->publish_directory)->trim(),
]);
});
static::created(function ($application) {
@@ -902,9 +902,9 @@ class Application extends BaseModel
$type = null;
$source = null;
if (is_string($volume)) {
$source = Str::of($volume)->before(':');
$source = str($volume)->before(':');
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
$type = Str::of('bind');
$type = str('bind');
}
} elseif (is_array($volume)) {
$type = data_get_str($volume, 'type');

View File

@@ -174,7 +174,7 @@ class EnvironmentVariable extends Model
if (str($environment_variable)->startsWith('{{'.$type) && str($environment_variable)->endsWith('}}')) {
$variable = Str::after($environment_variable, "{$type}.");
$variable = Str::before($variable, '}}');
$variable = Str::of($variable)->trim()->value;
$variable = str($variable)->trim()->value;
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
return $variable;
}
@@ -220,7 +220,7 @@ class EnvironmentVariable extends Model
protected function key(): Attribute
{
return Attribute::make(
set: fn (string $value) => Str::of($value)->trim(),
set: fn (string $value) => str($value)->trim(),
);
}
}

View File

@@ -4,7 +4,6 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class LocalPersistentVolume extends Model
{
@@ -33,14 +32,14 @@ class LocalPersistentVolume extends Model
protected function name(): Attribute
{
return Attribute::make(
set: fn (string $value) => Str::of($value)->trim()->value,
set: fn (string $value) => str($value)->trim()->value,
);
}
protected function mountPath(): Attribute
{
return Attribute::make(
set: fn (string $value) => Str::of($value)->trim()->start('/')->value
set: fn (string $value) => str($value)->trim()->start('/')->value
);
}
@@ -49,7 +48,7 @@ class LocalPersistentVolume extends Model
return Attribute::make(
set: function (?string $value) {
if ($value) {
return Str::of($value)->trim()->start('/')->value;
return str($value)->trim()->start('/')->value;
} else {
return $value;
}

View File

@@ -116,10 +116,14 @@ class Project extends BaseModel
public function default_environment()
{
$default = $this->environments()->where('name', 'production')->first();
if (! $default) {
$default = $this->environments()->sortBy('created_at')->first();
if ($default) {
return $default->name;
}
$default = $this->environments()->get();
if ($default->count() > 0) {
return $default->sortBy('created_at')->first()->name;
}
return $default;
return null;
}
}

View File

@@ -11,7 +11,6 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Support\Stringable;
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
@@ -29,10 +28,10 @@ class Server extends BaseModel
static::saving(function ($server) {
$payload = [];
if ($server->user) {
$payload['user'] = Str::of($server->user)->trim();
$payload['user'] = str($server->user)->trim();
}
if ($server->ip) {
$payload['ip'] = Str::of($server->ip)->trim();
$payload['ip'] = str($server->ip)->trim();
}
$server->forceFill($payload);
});
@@ -875,7 +874,7 @@ $schema://$host {
$releaseLines = collect(explode("\n", $os_release));
$collectedData = collect([]);
foreach ($releaseLines as $line) {
$item = Str::of($line)->trim();
$item = str($line)->trim();
$collectedData->put($item->before('=')->value(), $item->after('=')->lower()->replace('"', '')->value());
}
$ID = data_get($collectedData, 'ID');

View File

@@ -839,29 +839,13 @@ class Service extends BaseModel
$commands[] = "cd $workdir";
$json = Yaml::parse($this->docker_compose);
$envs_from_coolify = $this->environment_variables()->get();
foreach ($json['services'] as $service => $config) {
$envs = collect($config['environment']);
$envs->push("COOLIFY_CONTAINER_NAME=$service-{$this->uuid}");
foreach ($envs_from_coolify as $env) {
$envs = $envs->map(function ($value) use ($env) {
if (str($value)->startsWith($env->key)) {
return "{$env->key}={$env->real_value}";
}
return $value;
});
}
$envs = $envs->unique();
data_set($json, "services.$service.environment", $envs->toArray());
}
$this->docker_compose = Yaml::dump($json, 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
$docker_compose_base64 = base64_encode($this->docker_compose);
$commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
$commands[] = 'rm -f .env || true';
$envs_from_coolify = $this->environment_variables()->get();
foreach ($envs_from_coolify as $env) {
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
}
@@ -880,7 +864,6 @@ class Service extends BaseModel
{
$networks = getTopLevelNetworks($this);
// ray($networks);
return $networks;
}
}

View File

@@ -15,22 +15,7 @@ class Subscription extends Model
public function type()
{
if (isLemon()) {
$basic = explode(',', config('subscription.lemon_squeezy_basic_plan_ids'));
$pro = explode(',', config('subscription.lemon_squeezy_pro_plan_ids'));
$ultimate = explode(',', config('subscription.lemon_squeezy_ultimate_plan_ids'));
$subscription = $this->lemon_variant_id;
if (in_array($subscription, $basic)) {
return 'basic';
}
if (in_array($subscription, $pro)) {
return 'pro';
}
if (in_array($subscription, $ultimate)) {
return 'ultimate';
}
} elseif (isStripe()) {
if (isStripe()) {
if (! $this->stripe_plan_id) {
return 'zero';
}

View File

@@ -8,7 +8,6 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Str;
class DeploymentFailed extends Notification implements ShouldQueue
{
@@ -41,8 +40,8 @@ class DeploymentFailed extends Notification implements ShouldQueue
$this->project_uuid = data_get($application, 'environment.project.uuid');
$this->environment_name = data_get($application, 'environment.name');
$this->fqdn = data_get($application, 'fqdn');
if (Str::of($this->fqdn)->explode(',')->count() > 1) {
$this->fqdn = Str::of($this->fqdn)->explode(',')->first();
if (str($this->fqdn)->explode(',')->count() > 1) {
$this->fqdn = str($this->fqdn)->explode(',')->first();
}
$this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
}

View File

@@ -8,7 +8,6 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Str;
class DeploymentSuccess extends Notification implements ShouldQueue
{
@@ -41,8 +40,8 @@ class DeploymentSuccess extends Notification implements ShouldQueue
$this->project_uuid = data_get($application, 'environment.project.uuid');
$this->environment_name = data_get($application, 'environment.name');
$this->fqdn = data_get($application, 'fqdn');
if (Str::of($this->fqdn)->explode(',')->count() > 1) {
$this->fqdn = Str::of($this->fqdn)->explode(',')->first();
if (str($this->fqdn)->explode(',')->count() > 1) {
$this->fqdn = str($this->fqdn)->explode(',')->first();
}
$this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
}

View File

@@ -7,7 +7,6 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Str;
class StatusChanged extends Notification implements ShouldQueue
{
@@ -31,8 +30,8 @@ class StatusChanged extends Notification implements ShouldQueue
$this->project_uuid = data_get($resource, 'environment.project.uuid');
$this->environment_name = data_get($resource, 'environment.name');
$this->fqdn = data_get($resource, 'fqdn', null);
if (Str::of($this->fqdn)->explode(',')->count() > 1) {
$this->fqdn = Str::of($this->fqdn)->explode(',')->first();
if (str($this->fqdn)->explode(',')->count() > 1) {
$this->fqdn = str($this->fqdn)->explode(',')->first();
}
$this->resource_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->resource->uuid}";
}

View File

@@ -7,7 +7,6 @@ use App\Models\Server;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Str;
trait ExecuteRemoteCommand
{
@@ -45,7 +44,7 @@ trait ExecuteRemoteCommand
}
$remote_command = generateSshCommand($this->server, $command);
$process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden, $customType, $append) {
$output = Str::of($output)->trim();
$output = str($output)->trim();
if ($output->startsWith('╔')) {
$output = "\n".$output;
}

View File

@@ -6,7 +6,6 @@ use App\Models\Service;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\View\Component;
class Links extends Component
@@ -26,16 +25,16 @@ class Links extends Component
$this->links = $this->links->merge($links);
} else {
if ($application->fqdn) {
$fqdns = collect(Str::of($application->fqdn)->explode(','));
$fqdns = collect(str($application->fqdn)->explode(','));
$fqdns->map(function ($fqdn) {
$this->links->push(getFqdnWithoutPort($fqdn));
});
}
if ($application->ports) {
$portsCollection = collect(Str::of($application->ports)->explode(','));
$portsCollection = collect(str($application->ports)->explode(','));
$portsCollection->map(function ($port) {
if (Str::of($port)->contains(':')) {
$hostPort = Str::of($port)->before(':');
if (str($port)->contains(':')) {
$hostPort = str($port)->before(':');
} else {
$hostPort = $port;
}

View File

@@ -46,11 +46,11 @@ function queue_application_deployment(Application $application, string $deployme
if ($no_questions_asked) {
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id,
));
))->onQueue('high');
} elseif (next_queuable($server_id, $application_id)) {
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id,
));
))->onQueue('high');
}
}
function force_start_deployment(ApplicationDeploymentQueue $deployment)
@@ -61,7 +61,7 @@ function force_start_deployment(ApplicationDeploymentQueue $deployment)
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id,
));
))->onQueue('high');
}
function queue_next_deployment(Application $application)
{
@@ -74,7 +74,7 @@ function queue_next_deployment(Application $application)
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $next_found->id,
));
))->onQueue('high');
}
}
@@ -115,7 +115,7 @@ function next_after_cancel(?Server $server = null)
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $next->id,
));
))->onQueue('high');
}
break;
}

View File

@@ -91,7 +91,7 @@ function format_docker_envs_to_json($rawOutput)
}
function checkMinimumDockerEngineVersion($dockerVersion)
{
$majorDockerVersion = Str::of($dockerVersion)->before('.')->value();
$majorDockerVersion = str($dockerVersion)->before('.')->value();
if ($majorDockerVersion <= 22) {
$dockerVersion = null;
}
@@ -152,7 +152,7 @@ function get_port_from_dockerfile($dockerfile): ?int
$dockerfile_array = explode("\n", $dockerfile);
$found_exposed_port = null;
foreach ($dockerfile_array as $line) {
$line_str = Str::of($line)->trim();
$line_str = str($line)->trim();
if ($line_str->startsWith('EXPOSE')) {
$found_exposed_port = $line_str->replace('EXPOSE', '')->trim();
break;
@@ -534,7 +534,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
$labels = collect([]);
if ($pull_request_id === 0) {
if ($application->fqdn) {
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
$domains = str(data_get($application, 'fqdn'))->explode(',');
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
@@ -558,7 +558,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
}
} else {
if (data_get($preview, 'fqdn')) {
$domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
$domains = str(data_get($preview, 'fqdn'))->explode(',');
} else {
$domains = collect([]);
}

View File

@@ -85,7 +85,7 @@ function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $m
function get_installation_path(GithubApp $source)
{
$github = GithubApp::where('uuid', $source->uuid)->first();
$name = Str::of(Str::kebab($github->name));
$name = str(Str::kebab($github->name));
$installation_path = $github->html_url === 'https://github.com' ? 'apps' : 'github-apps';
return "$github->html_url/$installation_path/$name/installations/new";
@@ -93,7 +93,7 @@ function get_installation_path(GithubApp $source)
function get_permissions_path(GithubApp $source)
{
$github = GithubApp::where('uuid', $source->uuid)->first();
$name = Str::of(Str::kebab($github->name));
$name = str(Str::kebab($github->name));
return "$github->html_url/settings/apps/$name/permissions";
}

View File

@@ -4,7 +4,6 @@ use App\Models\Application;
use App\Models\EnvironmentVariable;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use Illuminate\Support\Str;
use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
@@ -38,7 +37,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
]);
instant_remote_process($commands, $server);
foreach ($fileVolumes as $fileVolume) {
$path = Str::of(data_get($fileVolume, 'fs_path'));
$path = str(data_get($fileVolume, 'fs_path'));
$content = data_get($fileVolume, 'content');
if ($path->startsWith('.')) {
$path = $path->after('.');
@@ -68,7 +67,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
$fileVolume->is_directory = false;
$fileVolume->save();
$content = base64_encode($content);
$dir = Str::of($fileLocation)->dirname();
$dir = str($fileLocation)->dirname();
instant_remote_process([
"mkdir -p $dir",
"echo '$content' | base64 -d | tee $fileLocation",
@@ -106,7 +105,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$resourceFqdns = str($resource->fqdn)->explode(',');
if ($resourceFqdns->count() === 1) {
$resourceFqdns = $resourceFqdns->first();
$variableName = 'SERVICE_FQDN_'.Str::of($resource->name)->upper()->replace('-', '');
$variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$fqdn = Url::fromString($resourceFqdns);
$port = $fqdn->getPort();
@@ -125,14 +124,14 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$generatedEnv->save();
}
}
$variableName = 'SERVICE_URL_'.Str::of($resource->name)->upper()->replace('-', '');
$variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$url = Url::fromString($fqdn);
$port = $url->getPort();
$path = $url->getPath();
$url = $url->getHost();
if ($generatedEnv) {
$url = Str::of($fqdn)->after('://');
$url = str($fqdn)->after('://');
$generatedEnv->value = $url.$path;
$generatedEnv->save();
}
@@ -175,7 +174,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$port_env_url->save();
}
} else {
$variableName = 'SERVICE_FQDN_'.Str::of($resource->name)->upper()->replace('-', '');
$variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$fqdn = Url::fromString($fqdn);
$fqdn = $fqdn->getScheme().'://'.$fqdn->getHost().$fqdn->getPath();
@@ -183,12 +182,12 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$generatedEnv->value = $fqdn;
$generatedEnv->save();
}
$variableName = 'SERVICE_URL_'.Str::of($resource->name)->upper()->replace('-', '');
$variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$url = Url::fromString($fqdn);
$url = $url->getHost().$url->getPath();
if ($generatedEnv) {
$url = Str::of($fqdn)->after('://');
$url = str($fqdn)->after('://');
$generatedEnv->value = $url;
$generatedEnv->save();
}

View File

@@ -469,7 +469,7 @@ function data_get_str($data, $key, $default = null): Stringable
{
$str = data_get($data, $key, $default) ?? $default;
return Str::of($str);
return str($str);
}
function generateFqdn(Server $server, string $random)
@@ -933,12 +933,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$content = null;
$isDirectory = false;
if (is_string($volume)) {
$source = Str::of($volume)->before(':');
$target = Str::of($volume)->after(':')->beforeLast(':');
$source = str($volume)->before(':');
$target = str($volume)->after(':')->beforeLast(':');
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
$type = Str::of('bind');
$type = str('bind');
} else {
$type = Str::of('volume');
$type = str('volume');
}
} elseif (is_array($volume)) {
$type = data_get_str($volume, 'type');
@@ -987,8 +987,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$slugWithoutUuid = Str::slug($source, '-');
$name = "{$savedService->service->uuid}_{$slugWithoutUuid}";
if (is_string($volume)) {
$source = Str::of($volume)->before(':');
$target = Str::of($volume)->after(':')->beforeLast(':');
$source = str($volume)->before(':');
$target = str($volume)->after(':')->beforeLast(':');
$source = $name;
$volume = "$source:$target";
} elseif (is_array($volume)) {
@@ -1032,7 +1032,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// Get variables from the service
foreach ($serviceVariables as $variableName => $variable) {
if (is_numeric($variableName)) {
$variable = Str::of($variable);
$variable = str($variable);
if ($variable->contains('=')) {
// - SESSION_SECRET=123
// - SESSION_SECRET=
@@ -1046,8 +1046,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} else {
// SESSION_SECRET: 123
// SESSION_SECRET:
$key = Str::of($variableName);
$value = Str::of($variable);
$key = str($variableName);
$value = str($variable);
}
if ($key->startsWith('SERVICE_FQDN')) {
if ($isNew || $savedService->fqdn === null) {
@@ -1137,7 +1137,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'service_id' => $resource->id,
])->first();
$value = Str::of(replaceVariables($value));
$value = str(replaceVariables($value));
$key = $value;
if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([
@@ -1170,7 +1170,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// }
} else {
if ($command->value() === 'URL') {
$fqdn = Str::of($fqdn)->after('://')->value();
$fqdn = str($fqdn)->after('://')->value();
}
EnvironmentVariable::create([
'key' => $key,
@@ -1254,18 +1254,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
]);
}
}
$envs_from_coolify = $resource->environment_variables()->get();
$serviceVariables = $serviceVariables->map(function ($variable) use ($envs_from_coolify) {
$env_variable_key = str($variable)->before('=');
$env_variable_value = str($variable)->after('=');
$found_env = $envs_from_coolify->where('key', $env_variable_key)->first();
if ($found_env) {
$env_variable_value = $found_env->value;
}
return "$env_variable_key=$env_variable_value";
});
}
// Add labels to the service
if ($savedService->serviceType()) {
@@ -1333,6 +1321,41 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_set($service, 'environment', $serviceVariables->toArray());
updateCompose($savedService);
return $service;
});
$envs_from_coolify = $resource->environment_variables()->get();
$services = collect($services)->map(function ($service, $serviceName) use ($resource, $envs_from_coolify) {
$serviceVariables = collect(data_get($service, 'environment', []));
$parsedServiceVariables = collect([]);
foreach ($serviceVariables as $key => $value) {
if (is_numeric($key)) {
$value = str($value);
if ($value->contains('=')) {
$key = $value->before('=')->value();
$value = $value->after('=')->value();
} else {
$key = $value->value();
$value = null;
}
$parsedServiceVariables->put($key, $value);
} else {
$parsedServiceVariables->put($key, $value);
}
}
$parsedServiceVariables->put('COOLIFY_CONTAINER_NAME', "$serviceName-{$resource->uuid}");
$parsedServiceVariables = $parsedServiceVariables->map(function ($value, $key) use ($envs_from_coolify) {
$found_env = $envs_from_coolify->where('key', $key)->first();
if ($found_env) {
return $found_env->value;
}
return $value;
});
data_set($service, 'environment', $parsedServiceVariables->toArray());
return $service;
});
$finalServices = [
@@ -1637,7 +1660,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// Get variables from the service
foreach ($serviceVariables as $variableName => $variable) {
if (is_numeric($variableName)) {
$variable = Str::of($variable);
$variable = str($variable);
if ($variable->contains('=')) {
// - SESSION_SECRET=123
// - SESSION_SECRET=
@@ -1651,8 +1674,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} else {
// SESSION_SECRET: 123
// SESSION_SECRET:
$key = Str::of($variableName);
$value = Str::of($variable);
$key = str($variableName);
$value = str($variable);
}
if ($key->startsWith('SERVICE_FQDN')) {
if ($isNew) {
@@ -1696,7 +1719,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'application_id' => $resource->id,
'is_preview' => false,
])->first();
$value = Str::of(replaceVariables($value));
$value = str(replaceVariables($value));
$key = $value;
if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([
@@ -1718,7 +1741,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$fqdn = data_get($foundEnv, 'value');
} else {
if ($command?->value() === 'URL') {
$fqdn = Str::of($fqdn)->after('://')->value();
$fqdn = str($fqdn)->after('://')->value();
}
EnvironmentVariable::create([
'key' => $key,

View File

@@ -1,51 +1,8 @@
<?php
use App\Models\Team;
use Illuminate\Support\Carbon;
use Stripe\Stripe;
function getSubscriptionLink($type)
{
$checkout_id = config("subscription.lemon_squeezy_checkout_id_$type");
if (! $checkout_id) {
return null;
}
$user_id = auth()->user()->id;
$team_id = currentTeam()->id ?? null;
$email = auth()->user()->email ?? null;
$name = auth()->user()->name ?? null;
$url = "https://store.coollabs.io/checkout/buy/$checkout_id?";
if ($user_id) {
$url .= "&checkout[custom][user_id]={$user_id}";
}
if (isset($team_id)) {
$url .= "&checkout[custom][team_id]={$team_id}";
}
if ($email) {
$url .= "&checkout[email]={$email}";
}
if ($name) {
$url .= "&checkout[name]={$name}";
}
return $url;
}
function getPaymentLink()
{
return currentTeam()->subscription->lemon_update_payment_menthod_url;
}
function getRenewDate()
{
return Carbon::parse(currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s');
}
function getEndDate()
{
return Carbon::parse(currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s');
}
function isSubscriptionActive()
{
if (! isCloud()) {
@@ -60,12 +17,6 @@ function isSubscriptionActive()
if (is_null($subscription)) {
return false;
}
if (isLemon()) {
return $subscription->lemon_status === 'active';
}
// if (isPaddle()) {
// return $subscription->paddle_status === 'active';
// }
if (isStripe()) {
return $subscription->stripe_invoice_paid === true;
}
@@ -82,12 +33,6 @@ function isSubscriptionOnGracePeriod()
if (! $subscription) {
return false;
}
if (isLemon()) {
$is_still_grace_period = $subscription->lemon_ends_at &&
Carbon::parse($subscription->lemon_ends_at) > Carbon::now();
return $is_still_grace_period;
}
if (isStripe()) {
return $subscription->stripe_cancel_at_period_end;
}
@@ -98,18 +43,10 @@ function subscriptionProvider()
{
return config('subscription.provider');
}
function isLemon()
{
return config('subscription.provider') === 'lemon';
}
function isStripe()
{
return config('subscription.provider') === 'stripe';
}
function isPaddle()
{
return config('subscription.provider') === 'paddle';
}
function getStripeCustomerPortalSession(Team $team)
{
Stripe::setApiKey(config('subscription.stripe_api_key'));

View File

@@ -24,6 +24,7 @@
"league/flysystem-aws-s3-v3": "^3.0",
"league/flysystem-sftp-v3": "^3.0",
"livewire/livewire": "3.4.9",
"log1x/laravel-webfonts": "^1.0",
"lorisleiva/laravel-actions": "^2.7",
"nubs/random-name-generator": "^2.2",
"phpseclib/phpseclib": "~3.0",

64
composer.lock generated
View File

@@ -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": "dbce9f366320f4d58392673fe25c69f6",
"content-hash": "168e351cec87acbea9c1c745b83eead2",
"packages": [
{
"name": "amphp/amp",
@@ -4522,6 +4522,68 @@
],
"time": "2024-03-14T14:03:32+00:00"
},
{
"name": "log1x/laravel-webfonts",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/Log1x/laravel-webfonts.git",
"reference": "0d38122aa7f5501394006a6715f7d97dac223507"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Log1x/laravel-webfonts/zipball/0d38122aa7f5501394006a6715f7d97dac223507",
"reference": "0d38122aa7f5501394006a6715f7d97dac223507",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^7.8",
"laravel/prompts": "^0.1.15",
"php": ">=8.1"
},
"require-dev": {
"illuminate/console": "^10.41",
"illuminate/http": "^10.41",
"illuminate/support": "^10.41",
"laravel/pint": "^1.13"
},
"type": "package",
"extra": {
"laravel": {
"providers": [
"Log1x\\LaravelWebfonts\\WebfontsServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Log1x\\LaravelWebfonts\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brandon Nifong",
"email": "brandon@tendency.me",
"homepage": "https://github.com/log1x"
}
],
"description": "Download, install, and preload over 1500 Google fonts locally in your Laravel project",
"support": {
"issues": "https://github.com/Log1x/laravel-webfonts/issues",
"source": "https://github.com/Log1x/laravel-webfonts/tree/v1.0.1"
},
"funding": [
{
"url": "https://github.com/Log1x",
"type": "github"
}
],
"time": "2024-03-28T11:53:11+00:00"
},
{
"name": "lorisleiva/laravel-actions",
"version": "v2.8.0",

View File

@@ -7,7 +7,7 @@ return [
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.298',
'release' => '4.0.0-beta.306',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@@ -1,7 +1,8 @@
<?php
return [
'provider' => env('SUBSCRIPTION_PROVIDER', null), // stripe, paddle, lemon
'provider' => env('SUBSCRIPTION_PROVIDER', null), // stripe
// Stripe
'stripe_api_key' => env('STRIPE_API_KEY', null),
'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', null),
@@ -22,29 +23,4 @@ return [
'stripe_price_id_dynamic_monthly' => env('STRIPE_PRICE_ID_DYNAMIC_MONTHLY', null),
'stripe_price_id_dynamic_yearly' => env('STRIPE_PRICE_ID_DYNAMIC_YEARLY', null),
// Paddle
'paddle_vendor_id' => env('PADDLE_VENDOR_ID', null),
'paddle_vendor_auth_code' => env('PADDLE_VENDOR_AUTH_CODE', null),
'paddle_webhook_secret' => env('PADDLE_WEBHOOK_SECRET', null),
'paddle_public_key' => env('PADDLE_PUBLIC_KEY', null),
'paddle_price_id_basic_monthly' => env('PADDLE_PRICE_ID_BASIC_MONTHLY', null),
'paddle_price_id_basic_yearly' => env('PADDLE_PRICE_ID_BASIC_YEARLY', null),
'paddle_price_id_pro_monthly' => env('PADDLE_PRICE_ID_PRO_MONTHLY', null),
'paddle_price_id_pro_yearly' => env('PADDLE_PRICE_ID_PRO_YEARLY', null),
'paddle_price_id_ultimate_monthly' => env('PADDLE_PRICE_ID_ULTIMATE_MONTHLY', null),
'paddle_price_id_ultimate_yearly' => env('PADDLE_PRICE_ID_ULTIMATE_YEARLY', null),
// Lemon
'lemon_squeezy_api_key' => env('LEMON_SQUEEZY_API_KEY', null),
'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET', null),
'lemon_squeezy_checkout_id_basic_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_BASIC_MONTHLY', null),
'lemon_squeezy_checkout_id_basic_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_BASIC_YEARLY', null),
'lemon_squeezy_checkout_id_pro_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_PRO_MONTHLY', null),
'lemon_squeezy_checkout_id_pro_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_PRO_YEARLY', null),
'lemon_squeezy_checkout_id_ultimate_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_MONTHLY', null),
'lemon_squeezy_checkout_id_ultimate_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_YEARLY', null),
'lemon_squeezy_basic_plan_ids' => env('LEMON_SQUEEZY_BASIC_PLAN_IDS', ''),
'lemon_squeezy_pro_plan_ids' => env('LEMON_SQUEEZY_PRO_PLAN_IDS', ''),
'lemon_squeezy_ultimate_plan_ids' => env('LEMON_SQUEEZY_ULTIMATE_PLAN_IDS', ''),
];

View File

@@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.298';
return '4.0.0-beta.306';

View File

@@ -69,27 +69,6 @@ services:
- STRIPE_PRICE_ID_ULTIMATE_MONTHLY_OLD
- STRIPE_PRICE_ID_ULTIMATE_YEARLY_OLD
- STRIPE_EXCLUDED_PLANS
- PADDLE_VENDOR_ID
- PADDLE_WEBHOOK_SECRET
- PADDLE_VENDOR_AUTH_CODE
- PADDLE_PUBLIC_KEY
- PADDLE_PRICE_ID_BASIC_MONTHLY
- PADDLE_PRICE_ID_BASIC_YEARLY
- PADDLE_PRICE_ID_PRO_MONTHLY
- PADDLE_PRICE_ID_PRO_YEARLY
- PADDLE_PRICE_ID_ULTIMATE_MONTHLY
- PADDLE_PRICE_ID_ULTIMATE_YEARLY
- LEMON_SQUEEZY_API_KEY
- LEMON_SQUEEZY_WEBHOOK_SECRET
- LEMON_SQUEEZY_CHECKOUT_ID_BASIC_MONTHLY
- LEMON_SQUEEZY_CHECKOUT_ID_BASIC_YEARLY
- LEMON_SQUEEZY_CHECKOUT_ID_PRO_MONTHLY
- LEMON_SQUEEZY_CHECKOUT_ID_PRO_YEARLY
- LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_MONTHLY
- LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_YEARLY
- LEMON_SQUEEZY_BASIC_PLAN_IDS
- LEMON_SQUEEZY_PRO_PLAN_IDS
- LEMON_SQUEEZY_ULTIMATE_PLAN_IDS
ports:
- "${APP_PORT:-8000}:80"
expose:

30
lang/cs.json Normal file
View File

@@ -0,0 +1,30 @@
{
"auth.login": "Přihlásit se",
"auth.login.azure": "Přihlásit se pomocí Microsoftu",
"auth.login.bitbucket": "Přihlásit se pomocí Bitbucketu",
"auth.login.github": "Přihlásit se pomocí GitHubu",
"auth.login.gitlab": "Přihlásit se pomocí Gitlabu",
"auth.login.google": "Přihlásit se pomocí Google",
"auth.already_registered": "Již jste registrováni?",
"auth.confirm_password": "Potvrďte heslo",
"auth.forgot_password": "Zapomněli jste heslo",
"auth.forgot_password_send_email": "Poslat e-mail pro resetování hesla",
"auth.register_now": "Registrovat se",
"auth.logout": "Odhlásit se",
"auth.register": "Registrovat se",
"auth.registration_disabled": "Registrace je zakázána. Kontaktujte prosím administrátora.",
"auth.reset_password": "Obnovit heslo",
"auth.failed": "Tyto údaje neodpovídají našim záznamům.",
"auth.failed.callback": "Nepodařilo se zpracovat zpětné volání od poskytovatele přihlášení.",
"auth.failed.password": "Zadané heslo je nesprávné.",
"auth.failed.email": "Nemůžeme najít uživatele s touto e-mailovou adresou.",
"auth.throttle": "Příliš mnoho pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.",
"input.name": "Jméno",
"input.email": "E-mail",
"input.password": "Heslo",
"input.password.again": "Heslo znovu",
"input.code": "Jednorázový kód",
"input.recovery_code": "Obnovovací kód",
"button.save": "Uložit",
"repository.url": "<span class='text-helper'>Příklady</span><br>Pro veřejné repozitáře, použijte <span class='text-helper'>https://...</span>.<br>Pro soukromé repozitáře, použijte <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>main</span> branch bude zvolena<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nodejs-fastify</span> branch bude vybrána.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>main</span> branch vybrána.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>main</span> branch bude vybrána."
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,18 @@
<svg width="500" height="62" viewBox="0 0 500 62" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M93.0578 0H101.918C101.9 7.56173 101.918 14.7077 101.97 22.2693C105.483 18.628 109.791 16.8508 114.895 16.9378C120.189 16.8952 124.862 18.5505 128.914 21.9034C132.741 25.5912 134.913 30.1037 135.428 35.4412C136.18 41.2349 135.138 46.671 132.301 51.7493C128.695 57.2448 123.605 60.3287 117.031 61.0009C112.204 61.5389 107.792 60.4586 103.794 57.7602C103.156 57.257 102.548 56.7168 101.97 56.1399C101.918 57.6378 101.9 59.1363 101.918 60.6351H93.0578V46.4178C93.5587 43.3221 94.9832 40.7434 97.3313 38.6819C94.957 36.6721 93.5325 34.1284 93.0578 31.0505V0ZM111.82 24.7782C119.282 24.5024 123.92 27.9871 125.735 35.2321C126.466 38.8811 126.153 42.4355 124.797 45.8951C122.104 51.35 117.708 53.8067 111.611 53.2651C107.399 52.9021 104.255 50.8984 102.178 47.2541C101.844 46.0348 101.497 44.8152 101.136 43.5952C100.406 41.6257 99.2072 40.0054 97.5398 38.7342C99.8148 36.8727 101.222 34.4858 101.761 31.5732C103.024 28.6346 105.16 26.631 108.172 25.5623C109.377 25.1811 110.593 24.9198 111.82 24.7782Z" fill="white"/>
<path d="M468.713 0H459.853C459.836 11.1858 459.853 22.3714 459.905 33.557C460.364 36.6739 461.771 39.2525 464.127 41.2929C461.771 43.3333 460.364 45.9118 459.905 49.0287C459.853 53.0359 459.836 57.0432 459.853 61.0507H468.713C468.731 57.0781 468.713 53.1057 468.661 49.1333C468.233 45.9729 466.825 43.3594 464.44 41.2929C466.141 39.811 467.374 37.9991 468.14 35.8568C468.576 34.2795 469.063 32.7114 469.599 31.1526C471.611 26.6578 475.085 24.3928 480.022 24.3576C482.93 24.2492 485.432 25.19 487.527 27.1801C489.547 29.7393 490.555 32.6664 490.55 35.9614C490.602 44.3244 490.62 52.6875 490.602 61.0507H499.983C500.031 51.6755 499.979 42.3019 499.826 32.9298C499.661 31.229 499.348 29.5564 498.888 27.9119C497.762 23.7875 495.381 20.6339 491.748 18.4511C488.173 16.6468 484.387 15.915 480.387 16.2558C475.604 16.6067 471.73 18.6452 468.765 22.3713C468.713 14.9143 468.695 7.45719 468.713 0Z" fill="white"/>
<path d="M432.685 48.9195V25.027H425.036V16.8623H432.685V4.57233H442.053V16.8623H452.796V25.027H442.053V47.9741C442.053 51.4118 443.256 52.701 446.78 52.701H453.311V60.8657H444.717C436.294 60.8657 432.685 56.9122 432.685 48.9195Z" fill="white"/>
<path d="M226.002 60.6327C212.681 60.6327 204.172 51.9524 204.172 38.2013C204.172 24.6222 212.939 15.5981 226.26 15.5981C237.605 15.5981 244.652 21.872 246.457 31.8415H236.745C235.542 26.6848 231.846 23.5049 226.088 23.5049C218.611 23.5049 213.712 29.521 213.712 38.2013C213.712 46.7957 218.611 52.7259 226.088 52.7259C231.76 52.7259 235.542 49.46 236.659 44.3893H246.457C244.738 54.3588 237.261 60.6327 226.002 60.6327Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M199.194 52.0383H201.085V60.1171H196.186C190.17 60.1171 188.107 57.4528 188.107 53.0697C185.185 57.5387 180.716 60.6327 173.583 60.6327C163.957 60.6327 157.254 55.9058 157.254 47.7411C157.254 38.717 163.785 33.6463 176.075 33.6463H187.162V30.982C187.162 26.0832 183.638 23.0752 177.536 23.0752C172.036 23.0752 168.34 25.6535 167.653 29.521H158.543C159.488 20.9266 166.879 15.5981 177.966 15.5981C189.654 15.5981 196.358 21.1844 196.358 31.5836V49.2022C196.358 51.4367 197.304 52.0383 199.194 52.0383ZM187.162 42.0688V40.5218H175.646C169.887 40.5218 166.707 42.6704 166.707 47.1395C166.707 50.8351 169.801 53.4134 174.872 53.4134C182.607 53.4134 187.076 48.8584 187.162 42.0688Z" fill="white"/>
<path d="M299.159 45.5925H290.049C290.307 55.1323 298.386 60.6327 310.16 60.6327C320.817 60.6327 328.81 55.3042 328.81 46.8817C328.81 37.0841 320.645 35.0214 310.59 33.8182C304.23 33.1306 300.019 32.615 300.019 28.4896C300.019 24.9659 303.628 22.7314 309.129 22.7314C314.629 22.7314 318.411 25.5676 318.926 29.6069H328.036C327.435 20.5828 319.786 15.598 308.957 15.598C298.386 15.5121 290.909 20.9266 290.909 29.2631C290.909 38.3732 298.816 40.5218 308.871 41.725L309.545 41.81C315.927 42.6125 319.7 43.0869 319.7 47.6552C319.7 51.1789 315.918 53.4134 310.16 53.4134C303.457 53.4134 299.503 50.1475 299.159 45.5925Z" fill="white"/>
<path d="M150.551 0H141.736V31.7437C141.736 35.377 143.369 38.7982 146.144 40.9782C143.369 43.1582 141.736 46.5794 141.736 50.2127V60.6016H150.551V50.2127C150.551 46.5794 148.918 43.1582 146.144 40.9782C148.918 38.7982 150.551 35.377 150.551 31.7437V0Z" fill="white"/>
<path d="M409.901 16.8345H418.715V31.8426C418.715 35.4759 417.083 38.8972 414.308 41.0772C411.534 38.8972 409.901 35.4759 409.901 31.8426V16.8345Z" fill="white"/>
<path d="M414.308 41.0772C417.083 43.2571 418.715 46.6784 418.715 50.3117V60.7005H409.901V50.3117C409.901 46.6784 411.534 43.2571 414.308 41.0772Z" fill="white"/>
<path d="M262.398 0H253.584L253.514 24.0881C253.514 27.7214 255.147 31.3505 257.922 33.5304C255.147 35.7104 253.514 38.9238 253.514 42.5571L253.584 60.6016H262.398L262.329 42.5571C262.329 38.9238 260.696 35.7104 257.922 33.5304C260.696 31.3505 262.329 27.7214 262.329 24.0881L262.398 0Z" fill="white"/>
<path d="M276.675 16.2241C280.359 16.1892 284.042 16.2241 287.724 16.3286C282.257 22.3171 276.733 28.2584 271.151 34.1525C276.581 42.2157 281.993 50.3353 287.389 58.4297L287.392 58.4343L287.402 58.4496L288.766 60.4963H277.718C275.533 57.282 273.362 54.0588 271.203 50.8264C267.716 45.0332 264.189 39.2661 260.624 33.5252C266.034 27.8033 271.385 22.0362 276.675 16.2241Z" fill="white"/>
<path d="M414.308 11.4341C417.351 11.4341 419.817 8.96756 419.817 5.9249C419.817 2.88223 417.351 0.415667 414.308 0.415667C411.265 0.415667 408.799 2.88223 408.799 5.9249C408.799 8.96756 411.265 11.4341 414.308 11.4341Z" fill="white"/>
<path d="M354.895 16.2542C358.451 16.0176 361.791 16.753 364.916 18.4605C366.963 19.8309 368.582 21.5925 369.773 23.7452C372.505 19.6097 376.34 17.1982 381.276 16.5107C385.58 15.8328 389.67 16.4827 393.546 18.4605C396.922 20.6189 399.121 23.6461 400.141 27.5421C400.706 29.7642 401.012 32.0218 401.061 34.3148C401.112 43.1056 401.13 51.8964 401.112 60.6873H391.399C391.437 51.4511 391.386 42.2156 391.245 32.9808C390.978 30.5307 390.041 28.3929 388.433 26.5672C386.896 25.1403 385.072 24.4391 382.963 24.4636C377.949 24.5997 374.711 27.0625 373.249 31.852C372.758 33.7694 372.553 35.7191 372.636 37.7011C372.585 45.3631 372.567 53.0252 372.585 60.6873H362.871C362.888 52.2385 362.871 43.7897 362.82 35.3409C362.847 32.7158 362.234 30.253 360.979 27.9525C358.681 24.9256 355.664 23.8482 351.93 24.7201C349.246 25.3215 347.218 26.8095 345.846 29.1839C344.889 30.9043 344.378 32.7514 344.312 34.7252C343.658 37.2555 342.345 39.3591 340.375 41.0362C342.935 43.1807 344.35 45.9343 344.619 49.2968C344.67 53.0935 344.687 56.8903 344.67 60.6873H335.876C335.859 56.822 335.876 52.9567 335.928 49.0916C336.256 45.8143 337.671 43.1293 340.171 41.0362C337.934 39.1303 336.554 36.7188 336.03 33.8017C335.88 28.1604 335.829 22.5165 335.876 16.8699H344.67C344.653 18.2386 344.67 19.6068 344.721 20.9746C347.517 18.1096 350.908 16.5361 354.895 16.2542Z" fill="white"/>
<path d="M0.509564 10.8453C-1.07569 7.67952 1.2298 3.95471 4.77453 3.95471H69.2141C72.7589 3.95471 75.0643 7.67952 73.4791 10.8453L66.3265 25.1291C65.5188 26.7422 63.8677 27.7611 62.0616 27.7611H11.9271C10.121 27.7611 8.46986 26.7422 7.66213 25.1291L0.509564 10.8453Z" fill="white"/>
<path d="M55.5976 49.4381C57.1828 52.6039 54.8773 56.3287 51.3326 56.3287L22.6559 56.3287C19.1111 56.3287 16.8056 52.6039 18.3909 49.4381L25.5435 35.1543C26.3512 33.5413 28.0023 32.5224 29.8084 32.5224H44.18C45.9861 32.5224 47.6373 33.5413 48.445 35.1543L55.5976 49.4381Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

16
public/js/apexcharts.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,11 @@
@import 'fonts';
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body {
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400;

72
resources/css/fonts.css Normal file
View File

@@ -0,0 +1,72 @@
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 100;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-100.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 200;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-200.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-300.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 500;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-500.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 600;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-600.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-700.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 800;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-800.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 900;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-900.woff2') format('woff2');
}
@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-regular.woff2') format('woff2');
}

View File

@@ -61,8 +61,7 @@
@endif
<template x-teleport="body">
<div x-show="modalOpen"
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen"
style="zoom:1.1;" x-cloak>
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen" x-cloak>
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false"

View File

@@ -5,9 +5,10 @@
'disabled' => false,
'action' => 'delete',
'content' => null,
'closeOutside' => true
'closeOutside' => true,
])
<div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false" class="relative w-auto h-auto">
<div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false"
class="relative w-auto h-auto">
@if ($content)
<div @click="modalOpen=true">
{{ $content }}
@@ -22,13 +23,15 @@
@endif
@endif
<template x-teleport="body">
<div x-show="modalOpen" class="fixed top-0 left-0 lg:px-0 px-4 z-[99] flex items-center justify-center w-screen h-screen" style="zoom:1.1;"
x-cloak>
<div x-show="modalOpen"
class="fixed top-0 left-0 lg:px-0 px-4 z-[99] flex items-center justify-center w-screen h-screen" x-cloak>
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" @if ($closeOutside) @click.outside="modalOpen=false" @endif x-transition:enter="ease-out duration-100"
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen"
@if ($closeOutside) @click.outside="modalOpen=false" @endif
x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-100"

View File

@@ -1,80 +0,0 @@
<x-slot:basic>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic" class="w-full h-10 buyme"
x-on:click="subscribe('basic-monthly')"> Subscribe
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic" class="w-full h-10 buyme"
x-on:click="subscribe('basic-yearly')"> Subscribe
</x-forms.button>
</x-slot:basic>
<x-slot:pro>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme"
x-on:click="subscribe('pro-monthly')"> Subscribe
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme"
x-on:click="subscribe('pro-yearly')"> Subscribe
</x-forms.button>
</x-slot:pro>
<x-slot:ultimate>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-ultimate" class="w-full h-10 buyme"
x-on:click="subscribe('ultimate-monthly')"> Subscribe
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-ultimate" class="w-full h-10 buyme"
x-on:click="subscribe('ultimate-yearly')"> Subscribe
</x-forms.button>
</x-slot:ultimate>
<x-slot:other>
<script src="https://cdn.paddle.com/paddle/v2/paddle.js"></script>
<script type="text/javascript">
Paddle.Environment.set("{{ isDev() ? 'sandbox' : 'production' }}");
Paddle.Setup({
seller: {{ config('subscription.paddle_vendor_id') }},
checkout: {
settings: {
displayMode: "overlay",
theme: "light",
}
}
});
function subscribe(type) {
let priceId = null
switch (type) {
case 'basic-monthly':
priceId = "{{ config('subscription.paddle_price_id_basic_monthly') }}"
break;
case 'basic-yearly':
priceId = "{{ config('subscription.paddle_price_id_basic_yearly') }}"
break;
case 'pro-monthly':
priceId = "{{ config('subscription.paddle_price_id_pro_monthly') }}"
break;
case 'pro-yearly':
priceId = "{{ config('subscription.paddle_price_id_pro_yearly') }}"
break;
case 'ultimate-monthly':
priceId = "{{ config('subscription.paddle_price_id_ultimate_monthly') }}"
break;
case 'ultimate-yearly':
priceId = "{{ config('subscription.paddle_price_id_ultimate_yearly') }}"
break;
default:
break;
}
Paddle.Checkout.open({
customer: {
email: '{{ auth()->user()->email }}',
},
customData: {
"team_id": "{{ currentTeam()->id }}",
},
items: [{
priceId,
quantity: 1
}],
});
}
</script>
</x-slot:other>

View File

@@ -34,15 +34,16 @@
<div>
</div>
</div>
{{-- <div class="p-4 rounded bg-coolgray-400">
<div class="p-4 rounded bg-coolgray-400">
<h2 id="tier-hobby" class="flex items-start gap-4 text-4xl font-bold tracking-tight">Unlimited Trial
<x-forms.button><a class="font-bold dark:text-white hover:no-underline"
href="https://github.com/coollabsio/coolify">Get Started</a></x-forms.button>
</h2>
<p class="mt-4 text-sm leading-6">Start self-hosting <span class="dark:text-warning">without limits</span> with
<p class="mt-4 text-sm leading-6">Start self-hosting <span class="dark:text-warning">without limits</span>
with
our
OSS version. Same features as the paid version, but you have to manage by yourself.</p>
</div> --}}
</div>
<div class="flow-root mt-12">
<div class="pb-10 text-xl text-center">For the detailed list of features, please visit our landing page: <a
@@ -78,8 +79,8 @@
</p>
<ul role="list" class="space-y-3 text-sm leading-6 ">
<li class="flex">
<svg class="flex-none w-5 h-6 mr-3 dark:text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<svg class="flex-none w-5 h-6 mr-3 dark:text-warning" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.775 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />
@@ -141,13 +142,14 @@
{{ $pro }}
@endisset
@endif
<p class="h-20 mt-10 text-sm leading-6 dark:text-white">Expand your business or set up your own hosting
<p class="h-20 mt-10 text-sm leading-6 dark:text-white">Expand your business or set up your own
hosting
environment.
</p>
<ul role="list" class="mt-6 space-y-3 text-sm leading-6 ">
<li class="flex ">
<svg class="flex-none w-5 h-6 mr-3 dark:text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<svg class="flex-none w-5 h-6 mr-3 dark:text-warning" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />
@@ -188,7 +190,7 @@
</div>
<div class="pt-16 lg:px-8 lg:pt-0 xl:px-12">
<h3 id="tier-ultimate" class="text-base font-semibold leading-7 dark:text-white">Ultimate</h3>
<p class="flex items-baseline mt-6 gap-x-1">
<p class="flex items-baseline mt-6 gap-x-1">
<span x-show="selected === 'monthly'" x-cloak>
<span class="text-4xl font-bold tracking-tight dark:text-white">Custom</span>
{{-- <span class="text-sm font-semibold leading-6 ">pay-as-you-go</span> --}}
@@ -198,7 +200,7 @@
{{-- <span class="text-sm font-semibold leading-6 ">/month + VAT</span> --}}
</span>
</p>
<span x-show="selected === 'monthly'" x-cloak>
<span x-show="selected === 'monthly'" x-cloak>
<span>pay-as-you-go</span>
</span>
<span x-show="selected === 'yearly'" x-cloak>
@@ -213,8 +215,8 @@
single location.</p>
<ul role="list" class="mt-6 space-y-3 text-sm leading-6 ">
<li class="flex ">
<svg class="flex-none w-5 h-6 mr-3 dark:text-warning" viewBox="0 0 20 20" fill="currentColor"
aria-hidden="true">
<svg class="flex-none w-5 h-6 mr-3 dark:text-warning" viewBox="0 0 20 20"
fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
clip-rule="evenodd" />

View File

@@ -46,13 +46,6 @@
stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
{{-- <div class="flex-1 text-xl font-bold leading-6 dark:text-white">Dashboard</div> --}}
{{-- <a href="#">
<span class="sr-only">Your profile</span>
<img class="w-8 h-8 rounded-full bg-gray-50"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt="">
</a> --}}
</div>
<main class="lg:pl-48">

View File

@@ -4,32 +4,22 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://api.fonts.coollabs.io" crossorigin>
<link rel="dns-prefetch" href="https://api.fonts.coollabs.io" />
<link rel="preload" href="https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700;800&display=swap"
as="style" />
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/400.woff2" as="style" />
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/500.woff2" as="style" />
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/600.woff2" as="style" />
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/700.woff2" as="style" />
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/800.woff2" as="style" />
<link href="https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<meta name="robots" content="noindex">
@use('App\Models\InstanceSettings')
@php
$instanceSettings = InstanceSettings::first();
$name = null;
$instanceSettings = InstanceSettings::first();
$name = null;
if($instanceSettings) {
$displayName = $instanceSettings->getTitleDisplayName();
if ($instanceSettings) {
$displayName = $instanceSettings->getTitleDisplayName();
if(strlen($displayName) > 0) {
$name = $displayName . ' ';
if (strlen($displayName) > 0) {
$name = $displayName . ' ';
}
}
}
@endphp
<title>{{ $name }}{{ $title ?? 'Coolify' }}</title>
<title>{{ $name }}{{ $title ?? 'Coolify' }}</title>
@env('local')
<link rel="icon" href="{{ asset('favicon-dev.png') }}" type="image/x-icon" />
@else
@@ -46,13 +36,9 @@
<script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
@endif
@auth
<script src="https://cdnjs.cloudflare.com/ajax/libs/laravel-echo/1.15.3/echo.iife.min.js"
integrity="sha512-aPAh2oRUr3ALz2MwVWkd6lmdgBQC0wSr0R++zclNjXZreT/JrwDPZQwA/p6R3wOCTcXKIHgA9pQGEQBWQmdLaA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/8.3.0/pusher.min.js"
integrity="sha512-tXL5mrkSoP49uQf2jO0LbvzMyFgki//znmq0wYXGq94gVF6TU0QlrSbwGuPpKTeN1mIjReeqKZ4/NJPjHN1d2Q=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
<script type="text/javascript" src="{{ URL::asset('js/pusher.js') }}"></script>
<script type="text/javascript" src="{{ URL::asset('js/apexcharts.js') }}"></script>
@endauth
</head>
@section('body')

View File

@@ -23,7 +23,7 @@
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
@foreach ($projects as $project)
<div class="gap-2 border border-transparent cursor-pointer box group"
onclick="window.location.href = '{{ route('project.resource.index', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => $project->default_environment()->name]) }}'">
onclick="gotoProject('{{ $project->uuid }}','{{ $project->default_environment() }}')">
<div class="flex flex-1 mx-6">
<div class="flex flex-col justify-center flex-1">
<div class="box-title">{{ $project->name }}</div>
@@ -160,7 +160,10 @@
@endif
<script>
function gotoProject(uuid, environment = 'production') {
function gotoProject(uuid, environment) {
if (!environment) {
window.location.href = '/project/' + uuid;
}
window.location.href = '/project/' + uuid + '/' + environment;
}
</script>

View File

@@ -68,9 +68,9 @@
@foreach (data_get($application, 'previews') as $previewName => $preview)
<div class="flex flex-col w-full p-4 border dark:border-coolgray-200">
<div class="flex gap-2">PR #{{ data_get($preview, 'pull_request_id') }} |
@if (Str::of(data_get($preview, 'status'))->startsWith('running'))
@if (str(data_get($preview, 'status'))->startsWith('running'))
<x-status.running :status="data_get($preview, 'status')" />
@elseif(Str::of(data_get($preview, 'status'))->startsWith('restarting'))
@elseif(str(data_get($preview, 'status'))->startsWith('restarting'))
<x-status.restarting :status="data_get($preview, 'status')" />
@else
<x-status.stopped :status="data_get($preview, 'status')" />

View File

@@ -27,7 +27,8 @@
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input placeholder="coollabsio/coolify-example" id="application.git_repository" label="Repository" />
<x-forms.input placeholder="coollabsio/coolify-example" id="application.git_repository"
label="Repository" />
<x-forms.input placeholder="main" id="application.git_branch" label="Branch" />
</div>
<div class="flex items-end gap-2">
@@ -35,14 +36,14 @@
label="Commit SHA" />
</div>
</div>
@if(data_get($application, 'private_key_id'))
@if (data_get($application, 'private_key_id'))
<h3 class="pt-4">Deploy Key</h3>
<div class="py-2 pt-4">Currently attached Private Key: <span
class="dark:text-warning">{{ data_get($application, 'private_key.name') }}</span>
</div>
<h4 class="py-2 ">Select another Private Key</h4>
<div class="flex gap-2">
<div class="flex flex-wrap gap-2">
@foreach ($private_keys as $key)
<x-forms.button wire:click.defer="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
</x-forms.button>

View File

@@ -4,7 +4,7 @@
<x-forms.button type="submit">
Save
</x-forms.button>
@if (Str::of($status)->startsWith('running'))
@if (str($status)->startsWith('running'))
<livewire:project.database.backup-now :backup="$backup" />
@endif
@if ($backup->database_id !== 0)

View File

@@ -12,12 +12,12 @@
<div class="grid gap-2 lg:grid-cols-2">
@forelse ($projects as $project)
<div class="box group" x-data x-on:click="goto('{{ $project->uuid }}')">
<a class="flex flex-col justify-center flex-1 mx-6"
href="{{ route('project.resource.index', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => $project->default_environment()->name]) }}">
<div class="flex flex-col justify-center flex-1 mx-6"
onclick="gotoProject('{{ $project->uuid }}','{{ $project->default_environment() }}')">
<div class="box-title">{{ $project->name }}</div>
<div class="box-description ">
{{ $project->description }}</div>
</a>
</div>
<div class="flex items-center justify-center gap-2 pt-4 pb-2 mr-4 text-xs lg:py-0 lg:justify-normal">
<a class="mx-4 font-bold hover:underline"
href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}">
@@ -36,5 +36,12 @@
function goto(uuid) {
window.location.href = '/project/' + uuid;
}
function gotoProject(uuid, environment) {
if (!environment) {
window.location.href = '/project/' + uuid;
}
window.location.href = '/project/' + uuid + '/' + environment;
}
</script>
</div>

View File

@@ -54,11 +54,11 @@
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-1">
@foreach ($applications as $application)
<div @class([
'border-l border-dashed border-red-500 ' => Str::of(
'border-l border-dashed border-red-500 ' => str(
$application->status)->contains(['exited']),
'border-l border-dashed border-success' => Str::of(
'border-l border-dashed border-success' => str(
$application->status)->contains(['running']),
'border-l border-dashed border-warning' => Str::of(
'border-l border-dashed border-warning' => str(
$application->status)->contains(['starting']),
'flex gap-2 box-without-bg-without-border dark:bg-coolgray-100 bg-white dark:hover:text-neutral-300 group',
])>
@@ -123,12 +123,12 @@
@endforeach
@foreach ($databases as $database)
<div @class([
'border-l border-dashed border-red-500' => Str::of(
$database->status)->contains(['exited']),
'border-l border-dashed border-success' => Str::of(
$database->status)->contains(['running']),
'border-l border-dashed border-warning' => Str::of(
$database->status)->contains(['restarting']),
'border-l border-dashed border-red-500' => str($database->status)->contains(
['exited']),
'border-l border-dashed border-success' => str($database->status)->contains(
['running']),
'border-l border-dashed border-warning' => str($database->status)->contains(
['restarting']),
'flex gap-2 box-without-bg-without-border dark:bg-coolgray-100 bg-white dark:hover:text-neutral-300 group',
])>
<div class="flex flex-row w-full">

View File

@@ -2,7 +2,7 @@
<div>
<div class="flex gap-2">
<h2>Service Stack</h2>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:target='submit' type="submit">Save</x-forms.button>
<x-modal-input buttonTitle="Edit Compose File" title="Edit Docker Compose" :closeOutside="false">
<livewire:project.service.edit-compose serviceId="{{ $service->id }}" />
</x-modal-input>

View File

@@ -119,7 +119,7 @@
</script>
<div>
<h3>Memory (MB)</h3>
<h3>Memory (%)</h3>
<div wire:ignore id="{!! $chartId !!}-memory"></div>
<script>
@@ -176,7 +176,7 @@
}
},
series: [{
name: "Memory (MB)",
name: "Memory (%)",
data: []
}],
noData: {

View File

@@ -4,10 +4,10 @@
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.form :server="$server" />
<livewire:server.delete :server="$server" />
@if ($server->isFunctional() && $server->isMetricsEnabled())
<div class="pt-10">
<livewire:server.charts :server="$server" />
</div>
@endif
<livewire:server.delete :server="$server" />
</div>

View File

@@ -26,7 +26,7 @@
@else
To configure automatic backup for your Coolify instance, you first need to add as a database resource
into Coolify.
<x-forms.button wire:click="add_coolify_database">Add Database</x-forms.button>
<x-forms.button class="mt-2" wire:click="add_coolify_database">Add Database</x-forms.button>
@endif
</div>
<div class="py-4">

View File

@@ -2,12 +2,11 @@
@if (subscriptionProvider() === 'stripe')
<div class="pt-4">
<h2>Your current plan</h2>
<div class="pb-4">Tier: <strong
class="dark:text-warning">
<div class="pb-4">Tier: <strong class="dark:text-warning">
@if (data_get(currentTeam(), 'subscription')->type() == 'dynamic')
Pay-as-you-go
Pay-as-you-go
@else
{{ data_get(currentTeam(), 'subscription')->type() }}
{{ data_get(currentTeam(), 'subscription')->type() }}
@endif
</strong></div>
@@ -50,35 +49,4 @@
target="_blank">contact us.</a>
</div>
@endif
{{-- @if (subscriptionProvider() === 'lemon')
<div>Status: {{ currentTeam()->subscription->lemon_status }}</div>
<div>Type: {{ currentTeam()->subscription->lemon_variant_name }}</div>
@if (currentTeam()->subscription->lemon_status === 'cancelled')
<div class="pb-4">Subscriptions ends at: {{ getRenewDate() }}</div>
<div class="py-4">If you would like to change the subscription to a lower/higher plan, <a
class="underline dark:text-white" href="{{ config('coolify.contact') }}" target="_blank">please
contact
us.</a></div>
@else
<div class="pb-4">Renews at: {{ getRenewDate() }}</div>
@endif
<div class="flex flex-col gap-2">
<div class="flex gap-2">
@if (currentTeam()->subscription->lemon_status === 'cancelled')
<x-forms.button class="bg-coollabs-gradient" wire:click='resume'>Resume Subscription
</x-forms.button>
@else
<x-forms.button wire:click='cancel'>Cancel Subscription</x-forms.button>
@endif
</div>
<div>
<x-forms.button><a class="dark:text-white hover:no-underline" href="{{ getPaymentLink() }}">Update Payment
Details</a>
</x-forms.button>
<a class="dark:text-white hover:no-underline"
href="https://app.lemonsqueezy.com/my-orders"><x-forms.button>Manage My
Subscription</x-forms.button></a>
</div>
</div>
@endif --}}
</div>

View File

@@ -1,67 +0,0 @@
<x-pricing-plans>
@if (config('subscription.provider') === 'stripe')
<x-slot:basic>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic" class="w-full h-10 buyme"
wire:click="subscribeStripe('basic-monthly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic" class="w-full h-10 buyme"
wire:click="subscribeStripe('basic-yearly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button>
</x-slot:basic>
<x-slot:pro>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme"
wire:click="subscribeStripe('pro-monthly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme"
wire:click="subscribeStripe('pro-yearly')"> {{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button>
</x-slot:pro>
<x-slot:ultimate>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-ultimate" class="w-full h-10 buyme"
wire:click="subscribeStripe('ultimate-monthly')">
{{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-ultimate" class="w-full h-10 buyme"
wire:click="subscribeStripe('ultimate-yearly')"> {{ $isTrial ? 'Start Trial' : 'Subscribe' }}
</x-forms.button>
</x-slot:ultimate>
@endif
@if (config('subscription.provider') === 'paddle')
<x-paddle />
@endif
@if (config('subscription.provider') === 'lemon')
<x-slot:basic>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-basic"
class="w-full h-10 buyme" wire:click="getSubscriptionLink('basic-monthly')"> Subscribe
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-basic"
class="w-full h-10 buyme" wire:click="getSubscriptionLink('basic-yearly')"> Subscribe
</x-forms.button>
</x-slot:basic>
<x-slot:pro>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-pro"
class="w-full h-10 buyme" wire:click="getSubscriptionLink('pro-monthly')"> Subscribe
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-pro" class="w-full h-10 buyme"
wire:click="getSubscriptionLink('pro-yearly')"> Subscribe
</x-forms.button>
</x-slot:pro>
<x-slot:ultimate>
<x-forms.button x-show="selected === 'monthly'" x-cloak aria-describedby="tier-ultimate"
class="w-full h-10 buyme" wire:click="getSubscriptionLink('ultimate-monthly')"> Subscribe
</x-forms.button>
<x-forms.button x-show="selected === 'yearly'" x-cloak aria-describedby="tier-ultimate"
class="w-full h-10 buyme" wire:click="getSubscriptionLink('ultimate-yearly')"> Subscribe
</x-forms.button>
</x-slot:ultimate>
@endif
</x-pricing-plans>

View File

@@ -25,7 +25,7 @@
<div>This is the default team. You can't delete it.</div>
@elseif(auth()->user()->teams()->get()->count() === 1 || auth()->user()->currentTeam()->personal_team)
<div>You can't delete your last / personal team.</div>
@elseif(currentTeam()->subscription && currentTeam()->subscription?->lemon_status !== 'cancelled')
@elseif(currentTeam()->subscription)
<div>Please cancel your subscription <a class="underline dark:text-white"
href="{{ route('subscription.show') }}">here</a> before delete this team.</div>
@else

View File

@@ -29,7 +29,7 @@
<template x-teleport="body">
<div x-show="modalOpen"
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen" style="zoom:1.1;"
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen"
x-cloak>
<div x-show="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
@@ -58,23 +58,26 @@
<div class="relative w-auto pb-8">
<p>Are you sure you would like to upgrade your instance to {{ $latestVersion }}?</p>
<br />
<p>You can review the changelogs <a class="font-bold underline"
<p>You can review the changelogs <a class="font-bold underline dark:text-white"
href="https://github.com/coollabsio/coolify/releases" target="_blank">here</a>.</p>
<br />
<p>If something goes wrong and you cannot upgrade your instance, You can check the following <a class="font-bold underline"
href="https://coolify.io/docs/upgrade" target="_blank">guide</a> on what to do.</p>
<p>If something goes wrong and you cannot upgrade your instance, You can check the following
<a class="font-bold underline dark:text-white" href="https://coolify.io/docs/upgrade"
target="_blank">guide</a> on what to do.
</p>
@if ($showProgress)
<div class="flex flex-col pt-4">
<h4>Progress <x-loading /></h4>
<h2>Progress <x-loading /></h2>
<div x-html="currentStatus"></div>
</div>
@endif
</div>
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
<div class="flex gap-4">
@if (!$showProgress)
<x-forms.button @click="modalOpen=false"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel
</x-forms.button>
<div class="flex-1"></div>
<x-forms.button @click="confirmed" class="w-24" isHighlighted type="button">Continue
</x-forms.button>
@endif
@@ -96,6 +99,10 @@
this.$wire.$call('upgrade')
this.upgrade();
this.$wire.showProgress = true;
window.addEventListener('beforeunload', (event) => {
event.preventDefault();
event.returnValue = '';
});
},
revive() {
if (checkHealthInterval) return true;

View File

@@ -1,62 +0,0 @@
<?php
use App\Actions\CoolifyTask\RunRemoteProcess;
use App\Actions\CoolifyTask\TidyOutput;
use App\Models\Server;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
uses(DatabaseMigrations::class);
beforeEach(function () {
$this->seed(DatabaseSeeder::class);
});
it('starts a docker container correctly', function () {
test()->actingAs(User::factory([
'uuid' => Str::uuid(),
'email' => Str::uuid() . '@example.com',
])->create());
$coolifyNamePrefix = 'coolify_test_';
$format = '{"ID":"{{ .ID }}", "Image": "{{ .Image }}", "Names":"{{ .Names }}"}';
$areThereCoolifyTestContainers = "docker ps --filter=\"name={$coolifyNamePrefix}*\" --format '{$format}' ";
// Generate a known name
$containerName = 'coolify_test_' . now()->format('Ymd_his');
$host = Server::where('name', 'testing-local-docker-container')->first();
remote_process([
"docker rm -f $(docker ps --filter='name={$coolifyNamePrefix}*' -aq) > /dev/null 2>&1"
], $host);
// Assert there's no containers start with coolify_test_*
$activity = remote_process([$areThereCoolifyTestContainers], $host);
$tidyOutput = RunRemoteProcess::decodeOutput($activity);
$containers = format_docker_command_output_to_json($tidyOutput);
expect($containers)->toBeEmpty();
// start a container nginx -d --name = $containerName
$activity = remote_process(["docker run -d --rm --name {$containerName} nginx"], $host);
expect($activity->getExtraProperty('exitCode'))->toBe(0);
// docker ps name = $container
$activity = remote_process([$areThereCoolifyTestContainers], $host);
$tidyOutput = RunRemoteProcess::decodeOutput($activity);
$containers = format_docker_command_output_to_json($tidyOutput);
expect($containers->where('Names', $containerName)->count())->toBe(1);
// Stop testing containers
$activity = remote_process([
"docker ps --filter='name={$coolifyNamePrefix}*' -aq && " .
"docker rm -f $(docker ps --filter='name={$coolifyNamePrefix}*' -aq)"
], $host);
expect($activity->getExtraProperty('exitCode'))->toBe(0);
});

View File

@@ -1,32 +0,0 @@
<?php
use App\Actions\CoolifyTask\RunRemoteProcess;
use App\Models\Server;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
uses(DatabaseMigrations::class);
beforeEach(function () {
$this->seed(DatabaseSeeder::class);
});
it('outputs correctly', function () {
$host = Server::where('name', 'testing-local-docker-container')->first();
$activity = remote_process([
'pwd',
'x=1; while [ $x -le 3 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done',
], $host);
$tidyOutput = RunRemoteProcess::decodeOutput($activity);
expect($tidyOutput)
->toContain('Welcome 1 times')
->toContain('Welcome 3 times')
->not()->toBeJson();
});

View File

@@ -1,7 +1,7 @@
{
"coolify": {
"v4": {
"version": "4.0.0-beta.298"
"version": "4.0.0-beta.306"
}
}
}