Compare commits

...

38 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
90 changed files with 471 additions and 719 deletions

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()->get()->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

@@ -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.300',
'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.300';
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."
}

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

@@ -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

@@ -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

@@ -58,25 +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"
<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
@@ -98,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.300"
"version": "4.0.0-beta.306"
}
}
}
}