@@ -19,11 +19,7 @@ DB_PORT=5432
|
|||||||
# Set to true to enable Ray
|
# Set to true to enable Ray
|
||||||
RAY_ENABLED=false
|
RAY_ENABLED=false
|
||||||
# Set custom ray port
|
# Set custom ray port
|
||||||
RAY_PORT=
|
# RAY_PORT=
|
||||||
|
|
||||||
# Clockwork Configuration (remove?)
|
|
||||||
CLOCKWORK_ENABLED=false
|
|
||||||
CLOCKWORK_QUEUE_COLLECT=true
|
|
||||||
|
|
||||||
# Enable Laravel Telescope for debugging
|
# Enable Laravel Telescope for debugging
|
||||||
TELESCOPE_ENABLED=false
|
TELESCOPE_ENABLED=false
|
||||||
|
|||||||
14
.github/pull_request_template.md
vendored
14
.github/pull_request_template.md
vendored
@@ -1 +1,13 @@
|
|||||||
> Always use `next` branch as destination branch for PRs, not `main`
|
## Submit Checklist (REMOVE THIS SECTION BEFORE SUBMITTING)
|
||||||
|
- [ ] I have selected the `next` branch as the destination for my PR, not `main`.
|
||||||
|
- [ ] I have listed all changes in the `Changes` section.
|
||||||
|
- [ ] I have filled out the `Issues` section with the issue/discussion link(s) (if applicable).
|
||||||
|
- [ ] I have tested my changes.
|
||||||
|
- [ ] I have considered backwards compatibility.
|
||||||
|
- [ ] I have removed this checklist and any unused sections.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
-
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
- fix #
|
||||||
|
|||||||
@@ -6,15 +6,19 @@ You can ask for guidance anytime on our [Discord server](https://coollabs.io/dis
|
|||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [Setup Development Environment](#1-setup-development-environment)
|
- [Contributing to Coolify](#contributing-to-coolify)
|
||||||
2. [Verify Installation](#2-verify-installation-optional)
|
- [Table of Contents](#table-of-contents)
|
||||||
3. [Fork and Setup Local Repository](#3-fork-and-setup-local-repository)
|
- [1. Setup Development Environment](#1-setup-development-environment)
|
||||||
4. [Set up Environment Variables](#4-set-up-environment-variables)
|
- [2. Verify Installation (Optional)](#2-verify-installation-optional)
|
||||||
5. [Start Coolify](#5-start-coolify)
|
- [3. Fork and Setup Local Repository](#3-fork-and-setup-local-repository)
|
||||||
6. [Start Development](#6-start-development)
|
- [4. Set up Environment Variables](#4-set-up-environment-variables)
|
||||||
7. [Development Notes](#7-development-notes)
|
- [5. Start Coolify](#5-start-coolify)
|
||||||
8. [Create a Pull Request](#8-create-a-pull-request)
|
- [6. Start Development](#6-start-development)
|
||||||
9. [Additional Contribution Guidelines](#additional-contribution-guidelines)
|
- [7. Development Notes](#7-development-notes)
|
||||||
|
- [8. Create a Pull Request](#8-create-a-pull-request)
|
||||||
|
- [Additional Contribution Guidelines](#additional-contribution-guidelines)
|
||||||
|
- [Contributing a New Service](#contributing-a-new-service)
|
||||||
|
- [Contributing to Documentation](#contributing-to-documentation)
|
||||||
|
|
||||||
## 1. Setup Development Environment
|
## 1. Setup Development Environment
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Actions\Application;
|
namespace App\Actions\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use App\Actions\Server\CleanupDocker;
|
use App\Actions\Server\CleanupDocker;
|
||||||
|
use App\Models\Application;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopApplication
|
class StopApplication
|
||||||
@@ -21,6 +21,7 @@ class StopApplication
|
|||||||
|
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$application->uuid}"], $server);
|
instant_remote_process(["docker stack rm {$application->uuid}"], $server);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,10 +33,11 @@ class StopApplication
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
CleanupDocker::run($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ namespace App\Actions\CoolifyTask;
|
|||||||
|
|
||||||
use App\Enums\ActivityTypes;
|
use App\Enums\ActivityTypes;
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
|
use App\Helpers\SshMultiplexingHelper;
|
||||||
use App\Jobs\ApplicationDeploymentJob;
|
use App\Jobs\ApplicationDeploymentJob;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Process\ProcessResult;
|
use Illuminate\Process\ProcessResult;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
use App\Helpers\SshMultiplexingHelper;
|
|
||||||
|
|
||||||
class RunRemoteProcess
|
class RunRemoteProcess
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ class StartPostgresql
|
|||||||
$this->generate_init_scripts();
|
$this->generate_init_scripts();
|
||||||
$this->add_custom_conf();
|
$this->add_custom_conf();
|
||||||
|
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class StopDatabase
|
|||||||
$this->stopContainer($database, $database->uuid, 300);
|
$this->stopContainer($database, $database->uuid, 300);
|
||||||
if (! $isDeleteOperation) {
|
if (! $isDeleteOperation) {
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
CleanupDocker::run($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ class GetContainersStatus
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$exitedServices = $exitedServices->unique('id');
|
$exitedServices = $exitedServices->unique('uuid');
|
||||||
foreach ($exitedServices as $exitedService) {
|
foreach ($exitedServices as $exitedService) {
|
||||||
if (str($exitedService->status)->startsWith('exited')) {
|
if (str($exitedService->status)->startsWith('exited')) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ class StartProxy
|
|||||||
"echo 'Pulling docker image.'",
|
"echo 'Pulling docker image.'",
|
||||||
'docker compose pull',
|
'docker compose pull',
|
||||||
"echo 'Stopping existing coolify-proxy.'",
|
"echo 'Stopping existing coolify-proxy.'",
|
||||||
'docker compose down -v --remove-orphans > /dev/null 2>&1',
|
'docker stop -t 10 coolify-proxy || true',
|
||||||
|
'docker rm coolify-proxy || true',
|
||||||
"echo 'Starting coolify-proxy.'",
|
"echo 'Starting coolify-proxy.'",
|
||||||
'docker compose up -d --remove-orphans',
|
'docker compose up -d --remove-orphans',
|
||||||
"echo 'Proxy started successfully.'",
|
"echo 'Proxy started successfully.'",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use App\Events\CloudflareTunnelConfigured;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
@@ -40,12 +41,17 @@ class ConfigureCloudflared
|
|||||||
instant_remote_process($commands, $server);
|
instant_remote_process($commands, $server);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
|
$server->settings->is_cloudflare_tunnel = false;
|
||||||
|
$server->settings->save();
|
||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
|
CloudflareTunnelConfigured::dispatch($server->team_id);
|
||||||
|
|
||||||
$commands = collect([
|
$commands = collect([
|
||||||
'rm -fr /tmp/cloudflared',
|
'rm -fr /tmp/cloudflared',
|
||||||
]);
|
]);
|
||||||
instant_remote_process($commands, $server);
|
instant_remote_process($commands, $server);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use App\Actions\Server\CleanupDocker;
|
use App\Actions\Server\CleanupDocker;
|
||||||
|
use App\Models\Service;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class DeleteService
|
class DeleteService
|
||||||
@@ -70,7 +70,7 @@ class DeleteService
|
|||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
CleanupDocker::run($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use App\Actions\Server\CleanupDocker;
|
use App\Actions\Server\CleanupDocker;
|
||||||
|
use App\Models\Service;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopService
|
class StopService
|
||||||
@@ -24,11 +24,12 @@ class StopService
|
|||||||
if (! $isDeleteOperation) {
|
if (! $isDeleteOperation) {
|
||||||
$service->delete_connected_networks($service->uuid);
|
$service->delete_connected_networks($service->uuid);
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
CleanupDocker::run($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Redis;
|
|
||||||
|
|
||||||
class CleanupQueue extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'cleanup:queue';
|
|
||||||
|
|
||||||
protected $description = 'Cleanup Queue';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
echo "Running queue cleanup...\n";
|
|
||||||
$prefix = config('database.redis.options.prefix');
|
|
||||||
$keys = Redis::connection()->keys('*:laravel*');
|
|
||||||
foreach ($keys as $key) {
|
|
||||||
$keyWithoutPrefix = str_replace($prefix, '', $key);
|
|
||||||
Redis::connection()->del($keyWithoutPrefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
31
app/Console/Commands/CleanupRedis.php
Normal file
31
app/Console/Commands/CleanupRedis.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
|
||||||
|
class CleanupRedis extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:redis';
|
||||||
|
|
||||||
|
protected $description = 'Cleanup Redis';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
echo "Cleanup Redis keys.\n";
|
||||||
|
$prefix = config('database.redis.options.prefix');
|
||||||
|
|
||||||
|
$keys = Redis::connection()->keys('*:laravel*');
|
||||||
|
collect($keys)->each(function ($key) use ($prefix) {
|
||||||
|
$keyWithoutPrefix = str_replace($prefix, '', $key);
|
||||||
|
Redis::connection()->del($keyWithoutPrefix);
|
||||||
|
});
|
||||||
|
|
||||||
|
$queueOverlaps = Redis::connection()->keys('*laravel-queue-overlap*');
|
||||||
|
collect($queueOverlaps)->each(function ($key) {
|
||||||
|
Redis::connection()->del($key);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
@@ -35,6 +37,16 @@ class CleanupStuckedResources extends Command
|
|||||||
private function cleanup_stucked_resources()
|
private function cleanup_stucked_resources()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
$servers = Server::all()->filter(function ($server) {
|
||||||
|
return $server->isFunctional();
|
||||||
|
});
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
CleanupHelperContainersJob::dispatch($server);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ namespace App\Console\Commands;
|
|||||||
use App\Actions\Server\StopSentinel;
|
use App\Actions\Server\StopSentinel;
|
||||||
use App\Enums\ActivityTypes;
|
use App\Enums\ActivityTypes;
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
@@ -18,7 +17,7 @@ use Illuminate\Support\Facades\Http;
|
|||||||
|
|
||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments} {--cleanup-proxy-networks}';
|
protected $signature = 'app:init {--force-cloud}';
|
||||||
|
|
||||||
protected $description = 'Cleanup instance related stuffs';
|
protected $description = 'Cleanup instance related stuffs';
|
||||||
|
|
||||||
@@ -26,9 +25,63 @@ class Init extends Command
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
if (isCloud() && ! $this->option('force-cloud')) {
|
||||||
|
echo "Skipping init as we are on cloud and --force-cloud option is not set\n";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->servers = Server::all();
|
$this->servers = Server::all();
|
||||||
$this->alive();
|
if (isCloud()) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->send_alive_signal();
|
||||||
get_public_ips();
|
get_public_ips();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility
|
||||||
|
$this->disable_metrics();
|
||||||
|
$this->replace_slash_in_environment_name();
|
||||||
|
$this->restore_coolify_db_backup();
|
||||||
|
//
|
||||||
|
$this->update_traefik_labels();
|
||||||
|
if (! isCloud() || $this->option('force-cloud')) {
|
||||||
|
$this->cleanup_unused_network_from_coolify_proxy();
|
||||||
|
}
|
||||||
|
if (isCloud()) {
|
||||||
|
$this->cleanup_unnecessary_dynamic_proxy_configuration();
|
||||||
|
} else {
|
||||||
|
$this->cleanup_in_progress_application_deployments();
|
||||||
|
}
|
||||||
|
$this->call('cleanup:redis');
|
||||||
|
$this->call('cleanup:stucked-resources');
|
||||||
|
|
||||||
|
if (isCloud()) {
|
||||||
|
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
|
||||||
|
if ($response->successful()) {
|
||||||
|
$services = $response->json();
|
||||||
|
File::put(base_path('templates/service-templates.json'), json_encode($services));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$localhost = $this->servers->where('id', 0)->first();
|
||||||
|
$localhost->setupDynamicProxyConfiguration();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if (! is_null(env('AUTOUPDATE', null))) {
|
||||||
|
if (env('AUTOUPDATE') == true) {
|
||||||
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
|
} else {
|
||||||
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function disable_metrics()
|
||||||
|
{
|
||||||
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
if ($server->settings->is_metrics_enabled === true) {
|
if ($server->settings->is_metrics_enabled === true) {
|
||||||
@@ -39,62 +92,6 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$full_cleanup = $this->option('full-cleanup');
|
|
||||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
|
||||||
$cleanup_proxy_networks = $this->option('cleanup-proxy-networks');
|
|
||||||
$this->replace_slash_in_environment_name();
|
|
||||||
if ($cleanup_deployments) {
|
|
||||||
echo "Running cleanup deployments.\n";
|
|
||||||
$this->cleanup_in_progress_application_deployments();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($cleanup_proxy_networks) {
|
|
||||||
echo "Running cleanup proxy networks.\n";
|
|
||||||
$this->cleanup_unused_network_from_coolify_proxy();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($full_cleanup) {
|
|
||||||
// Required for falsely deleted coolify db
|
|
||||||
$this->restore_coolify_db_backup();
|
|
||||||
$this->update_traefik_labels();
|
|
||||||
$this->cleanup_unused_network_from_coolify_proxy();
|
|
||||||
$this->cleanup_unnecessary_dynamic_proxy_configuration();
|
|
||||||
$this->cleanup_in_progress_application_deployments();
|
|
||||||
$this->cleanup_stucked_helper_containers();
|
|
||||||
$this->call('cleanup:queue');
|
|
||||||
$this->call('cleanup:stucked-resources');
|
|
||||||
if (! isCloud()) {
|
|
||||||
try {
|
|
||||||
$localhost = $this->servers->where('id', 0)->first();
|
|
||||||
$localhost->setupDynamicProxyConfiguration();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = InstanceSettings::get();
|
|
||||||
if (! is_null(env('AUTOUPDATE', null))) {
|
|
||||||
if (env('AUTOUPDATE') == true) {
|
|
||||||
$settings->update(['is_auto_update_enabled' => true]);
|
|
||||||
} else {
|
|
||||||
$settings->update(['is_auto_update_enabled' => false]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isCloud()) {
|
|
||||||
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
|
|
||||||
if ($response->successful()) {
|
|
||||||
$services = $response->json();
|
|
||||||
File::put(base_path('templates/service-templates.json'), json_encode($services));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->cleanup_stucked_helper_containers();
|
|
||||||
$this->call('cleanup:stucked-resources');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function update_traefik_labels()
|
private function update_traefik_labels()
|
||||||
@@ -108,7 +105,6 @@ class Init extends Command
|
|||||||
|
|
||||||
private function cleanup_unnecessary_dynamic_proxy_configuration()
|
private function cleanup_unnecessary_dynamic_proxy_configuration()
|
||||||
{
|
{
|
||||||
if (isCloud()) {
|
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
try {
|
try {
|
||||||
if (! $server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
@@ -128,13 +124,9 @@ class Init extends Command
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function cleanup_unused_network_from_coolify_proxy()
|
private function cleanup_unused_network_from_coolify_proxy()
|
||||||
{
|
{
|
||||||
if (isCloud()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
if (! $server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -175,6 +167,7 @@ class Init extends Command
|
|||||||
|
|
||||||
private function restore_coolify_db_backup()
|
private function restore_coolify_db_backup()
|
||||||
{
|
{
|
||||||
|
if (version_compare('4.0.0-beta.179', config('version'), '<=')) {
|
||||||
try {
|
try {
|
||||||
$database = StandalonePostgresql::withTrashed()->find(0);
|
$database = StandalonePostgresql::withTrashed()->find(0);
|
||||||
if ($database && $database->trashed()) {
|
if ($database && $database->trashed()) {
|
||||||
@@ -197,17 +190,9 @@ class Init extends Command
|
|||||||
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup_stucked_helper_containers()
|
|
||||||
{
|
|
||||||
foreach ($this->servers as $server) {
|
|
||||||
if ($server->isFunctional()) {
|
|
||||||
CleanupHelperContainersJob::dispatch($server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function alive()
|
private function send_alive_signal()
|
||||||
{
|
{
|
||||||
$id = config('app.id');
|
$id = config('app.id');
|
||||||
$version = config('version');
|
$version = config('version');
|
||||||
@@ -225,23 +210,7 @@ class Init extends Command
|
|||||||
echo "Error in alive: {$e->getMessage()}\n";
|
echo "Error in alive: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// private function cleanup_ssh()
|
|
||||||
// {
|
|
||||||
|
|
||||||
// TODO: it will cleanup id.root@host.docker.internal
|
|
||||||
// try {
|
|
||||||
// $files = Storage::allFiles('ssh/keys');
|
|
||||||
// foreach ($files as $file) {
|
|
||||||
// Storage::delete($file);
|
|
||||||
// }
|
|
||||||
// $files = Storage::allFiles('ssh/mux');
|
|
||||||
// foreach ($files as $file) {
|
|
||||||
// Storage::delete($file);
|
|
||||||
// }
|
|
||||||
// } catch (\Throwable $e) {
|
|
||||||
// echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
private function cleanup_in_progress_application_deployments()
|
private function cleanup_in_progress_application_deployments()
|
||||||
{
|
{
|
||||||
// Cleanup any failed deployments
|
// Cleanup any failed deployments
|
||||||
@@ -263,6 +232,7 @@ class Init extends Command
|
|||||||
|
|
||||||
private function replace_slash_in_environment_name()
|
private function replace_slash_in_environment_name()
|
||||||
{
|
{
|
||||||
|
if (version_compare('4.0.0-beta.298', config('version'), '<=')) {
|
||||||
$environments = Environment::all();
|
$environments = Environment::all();
|
||||||
foreach ($environments as $environment) {
|
foreach ($environments as $environment) {
|
||||||
if (str_contains($environment->name, '/')) {
|
if (str_contains($environment->name, '/')) {
|
||||||
@@ -272,3 +242,4 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
34
app/Events/CloudflareTunnelConfigured.php
Normal file
34
app/Events/CloudflareTunnelConfigured.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CloudflareTunnelConfigured implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public $teamId;
|
||||||
|
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception('Team id is null');
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,28 +24,24 @@ class SshMultiplexingHelper
|
|||||||
public static function ensureMultiplexedConnection(Server $server)
|
public static function ensureMultiplexedConnection(Server $server)
|
||||||
{
|
{
|
||||||
if (! self::isMultiplexingEnabled()) {
|
if (! self::isMultiplexingEnabled()) {
|
||||||
// ray('SSH Multiplexing: DISABLED')->red();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ray('SSH Multiplexing: ENABLED')->green();
|
|
||||||
// ray('Ensuring multiplexed connection for server:', $server);
|
|
||||||
|
|
||||||
$sshConfig = self::serverSshConfiguration($server);
|
$sshConfig = self::serverSshConfiguration($server);
|
||||||
$muxSocket = $sshConfig['muxFilename'];
|
$muxSocket = $sshConfig['muxFilename'];
|
||||||
$sshKeyLocation = $sshConfig['sshKeyLocation'];
|
$sshKeyLocation = $sshConfig['sshKeyLocation'];
|
||||||
|
|
||||||
self::validateSshKey($sshKeyLocation);
|
self::validateSshKey($sshKeyLocation);
|
||||||
|
|
||||||
$checkCommand = "ssh -O check -o ControlPath=$muxSocket {$server->user}@{$server->ip}";
|
$checkCommand = "ssh -O check -o ControlPath=$muxSocket ";
|
||||||
|
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||||
|
$checkCommand .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
|
||||||
|
}
|
||||||
|
$checkCommand .= "{$server->user}@{$server->ip}";
|
||||||
$process = Process::run($checkCommand);
|
$process = Process::run($checkCommand);
|
||||||
|
|
||||||
if ($process->exitCode() !== 0) {
|
if ($process->exitCode() !== 0) {
|
||||||
// ray('SSH Multiplexing: Existing connection check failed or not found')->orange();
|
|
||||||
// ray('Establishing new connection');
|
|
||||||
self::establishNewMultiplexedConnection($server);
|
self::establishNewMultiplexedConnection($server);
|
||||||
} else {
|
|
||||||
// ray('SSH Multiplexing: Existing connection is valid')->green();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,37 +51,24 @@ class SshMultiplexingHelper
|
|||||||
$sshKeyLocation = $sshConfig['sshKeyLocation'];
|
$sshKeyLocation = $sshConfig['sshKeyLocation'];
|
||||||
$muxSocket = $sshConfig['muxFilename'];
|
$muxSocket = $sshConfig['muxFilename'];
|
||||||
|
|
||||||
// ray('Establishing new multiplexed connection')->blue();
|
|
||||||
// ray('SSH Key Location:', $sshKeyLocation);
|
|
||||||
// ray('Mux Socket:', $muxSocket);
|
|
||||||
|
|
||||||
$connectionTimeout = config('constants.ssh.connection_timeout');
|
$connectionTimeout = config('constants.ssh.connection_timeout');
|
||||||
$serverInterval = config('constants.ssh.server_interval');
|
$serverInterval = config('constants.ssh.server_interval');
|
||||||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||||
|
|
||||||
$establishCommand = "ssh -fNM -o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} "
|
$establishCommand = "ssh -fNM -o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
||||||
.self::getCommonSshOptions($server, $sshKeyLocation, $connectionTimeout, $serverInterval)
|
|
||||||
."{$server->user}@{$server->ip}";
|
|
||||||
|
|
||||||
// ray('Establish Command:', $establishCommand);
|
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||||
|
$establishCommand .= ' -o ProxyCommand="cloudflared access ssh --hostname %h" ';
|
||||||
|
}
|
||||||
|
|
||||||
|
$establishCommand .= self::getCommonSshOptions($server, $sshKeyLocation, $connectionTimeout, $serverInterval);
|
||||||
|
$establishCommand .= "{$server->user}@{$server->ip}";
|
||||||
|
|
||||||
$establishProcess = Process::run($establishCommand);
|
$establishProcess = Process::run($establishCommand);
|
||||||
|
|
||||||
// ray('Establish Process Exit Code:', $establishProcess->exitCode());
|
|
||||||
// ray('Establish Process Output:', $establishProcess->output());
|
|
||||||
// ray('Establish Process Error Output:', $establishProcess->errorOutput());
|
|
||||||
|
|
||||||
if ($establishProcess->exitCode() !== 0) {
|
if ($establishProcess->exitCode() !== 0) {
|
||||||
// ray('Failed to establish multiplexed connection')->red();
|
|
||||||
throw new \RuntimeException('Failed to establish multiplexed connection: '.$establishProcess->errorOutput());
|
throw new \RuntimeException('Failed to establish multiplexed connection: '.$establishProcess->errorOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ray('Successfully established multiplexed connection')->green();
|
|
||||||
|
|
||||||
// Check if the mux socket file was created
|
|
||||||
if (! file_exists($muxSocket)) {
|
|
||||||
// ray('Mux socket file not found after connection establishment')->orange();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function removeMuxFile(Server $server)
|
public static function removeMuxFile(Server $server)
|
||||||
@@ -93,20 +76,12 @@ class SshMultiplexingHelper
|
|||||||
$sshConfig = self::serverSshConfiguration($server);
|
$sshConfig = self::serverSshConfiguration($server);
|
||||||
$muxSocket = $sshConfig['muxFilename'];
|
$muxSocket = $sshConfig['muxFilename'];
|
||||||
|
|
||||||
$closeCommand = "ssh -O exit -o ControlPath=$muxSocket {$server->user}@{$server->ip}";
|
$closeCommand = "ssh -O exit -o ControlPath=$muxSocket ";
|
||||||
$process = Process::run($closeCommand);
|
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||||
|
$closeCommand .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
|
||||||
// ray('Closing multiplexed connection')->blue();
|
|
||||||
// ray('Close command:', $closeCommand);
|
|
||||||
// ray('Close process exit code:', $process->exitCode());
|
|
||||||
// ray('Close process output:', $process->output());
|
|
||||||
// ray('Close process error output:', $process->errorOutput());
|
|
||||||
|
|
||||||
if ($process->exitCode() !== 0) {
|
|
||||||
// ray('Failed to close multiplexed connection')->orange();
|
|
||||||
} else {
|
|
||||||
// ray('Successfully closed multiplexed connection')->green();
|
|
||||||
}
|
}
|
||||||
|
$closeCommand .= "{$server->user}@{$server->ip}";
|
||||||
|
Process::run($closeCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateScpCommand(Server $server, string $source, string $dest)
|
public static function generateScpCommand(Server $server, string $source, string $dest)
|
||||||
@@ -116,16 +91,18 @@ class SshMultiplexingHelper
|
|||||||
$muxSocket = $sshConfig['muxFilename'];
|
$muxSocket = $sshConfig['muxFilename'];
|
||||||
|
|
||||||
$timeout = config('constants.ssh.command_timeout');
|
$timeout = config('constants.ssh.command_timeout');
|
||||||
|
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||||
|
|
||||||
$scp_command = "timeout $timeout scp ";
|
$scp_command = "timeout $timeout scp ";
|
||||||
|
|
||||||
if (self::isMultiplexingEnabled()) {
|
if (self::isMultiplexingEnabled()) {
|
||||||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
|
||||||
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
||||||
self::ensureMultiplexedConnection($server);
|
self::ensureMultiplexedConnection($server);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::addCloudflareProxyCommand($scp_command, $server);
|
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||||
|
$scp_command .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
|
||||||
|
}
|
||||||
|
|
||||||
$scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true);
|
$scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true);
|
||||||
$scp_command .= "{$source} {$server->user}@{$server->ip}:{$dest}";
|
$scp_command .= "{$source} {$server->user}@{$server->ip}:{$dest}";
|
||||||
@@ -144,16 +121,18 @@ class SshMultiplexingHelper
|
|||||||
$muxSocket = $sshConfig['muxFilename'];
|
$muxSocket = $sshConfig['muxFilename'];
|
||||||
|
|
||||||
$timeout = config('constants.ssh.command_timeout');
|
$timeout = config('constants.ssh.command_timeout');
|
||||||
|
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||||
|
|
||||||
$ssh_command = "timeout $timeout ssh ";
|
$ssh_command = "timeout $timeout ssh ";
|
||||||
|
|
||||||
if (self::isMultiplexingEnabled()) {
|
if (self::isMultiplexingEnabled()) {
|
||||||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
|
||||||
$ssh_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
$ssh_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
||||||
self::ensureMultiplexedConnection($server);
|
self::ensureMultiplexedConnection($server);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::addCloudflareProxyCommand($ssh_command, $server);
|
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||||
|
$ssh_command .= "-o ProxyCommand='cloudflared access ssh --hostname %h' ";
|
||||||
|
}
|
||||||
|
|
||||||
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
|
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
|
||||||
|
|
||||||
@@ -183,13 +162,6 @@ class SshMultiplexingHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function addCloudflareProxyCommand(string &$command, Server $server): void
|
|
||||||
{
|
|
||||||
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
|
||||||
$command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function getCommonSshOptions(Server $server, string $sshKeyLocation, int $connectionTimeout, int $serverInterval, bool $isScp = false): string
|
private static function getCommonSshOptions(Server $server, string $sshKeyLocation, int $connectionTimeout, int $serverInterval, bool $isScp = false): string
|
||||||
{
|
{
|
||||||
$options = "-i {$sshKeyLocation} "
|
$options = "-i {$sshKeyLocation} "
|
||||||
|
|||||||
@@ -2529,6 +2529,131 @@ class ApplicationsController extends Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[OA\Post(
|
||||||
|
summary: 'Execute Command',
|
||||||
|
description: "Execute a command on the application's current container.",
|
||||||
|
path: '/applications/{uuid}/execute',
|
||||||
|
operationId: 'execute-command-application',
|
||||||
|
security: [
|
||||||
|
['bearerAuth' => []],
|
||||||
|
],
|
||||||
|
tags: ['Applications'],
|
||||||
|
parameters: [
|
||||||
|
new OA\Parameter(
|
||||||
|
name: 'uuid',
|
||||||
|
in: 'path',
|
||||||
|
description: 'UUID of the application.',
|
||||||
|
required: true,
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'string',
|
||||||
|
format: 'uuid',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
requestBody: new OA\RequestBody(
|
||||||
|
required: true,
|
||||||
|
description: 'Command to execute.',
|
||||||
|
content: new OA\MediaType(
|
||||||
|
mediaType: 'application/json',
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'object',
|
||||||
|
properties: [
|
||||||
|
'command' => ['type' => 'string', 'description' => 'Command to execute.'],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
responses: [
|
||||||
|
new OA\Response(
|
||||||
|
response: 200,
|
||||||
|
description: "Execute a command on the application's current container.",
|
||||||
|
content: [
|
||||||
|
new OA\MediaType(
|
||||||
|
mediaType: 'application/json',
|
||||||
|
schema: new OA\Schema(
|
||||||
|
type: 'object',
|
||||||
|
properties: [
|
||||||
|
'message' => ['type' => 'string', 'example' => 'Command executed.'],
|
||||||
|
'response' => ['type' => 'string'],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
new OA\Response(
|
||||||
|
response: 401,
|
||||||
|
ref: '#/components/responses/401',
|
||||||
|
),
|
||||||
|
new OA\Response(
|
||||||
|
response: 400,
|
||||||
|
ref: '#/components/responses/400',
|
||||||
|
),
|
||||||
|
new OA\Response(
|
||||||
|
response: 404,
|
||||||
|
ref: '#/components/responses/404',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)]
|
||||||
|
public function execute_command_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
// TODO: Need to review this from security perspective, to not allow arbitrary command execution
|
||||||
|
$allowedFields = ['command'];
|
||||||
|
$teamId = getTeamIdFromToken();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalidTokenResponse();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['message' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['message' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
$return = validateIncomingRequest($request);
|
||||||
|
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
$validator = customApiValidator($request->all(), [
|
||||||
|
'command' => 'string|required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||||
|
if ($validator->fails() || ! empty($extraFields)) {
|
||||||
|
$errors = $validator->errors();
|
||||||
|
if (! empty($extraFields)) {
|
||||||
|
foreach ($extraFields as $field) {
|
||||||
|
$errors->add($field, 'This field is not allowed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Validation failed.',
|
||||||
|
'errors' => $errors,
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$container = getCurrentApplicationContainerStatus($application->destination->server, $application->id)->firstOrFail();
|
||||||
|
$status = getContainerStatus($application->destination->server, $container['Names']);
|
||||||
|
|
||||||
|
if ($status !== 'running') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Application is not running.',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$commands = collect([
|
||||||
|
executeInDocker($container['Names'], $request->command),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$res = instant_remote_process(command: $commands, server: $application->destination->server);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Command executed.',
|
||||||
|
'response' => $res,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
private function validateDataApplications(Request $request, Server $server)
|
private function validateDataApplications(Request $request, Server $server)
|
||||||
{
|
{
|
||||||
$teamId = getTeamIdFromToken();
|
$teamId = getTeamIdFromToken();
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Sleep;
|
use Illuminate\Support\Sleep;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
|
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@@ -10,7 +11,6 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
class CleanupStaleMultiplexedConnections implements ShouldQueue
|
class CleanupStaleMultiplexedConnections implements ShouldQueue
|
||||||
{
|
{
|
||||||
@@ -32,6 +32,7 @@ class CleanupStaleMultiplexedConnections implements ShouldQueue
|
|||||||
|
|
||||||
if (! $server) {
|
if (! $server) {
|
||||||
$this->removeMultiplexFile($muxFile);
|
$this->removeMultiplexFile($muxFile);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
@@ -25,16 +24,6 @@ class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [(new WithoutOverlapping($this->server->uuid))];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->server->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
GetContainersStatus::run($this->server);
|
GetContainersStatus::run($this->server);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
@@ -80,16 +79,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [new WithoutOverlapping($this->backup->id)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->backup->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
|
||||||
use App\Models\Team;
|
|
||||||
use App\Notifications\Database\DailyBackup;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public $tries = 1;
|
|
||||||
|
|
||||||
public function __construct() {}
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
// $teams = Team::all();
|
|
||||||
// foreach ($teams as $team) {
|
|
||||||
// $scheduled_backups = $team->scheduledDatabaseBackups()->get();
|
|
||||||
// if ($scheduled_backups->isEmpty()) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// foreach ($scheduled_backups as $scheduled_backup) {
|
|
||||||
// $last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
|
||||||
// if ($last_days_backups->isEmpty()) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// $failed = $last_days_backups->where('status', 'failed');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $scheduled_backups = ScheduledDatabaseBackup::all();
|
|
||||||
// $databases = collect();
|
|
||||||
// $teams = collect();
|
|
||||||
// foreach ($scheduled_backups as $scheduled_backup) {
|
|
||||||
// $last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
|
||||||
// if ($last_days_backups->isEmpty()) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// $failed = $last_days_backups->where('status', 'failed');
|
|
||||||
// $database = $scheduled_backup->database;
|
|
||||||
// $team = $database->team();
|
|
||||||
// $teams->put($team->id, $team);
|
|
||||||
// $databases->put("{$team->id}:{$database->name}", [
|
|
||||||
// 'failed_count' => $failed->count(),
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
// foreach ($databases as $name => $database) {
|
|
||||||
// [$team_id, $name] = explode(':', $name);
|
|
||||||
// $team = $teams->get($team_id);
|
|
||||||
// $team?->notify(new DailyBackup($databases));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -80,7 +80,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|| $this->resource instanceof StandaloneClickhouse;
|
|| $this->resource instanceof StandaloneClickhouse;
|
||||||
$server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server');
|
$server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server');
|
||||||
if (($this->dockerCleanup || $isDatabase) && $server) {
|
if (($this->dockerCleanup || $isDatabase) && $server) {
|
||||||
CleanupDocker::run($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->deleteConnectedNetworks && ! $isDatabase) {
|
if ($this->deleteConnectedNetworks && ! $isDatabase) {
|
||||||
@@ -92,7 +92,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
} finally {
|
} finally {
|
||||||
$this->resource->forceDelete();
|
$this->resource->forceDelete();
|
||||||
if ($this->dockerCleanup) {
|
if ($this->dockerCleanup) {
|
||||||
CleanupDocker::run($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
Artisan::queue('cleanup:stucked-resources');
|
Artisan::queue('cleanup:stucked-resources');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
@@ -26,16 +25,6 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [new WithoutOverlapping($this->server->id)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->server->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
@@ -25,16 +24,6 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(public GithubApp $github_app) {}
|
public function __construct(public GithubApp $github_app) {}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [(new WithoutOverlapping($this->github_app->uuid))];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->github_app->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
|
class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
@@ -18,16 +17,6 @@ class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public $timeout = 1000;
|
public $timeout = 1000;
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [(new WithoutOverlapping($this->server->uuid))];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): string
|
|
||||||
{
|
|
||||||
return $this->server->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ use Illuminate\Bus\Queueable;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ScheduledTaskJob implements ShouldQueue
|
class ScheduledTaskJob implements ShouldQueue
|
||||||
@@ -56,24 +55,17 @@ class ScheduledTaskJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
if ($this->resource instanceof Application) {
|
if ($this->resource instanceof Application) {
|
||||||
$timezone = $this->resource->destination->server->settings->server_timezone;
|
$timezone = $this->resource->destination->server->settings->server_timezone;
|
||||||
|
|
||||||
return $timezone;
|
return $timezone;
|
||||||
} elseif ($this->resource instanceof Service) {
|
} elseif ($this->resource instanceof Service) {
|
||||||
$timezone = $this->resource->server->settings->server_timezone;
|
$timezone = $this->resource->server->settings->server_timezone;
|
||||||
|
|
||||||
return $timezone;
|
return $timezone;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'UTC';
|
return 'UTC';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [new WithoutOverlapping($this->task->id)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->task->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public $tries = 3;
|
public $tries = 1;
|
||||||
|
|
||||||
public $timeout = 60;
|
public $timeout = 60;
|
||||||
|
|
||||||
@@ -45,16 +44,6 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [(new WithoutOverlapping($this->server->id))];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->server->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
@@ -26,16 +26,6 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(public Team $team) {}
|
public function __construct(public Team $team) {}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [(new WithoutOverlapping($this->team->uuid))];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->team->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
@@ -26,16 +25,6 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [(new WithoutOverlapping($this->server->uuid))];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->server->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
if (! $this->server->isServerReady($this->tries)) {
|
if (! $this->server->isServerReady($this->tries)) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Destination;
|
namespace App\Livewire\Destination;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Form extends Component
|
class Form extends Component
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class NavbarDeleteTeam extends Component
|
class NavbarDeleteTeam extends Component
|
||||||
{
|
{
|
||||||
@@ -20,6 +20,7 @@ class NavbarDeleteTeam extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace App\Livewire\Project\Application\Deployment;
|
|||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ namespace App\Livewire\Project\Application;
|
|||||||
use App\Actions\Docker\GetContainersStatus;
|
use App\Actions\Docker\GetContainersStatus;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
|
use Illuminate\Process\InvokedProcess;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
use Illuminate\Process\InvokedProcess;
|
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
|
|
||||||
class Previews extends Component
|
class Previews extends Component
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
namespace App\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class BackupEdit extends Component
|
class BackupEdit extends Component
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,9 @@ class BackupEdit extends Component
|
|||||||
public $s3s;
|
public $s3s;
|
||||||
|
|
||||||
public bool $delete_associated_backups_locally = false;
|
public bool $delete_associated_backups_locally = false;
|
||||||
|
|
||||||
public bool $delete_associated_backups_s3 = false;
|
public bool $delete_associated_backups_s3 = false;
|
||||||
|
|
||||||
public bool $delete_associated_backups_sftp = false;
|
public bool $delete_associated_backups_sftp = false;
|
||||||
|
|
||||||
public ?string $status = null;
|
public ?string $status = null;
|
||||||
@@ -56,6 +58,7 @@ class BackupEdit extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +185,7 @@ class BackupEdit extends Component
|
|||||||
['id' => 'delete_associated_backups_locally', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from local storage.'],
|
['id' => 'delete_associated_backups_locally', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from local storage.'],
|
||||||
// ['id' => 'delete_associated_backups_s3', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected S3 Storage.']
|
// ['id' => 'delete_associated_backups_s3', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected S3 Storage.']
|
||||||
// ['id' => 'delete_associated_backups_sftp', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected SFTP Storage.']
|
// ['id' => 'delete_associated_backups_sftp', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected SFTP Storage.']
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,23 @@
|
|||||||
namespace App\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Livewire\Attributes\On;
|
use Livewire\Attributes\On;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class BackupExecutions extends Component
|
class BackupExecutions extends Component
|
||||||
{
|
{
|
||||||
public ?ScheduledDatabaseBackup $backup = null;
|
public ?ScheduledDatabaseBackup $backup = null;
|
||||||
|
|
||||||
public $database;
|
public $database;
|
||||||
|
|
||||||
public $executions = [];
|
public $executions = [];
|
||||||
|
|
||||||
public $setDeletableBackup;
|
public $setDeletableBackup;
|
||||||
|
|
||||||
public $delete_backup_s3 = true;
|
public $delete_backup_s3 = true;
|
||||||
|
|
||||||
public $delete_backup_sftp = true;
|
public $delete_backup_sftp = true;
|
||||||
|
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
@@ -42,12 +46,14 @@ class BackupExecutions extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$execution = $this->backup->executions()->where('id', $executionId)->first();
|
$execution = $this->backup->executions()->where('id', $executionId)->first();
|
||||||
if (is_null($execution)) {
|
if (is_null($execution)) {
|
||||||
$this->dispatch('error', 'Backup execution not found.');
|
$this->dispatch('error', 'Backup execution not found.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +109,7 @@ class BackupExecutions extends Component
|
|||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +120,7 @@ class BackupExecutions extends Component
|
|||||||
return 'UTC';
|
return 'UTC';
|
||||||
}
|
}
|
||||||
$serverTimezone = $server->settings->server_timezone;
|
$serverTimezone = $server->settings->server_timezone;
|
||||||
|
|
||||||
return $serverTimezone;
|
return $serverTimezone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +133,7 @@ class BackupExecutions extends Component
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$dateObj->setTimezone(new \DateTimeZone('UTC'));
|
$dateObj->setTimezone(new \DateTimeZone('UTC'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dateObj->format('Y-m-d H:i:s T');
|
return $dateObj->format('Y-m-d H:i:s T');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +143,7 @@ class BackupExecutions extends Component
|
|||||||
'checkboxes' => [
|
'checkboxes' => [
|
||||||
['id' => 'delete_backup_s3', 'label' => 'Delete the selected backup permanently form S3 Storage'],
|
['id' => 'delete_backup_s3', 'label' => 'Delete the selected backup permanently form S3 Storage'],
|
||||||
['id' => 'delete_backup_sftp', 'label' => 'Delete the selected backup permanently form SFTP Storage'],
|
['id' => 'delete_backup_sftp', 'label' => 'Delete the selected backup permanently form SFTP Storage'],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use App\Models\StandaloneMongodb;
|
|||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class FileStorage extends Component
|
class FileStorage extends Component
|
||||||
{
|
{
|
||||||
@@ -89,6 +89,7 @@ class FileStorage extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +143,7 @@ class FileStorage extends Component
|
|||||||
],
|
],
|
||||||
'fileDeletionCheckboxes' => [
|
'fileDeletionCheckboxes' => [
|
||||||
['id' => 'permanently_delete', 'label' => 'The selected file will be permanently deleted form the server.'],
|
['id' => 'permanently_delete', 'label' => 'The selected file will be permanently deleted form the server.'],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class StackForm extends Component
|
|||||||
$key = data_get($field, 'key');
|
$key = data_get($field, 'key');
|
||||||
$value = data_get($field, 'value');
|
$value = data_get($field, 'value');
|
||||||
$rules = data_get($field, 'rules', 'nullable');
|
$rules = data_get($field, 'rules', 'nullable');
|
||||||
$isPassword = data_get($field, 'isPassword');
|
$isPassword = data_get($field, 'isPassword', false);
|
||||||
$this->fields->put($key, [
|
$this->fields->put($key, [
|
||||||
'serviceName' => $serviceName,
|
'serviceName' => $serviceName,
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
@@ -47,7 +47,15 @@ class StackForm extends Component
|
|||||||
$this->validationAttributes["fields.$key.value"] = $fieldKey;
|
$this->validationAttributes["fields.$key.value"] = $fieldKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->fields = $this->fields->sortBy('name');
|
$this->fields = $this->fields->groupBy('serviceName')->map(function ($group) {
|
||||||
|
return $group->sortBy(function ($field) {
|
||||||
|
return data_get($field, 'isPassword') ? 1 : 0;
|
||||||
|
})->mapWithKeys(function ($field) {
|
||||||
|
return [$field['key'] => $field];
|
||||||
|
});
|
||||||
|
})->flatMap(function ($group) {
|
||||||
|
return $group;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveCompose($raw)
|
public function saveCompose($raw)
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use App\Events\ApplicationStatusChanged;
|
|||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class Destination extends Component
|
class Destination extends Component
|
||||||
{
|
{
|
||||||
@@ -121,6 +121,7 @@ class Destination extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Shared;
|
namespace App\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Helpers\SshMultiplexingHelper;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
@@ -17,7 +18,6 @@ use App\Models\StandalonePostgresql;
|
|||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use App\Helpers\SshMultiplexingHelper;
|
|
||||||
|
|
||||||
class GetLogs extends Component
|
class GetLogs extends Component
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use Livewire\Component;
|
|||||||
class Executions extends Component
|
class Executions extends Component
|
||||||
{
|
{
|
||||||
public $executions = [];
|
public $executions = [];
|
||||||
|
|
||||||
public $selectedKey;
|
public $selectedKey;
|
||||||
|
|
||||||
public $task;
|
public $task;
|
||||||
|
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
@@ -42,6 +44,7 @@ class Executions extends Component
|
|||||||
return $this->task->service->destination->server;
|
return $this->task->service->destination->server;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +55,7 @@ class Executions extends Component
|
|||||||
return 'UTC';
|
return 'UTC';
|
||||||
}
|
}
|
||||||
$serverTimezone = $server->settings->server_timezone;
|
$serverTimezone = $server->settings->server_timezone;
|
||||||
|
|
||||||
return $serverTimezone;
|
return $serverTimezone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +68,7 @@ class Executions extends Component
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$dateObj->setTimezone(new \DateTimeZone('UTC'));
|
$dateObj->setTimezone(new \DateTimeZone('UTC'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dateObj->format('Y-m-d H:i:s T');
|
return $dateObj->format('Y-m-d H:i:s T');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
namespace App\Livewire\Project\Shared\Storages;
|
namespace App\Livewire\Project\Shared\Storages;
|
||||||
|
|
||||||
use App\Models\LocalPersistentVolume;
|
use App\Models\LocalPersistentVolume;
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
@@ -42,6 +42,7 @@ class Show extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ use Livewire\Component;
|
|||||||
class Create extends Component
|
class Create extends Component
|
||||||
{
|
{
|
||||||
public string $name = '';
|
public string $name = '';
|
||||||
|
|
||||||
public string $value = '';
|
public string $value = '';
|
||||||
|
|
||||||
public ?string $from = null;
|
public ?string $from = null;
|
||||||
|
|
||||||
public ?string $description = null;
|
public ?string $description = null;
|
||||||
|
|
||||||
public ?string $publicKey = null;
|
public ?string $publicKey = null;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Security\PrivateKey;
|
namespace App\Livewire\Security\PrivateKey;
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,13 +31,12 @@ class ConfigureCloudflareTunnels extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||||
ConfigureCloudflared::run($server, $this->cloudflare_token);
|
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
|
||||||
$server->settings->is_cloudflare_tunnel = true;
|
$server->settings->is_cloudflare_tunnel = true;
|
||||||
$server->ip = $this->ssh_domain;
|
$server->ip = $this->ssh_domain;
|
||||||
$server->save();
|
$server->save();
|
||||||
$server->settings->save();
|
$server->settings->save();
|
||||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
$this->dispatch('warning', 'Cloudflare Tunnels configuration started.');
|
||||||
$this->dispatch('refreshServerShow');
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class Delete extends Component
|
class Delete extends Component
|
||||||
{
|
{
|
||||||
@@ -17,6 +17,7 @@ class Delete extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -24,11 +24,16 @@ class Form extends Component
|
|||||||
|
|
||||||
public $timezones;
|
public $timezones;
|
||||||
|
|
||||||
protected $listeners = [
|
public function getListeners()
|
||||||
'serverInstalled',
|
{
|
||||||
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
|
|
||||||
|
return [
|
||||||
|
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => 'cloudflareTunnelConfigured',
|
||||||
'refreshServerShow' => 'serverInstalled',
|
'refreshServerShow' => 'serverInstalled',
|
||||||
'revalidate' => '$refresh',
|
'revalidate' => '$refresh',
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'server.name' => 'required',
|
'server.name' => 'required',
|
||||||
@@ -96,6 +101,12 @@ class Form extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function cloudflareTunnelConfigured()
|
||||||
|
{
|
||||||
|
$this->serverInstalled();
|
||||||
|
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
public function serverInstalled()
|
public function serverInstalled()
|
||||||
{
|
{
|
||||||
$this->server->refresh();
|
$this->server->refresh();
|
||||||
@@ -238,4 +249,12 @@ class Form extends Component
|
|||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
$this->dispatch('success', 'Server timezone updated.');
|
$this->dispatch('success', 'Server timezone updated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function manualCloudflareConfig()
|
||||||
|
{
|
||||||
|
$this->server->settings->is_cloudflare_tunnel = true;
|
||||||
|
$this->server->settings->save();
|
||||||
|
$this->server->refresh();
|
||||||
|
$this->dispatch('success', 'Cloudflare Tunnels enabled.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ use App\Actions\Proxy\CheckProxy;
|
|||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Events\ProxyStatusChanged;
|
use App\Events\ProxyStatusChanged;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Illuminate\Process\InvokedProcess;
|
use Illuminate\Process\InvokedProcess;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class Deploy extends Component
|
class Deploy extends Component
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ namespace App\Livewire\Team;
|
|||||||
|
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
class AdminView extends Component
|
class AdminView extends Component
|
||||||
{
|
{
|
||||||
@@ -79,6 +79,7 @@ class AdminView extends Component
|
|||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! auth()->user()->isInstanceAdmin()) {
|
if (! auth()->user()->isInstanceAdmin()) {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ use App\Enums\ApplicationDeploymentStatus;
|
|||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Illuminate\Process\InvokedProcess;
|
use Illuminate\Process\InvokedProcess;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
@@ -237,7 +237,6 @@ class Application extends BaseModel
|
|||||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function additional_servers()
|
public function additional_servers()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Server::class, 'additional_destinations')
|
return $this->belongsToMany(Server::class, 'additional_destinations')
|
||||||
@@ -1096,6 +1095,7 @@ class Application extends BaseModel
|
|||||||
throw new \Exception($e->getMessage());
|
throw new \Exception($e->getMessage());
|
||||||
}
|
}
|
||||||
$services = data_get($yaml, 'services');
|
$services = data_get($yaml, 'services');
|
||||||
|
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$services = collect($services)->map(function ($service) use ($commands) {
|
$services = collect($services)->map(function ($service) use ($commands) {
|
||||||
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||||
|
|||||||
@@ -35,14 +35,17 @@ class ScheduledDatabaseBackup extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ScheduledDatabaseBackupExecution::class)->where('created_at', '>=', now()->subDays($days))->get();
|
return $this->hasMany(ScheduledDatabaseBackupExecution::class)->where('created_at', '>=', now()->subDays($days))->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function server()
|
public function server()
|
||||||
{
|
{
|
||||||
if ($this->database) {
|
if ($this->database) {
|
||||||
if ($this->database->destination && $this->database->destination->server) {
|
if ($this->database->destination && $this->database->destination->server) {
|
||||||
$server = $this->database->destination->server;
|
$server = $this->database->destination->server;
|
||||||
|
|
||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use App\Models\Service;
|
|
||||||
use App\Models\Application;
|
|
||||||
|
|
||||||
class ScheduledTask extends BaseModel
|
class ScheduledTask extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -37,19 +35,23 @@ class ScheduledTask extends BaseModel
|
|||||||
if ($this->application) {
|
if ($this->application) {
|
||||||
if ($this->application->destination && $this->application->destination->server) {
|
if ($this->application->destination && $this->application->destination->server) {
|
||||||
$server = $this->application->destination->server;
|
$server = $this->application->destination->server;
|
||||||
|
|
||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
||||||
} elseif ($this->service) {
|
} elseif ($this->service) {
|
||||||
if ($this->service->destination && $this->service->destination->server) {
|
if ($this->service->destination && $this->service->destination->server) {
|
||||||
$server = $this->service->destination->server;
|
$server = $this->service->destination->server;
|
||||||
|
|
||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
||||||
} elseif ($this->database) {
|
} elseif ($this->database) {
|
||||||
if ($this->database->destination && $this->database->destination->server) {
|
if ($this->database->destination && $this->database->destination->server) {
|
||||||
$server = $this->database->destination->server;
|
$server = $this->database->destination->server;
|
||||||
|
|
||||||
return $server;
|
return $server;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,14 +165,13 @@ class Server extends BaseModel
|
|||||||
$dynamic_conf_path = $this->proxyPath().'/dynamic';
|
$dynamic_conf_path = $this->proxyPath().'/dynamic';
|
||||||
$proxy_type = $this->proxyType();
|
$proxy_type = $this->proxyType();
|
||||||
$redirect_url = $this->proxy->redirect_url;
|
$redirect_url = $this->proxy->redirect_url;
|
||||||
ray($proxy_type);
|
|
||||||
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
||||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
|
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
|
||||||
} elseif ($proxy_type === 'CADDY') {
|
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
|
||||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy";
|
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy";
|
||||||
}
|
}
|
||||||
if (empty($redirect_url)) {
|
if (empty($redirect_url)) {
|
||||||
if ($proxy_type === 'CADDY') {
|
if ($proxy_type === ProxyTypes::CADDY->value) {
|
||||||
$conf = ':80, :443 {
|
$conf = ':80, :443 {
|
||||||
respond 404
|
respond 404
|
||||||
}';
|
}';
|
||||||
@@ -242,7 +241,7 @@ respond 404
|
|||||||
$conf;
|
$conf;
|
||||||
|
|
||||||
$base64 = base64_encode($conf);
|
$base64 = base64_encode($conf);
|
||||||
} elseif ($proxy_type === 'CADDY') {
|
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
|
||||||
$conf = ":80, :443 {
|
$conf = ":80, :443 {
|
||||||
redir $redirect_url
|
redir $redirect_url
|
||||||
}";
|
}";
|
||||||
@@ -258,9 +257,6 @@ respond 404
|
|||||||
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
||||||
], $this);
|
], $this);
|
||||||
|
|
||||||
if (config('app.env') == 'local') {
|
|
||||||
ray($conf);
|
|
||||||
}
|
|
||||||
if ($proxy_type === 'CADDY') {
|
if ($proxy_type === 'CADDY') {
|
||||||
$this->reloadCaddy();
|
$this->reloadCaddy();
|
||||||
}
|
}
|
||||||
@@ -884,6 +880,35 @@ $schema://$host {
|
|||||||
return $this->hasMany(Service::class);
|
return $this->hasMany(Service::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function port(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: function ($value) {
|
||||||
|
return preg_replace('/[^0-9]/', '', $value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: function ($value) {
|
||||||
|
$sanitizedValue = preg_replace('/[^A-Za-z0-9\-_]/', '', $value);
|
||||||
|
|
||||||
|
return $sanitizedValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ip(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: function ($value) {
|
||||||
|
return preg_replace('/[^0-9a-zA-Z.:%-]/', '', $value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function getIp(): Attribute
|
public function getIp(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Process\InvokedProcess;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Process\InvokedProcess;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
@@ -144,6 +144,7 @@ class Service extends BaseModel
|
|||||||
foreach ($dbs as $db) {
|
foreach ($dbs as $db) {
|
||||||
$containersToStop[] = "{$db->name}-{$this->uuid}";
|
$containersToStop[] = "{$db->name}-{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $containersToStop;
|
return $containersToStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions).";
|
$message = "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).";
|
||||||
|
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'message' => "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions).",
|
'message' => "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace App\Traits;
|
namespace App\Traits;
|
||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
|
use App\Helpers\SshMultiplexingHelper;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use App\Helpers\SshMultiplexingHelper;
|
|
||||||
|
|
||||||
trait ExecuteRemoteCommand
|
trait ExecuteRemoteCommand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
|||||||
return 'exited';
|
return 'exited';
|
||||||
}
|
}
|
||||||
$container = format_docker_command_output_to_json($container);
|
$container = format_docker_command_output_to_json($container);
|
||||||
|
if ($container->isEmpty()) {
|
||||||
|
return 'exited';
|
||||||
|
}
|
||||||
if ($all_data) {
|
if ($all_data) {
|
||||||
return $container[0];
|
return $container[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1247,6 +1247,10 @@ function get_public_ips()
|
|||||||
}
|
}
|
||||||
$settings->update(['public_ipv4' => $ipv4]);
|
$settings->update(['public_ipv4' => $ipv4]);
|
||||||
}
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo "Error: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
$ipv6 = $second->output();
|
$ipv6 = $second->output();
|
||||||
if ($ipv6) {
|
if ($ipv6) {
|
||||||
$ipv6 = trim($ipv6);
|
$ipv6 = trim($ipv6);
|
||||||
@@ -2924,10 +2928,11 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
}
|
}
|
||||||
|
|
||||||
$parsedServices = collect([]);
|
$parsedServices = collect([]);
|
||||||
ray()->clearAll();
|
// ray()->clearAll();
|
||||||
|
|
||||||
$allMagicEnvironments = collect([]);
|
$allMagicEnvironments = collect([]);
|
||||||
foreach ($services as $serviceName => $service) {
|
foreach ($services as $serviceName => $service) {
|
||||||
|
$predefinedPort = null;
|
||||||
$magicEnvironments = collect([]);
|
$magicEnvironments = collect([]);
|
||||||
$image = data_get_str($service, 'image');
|
$image = data_get_str($service, 'image');
|
||||||
$environment = collect(data_get($service, 'environment', []));
|
$environment = collect(data_get($service, 'environment', []));
|
||||||
@@ -2936,6 +2941,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||||
|
|
||||||
if ($isService) {
|
if ($isService) {
|
||||||
|
$containerName = "$serviceName-{$resource->uuid}";
|
||||||
|
|
||||||
|
if ($serviceName === 'registry') {
|
||||||
|
$tempServiceName = 'docker-registry';
|
||||||
|
} else {
|
||||||
|
$tempServiceName = $serviceName;
|
||||||
|
}
|
||||||
|
if (str(data_get($service, 'image'))->contains('glitchtip')) {
|
||||||
|
$tempServiceName = 'glitchtip';
|
||||||
|
}
|
||||||
|
if ($serviceName === 'supabase-kong') {
|
||||||
|
$tempServiceName = 'supabase';
|
||||||
|
}
|
||||||
|
$serviceDefinition = data_get($allServices, $tempServiceName);
|
||||||
|
$predefinedPort = data_get($serviceDefinition, 'port');
|
||||||
|
if ($serviceName === 'plausible') {
|
||||||
|
$predefinedPort = '8000';
|
||||||
|
}
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
@@ -2987,8 +3010,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
// SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000
|
// SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000
|
||||||
if (substr_count(str($key)->value(), '_') === 3) {
|
if (substr_count(str($key)->value(), '_') === 3) {
|
||||||
$fqdnFor = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value();
|
$fqdnFor = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value();
|
||||||
|
$port = $key->afterLast('_')->value();
|
||||||
} else {
|
} else {
|
||||||
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
|
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
|
||||||
|
$port = null;
|
||||||
}
|
}
|
||||||
if ($isApplication) {
|
if ($isApplication) {
|
||||||
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
|
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
|
||||||
@@ -2999,19 +3024,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
|
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||||
$path = $value->value();
|
$path = $value->value();
|
||||||
if ($path !== '/') {
|
if ($path !== '/') {
|
||||||
$fqdn = "$fqdn$path";
|
$fqdn = "$fqdn$path";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$fqdnWithPort = $fqdn;
|
||||||
|
if ($port) {
|
||||||
|
$fqdnWithPort = "$fqdn:$port";
|
||||||
|
}
|
||||||
if ($isApplication && is_null($resource->fqdn)) {
|
if ($isApplication && is_null($resource->fqdn)) {
|
||||||
data_forget($resource, 'environment_variables');
|
data_forget($resource, 'environment_variables');
|
||||||
data_forget($resource, 'environment_variables_preview');
|
data_forget($resource, 'environment_variables_preview');
|
||||||
$resource->fqdn = $fqdn;
|
$resource->fqdn = $fqdnWithPort;
|
||||||
$resource->save();
|
$resource->save();
|
||||||
} elseif ($isService && is_null($savedService->fqdn)) {
|
} elseif ($isService && is_null($savedService->fqdn)) {
|
||||||
$savedService->fqdn = $fqdn;
|
$savedService->fqdn = $fqdnWithPort;
|
||||||
$savedService->save();
|
$savedService->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3040,7 +3070,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
}
|
}
|
||||||
|
|
||||||
$allMagicEnvironments = $allMagicEnvironments->merge($magicEnvironments);
|
$allMagicEnvironments = $allMagicEnvironments->merge($magicEnvironments);
|
||||||
|
|
||||||
if ($magicEnvironments->count() > 0) {
|
if ($magicEnvironments->count() > 0) {
|
||||||
foreach ($magicEnvironments as $key => $value) {
|
foreach ($magicEnvironments as $key => $value) {
|
||||||
$key = str($key);
|
$key = str($key);
|
||||||
@@ -3455,6 +3484,18 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$value = $value->after('?');
|
$value = $value->after('?');
|
||||||
}
|
}
|
||||||
if ($originalValue->value() === $value->value()) {
|
if ($originalValue->value() === $value->value()) {
|
||||||
|
// This means the variable does not have a default value, so it needs to be created in Coolify
|
||||||
|
$parsedKeyValue = replaceVariables($value);
|
||||||
|
$resource->environment_variables()->where('key', $parsedKeyValue)->where($nameOfId, $resource->id)->firstOrCreate([
|
||||||
|
'key' => $parsedKeyValue,
|
||||||
|
$nameOfId => $resource->id,
|
||||||
|
], [
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
// Add the variable to the environment so it will be shown in the deployable compose file
|
||||||
|
$environment[$parsedKeyValue->value()] = $resource->environment_variables()->where('key', $parsedKeyValue)->where($nameOfId, $resource->id)->first()->value;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
|
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
|
||||||
@@ -3547,6 +3588,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
if ($environment->count() > 0) {
|
if ($environment->count() > 0) {
|
||||||
$environment = $environment->filter(function ($value, $key) {
|
$environment = $environment->filter(function ($value, $key) {
|
||||||
return ! str($key)->startsWith('SERVICE_FQDN_');
|
return ! str($key)->startsWith('SERVICE_FQDN_');
|
||||||
|
})->map(function ($value, $key) use ($resource) {
|
||||||
|
// if value is empty, set it to null so if you set the environment variable in the .env file (Coolify's UI), it will used
|
||||||
|
if (str($value)->isEmpty()) {
|
||||||
|
if ($resource->environment_variables()->where('key', $key)->exists()) {
|
||||||
|
$value = $resource->environment_variables()->where('key', $key)->first()->value;
|
||||||
|
} else {
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$serviceLabels = $labels->merge($defaultLabels);
|
$serviceLabels = $labels->merge($defaultLabels);
|
||||||
@@ -3631,6 +3683,14 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
data_forget($service, 'volumes.*.is_directory');
|
data_forget($service, 'volumes.*.is_directory');
|
||||||
data_forget($service, 'exclude_from_hc');
|
data_forget($service, 'exclude_from_hc');
|
||||||
|
|
||||||
|
$volumesParsed = $volumesParsed->map(function ($volume) {
|
||||||
|
data_forget($volume, 'content');
|
||||||
|
data_forget($volume, 'is_directory');
|
||||||
|
data_forget($volume, 'isDirectory');
|
||||||
|
|
||||||
|
return $volume;
|
||||||
|
});
|
||||||
|
|
||||||
$payload = collect($service)->merge([
|
$payload = collect($service)->merge([
|
||||||
'container_name' => $containerName,
|
'container_name' => $containerName,
|
||||||
'restart' => $restart->value(),
|
'restart' => $restart->value(),
|
||||||
@@ -3661,6 +3721,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$parsedServices->put($serviceName, $payload);
|
$parsedServices->put($serviceName, $payload);
|
||||||
}
|
}
|
||||||
$topLevel->put('services', $parsedServices);
|
$topLevel->put('services', $parsedServices);
|
||||||
|
|
||||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||||
|
|
||||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||||
|
|||||||
@@ -84,7 +84,11 @@
|
|||||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
|
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
|
||||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate"
|
"Illuminate\\Foundation\\ComposerScripts::postUpdate"
|
||||||
],
|
],
|
||||||
"post-install-cmd": [],
|
"post-install-cmd": [
|
||||||
|
"cp -r 'hooks/' '.git/hooks/'",
|
||||||
|
"php -r \"copy('hooks/pre-commit', '.git/hooks/pre-commit');\"",
|
||||||
|
"php -r \"chmod('.git/hooks/pre-commit', 0777);\""
|
||||||
|
],
|
||||||
"post-root-package-install": [
|
"post-root-package-install": [
|
||||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,424 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Enable Clockwork
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork is enabled by default only when your application is in debug mode. Here you can explicitly enable or
|
|
||||||
| disable Clockwork. When disabled, no data is collected and the api and web ui are inactive.
|
|
||||||
| Unless explicitly enabled, Clockwork only runs on localhost, *.local, *.test and *.wip domains.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'enable' => env('CLOCKWORK_ENABLE', null),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Features
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| You can enable or disable various Clockwork features here. Some features have additional settings (eg. slow query
|
|
||||||
| threshold for database queries).
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'features' => [
|
|
||||||
|
|
||||||
// Cache usage stats and cache queries including results
|
|
||||||
'cache' => [
|
|
||||||
'enabled' => env('CLOCKWORK_CACHE_ENABLED', true),
|
|
||||||
|
|
||||||
// Collect cache queries
|
|
||||||
'collect_queries' => env('CLOCKWORK_CACHE_QUERIES', true),
|
|
||||||
|
|
||||||
// Collect values from cache queries (high performance impact with a very high number of queries)
|
|
||||||
'collect_values' => env('CLOCKWORK_CACHE_COLLECT_VALUES', false)
|
|
||||||
],
|
|
||||||
|
|
||||||
// Database usage stats and queries
|
|
||||||
'database' => [
|
|
||||||
'enabled' => env('CLOCKWORK_DATABASE_ENABLED', true),
|
|
||||||
|
|
||||||
// Collect database queries (high performance impact with a very high number of queries)
|
|
||||||
'collect_queries' => env('CLOCKWORK_DATABASE_COLLECT_QUERIES', true),
|
|
||||||
|
|
||||||
// Collect details of models updates (high performance impact with a lot of model updates)
|
|
||||||
'collect_models_actions' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_ACTIONS', true),
|
|
||||||
|
|
||||||
// Collect details of retrieved models (very high performance impact with a lot of models retrieved)
|
|
||||||
'collect_models_retrieved' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_RETRIEVED', false),
|
|
||||||
|
|
||||||
// Query execution time threshold in milliseconds after which the query will be marked as slow
|
|
||||||
'slow_threshold' => env('CLOCKWORK_DATABASE_SLOW_THRESHOLD'),
|
|
||||||
|
|
||||||
// Collect only slow database queries
|
|
||||||
'slow_only' => env('CLOCKWORK_DATABASE_SLOW_ONLY', false),
|
|
||||||
|
|
||||||
// Detect and report duplicate queries
|
|
||||||
'detect_duplicate_queries' => env('CLOCKWORK_DATABASE_DETECT_DUPLICATE_QUERIES', false)
|
|
||||||
],
|
|
||||||
|
|
||||||
// Dispatched events
|
|
||||||
'events' => [
|
|
||||||
'enabled' => env('CLOCKWORK_EVENTS_ENABLED', true),
|
|
||||||
|
|
||||||
// Ignored events (framework events are ignored by default)
|
|
||||||
'ignored_events' => [
|
|
||||||
// App\Events\UserRegistered::class,
|
|
||||||
// 'user.registered'
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
// Laravel log (you can still log directly to Clockwork with laravel log disabled)
|
|
||||||
'log' => [
|
|
||||||
'enabled' => env('CLOCKWORK_LOG_ENABLED', true)
|
|
||||||
],
|
|
||||||
|
|
||||||
// Sent notifications
|
|
||||||
'notifications' => [
|
|
||||||
'enabled' => env('CLOCKWORK_NOTIFICATIONS_ENABLED', true),
|
|
||||||
],
|
|
||||||
|
|
||||||
// Performance metrics
|
|
||||||
'performance' => [
|
|
||||||
// Allow collecting of client metrics. Requires separate clockwork-browser npm package.
|
|
||||||
'client_metrics' => env('CLOCKWORK_PERFORMANCE_CLIENT_METRICS', true)
|
|
||||||
],
|
|
||||||
|
|
||||||
// Dispatched queue jobs
|
|
||||||
'queue' => [
|
|
||||||
'enabled' => env('CLOCKWORK_QUEUE_ENABLED', true)
|
|
||||||
],
|
|
||||||
|
|
||||||
// Redis commands
|
|
||||||
'redis' => [
|
|
||||||
'enabled' => env('CLOCKWORK_REDIS_ENABLED', true)
|
|
||||||
],
|
|
||||||
|
|
||||||
// Routes list
|
|
||||||
'routes' => [
|
|
||||||
'enabled' => env('CLOCKWORK_ROUTES_ENABLED', false),
|
|
||||||
|
|
||||||
// Collect only routes from particular namespaces (only application routes by default)
|
|
||||||
'only_namespaces' => [ 'App' ]
|
|
||||||
],
|
|
||||||
|
|
||||||
// Rendered views
|
|
||||||
'views' => [
|
|
||||||
'enabled' => env('CLOCKWORK_VIEWS_ENABLED', true),
|
|
||||||
|
|
||||||
// Collect views including view data (high performance impact with a high number of views)
|
|
||||||
'collect_data' => env('CLOCKWORK_VIEWS_COLLECT_DATA', false),
|
|
||||||
|
|
||||||
// Use Twig profiler instead of Laravel events for apps using laravel-twigbridge (more precise, but does
|
|
||||||
// not support collecting view data)
|
|
||||||
'use_twig_profiler' => env('CLOCKWORK_VIEWS_USE_TWIG_PROFILER', false)
|
|
||||||
]
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Enable web UI
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork comes with a web UI accessible via http://your.app/clockwork. Here you can enable or disable this
|
|
||||||
| feature. You can also set a custom path for the web UI.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'web' => env('CLOCKWORK_WEB', true),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Enable toolbar
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork can show a toolbar with basic metrics on all responses. Here you can enable or disable this feature.
|
|
||||||
| Requires a separate clockwork-browser npm library.
|
|
||||||
| For installation instructions see https://underground.works/clockwork/#docs-viewing-data
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'toolbar' => env('CLOCKWORK_TOOLBAR', true),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| HTTP requests collection
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork collects data about HTTP requests to your app. Here you can choose which requests should be collected.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'requests' => [
|
|
||||||
// With on-demand mode enabled, Clockwork will only profile requests when the browser extension is open or you
|
|
||||||
// manually pass a "clockwork-profile" cookie or get/post data key.
|
|
||||||
// Optionally you can specify a "secret" that has to be passed as the value to enable profiling.
|
|
||||||
'on_demand' => env('CLOCKWORK_REQUESTS_ON_DEMAND', false),
|
|
||||||
|
|
||||||
// Collect only errors (requests with HTTP 4xx and 5xx responses)
|
|
||||||
'errors_only' => env('CLOCKWORK_REQUESTS_ERRORS_ONLY', false),
|
|
||||||
|
|
||||||
// Response time threshold in milliseconds after which the request will be marked as slow
|
|
||||||
'slow_threshold' => env('CLOCKWORK_REQUESTS_SLOW_THRESHOLD'),
|
|
||||||
|
|
||||||
// Collect only slow requests
|
|
||||||
'slow_only' => env('CLOCKWORK_REQUESTS_SLOW_ONLY', false),
|
|
||||||
|
|
||||||
// Sample the collected requests (e.g. set to 100 to collect only 1 in 100 requests)
|
|
||||||
'sample' => env('CLOCKWORK_REQUESTS_SAMPLE', false),
|
|
||||||
|
|
||||||
// List of URIs that should not be collected
|
|
||||||
'except' => [
|
|
||||||
'/horizon/.*', // Laravel Horizon requests
|
|
||||||
'/telescope/.*', // Laravel Telescope requests
|
|
||||||
'/_tt/.*', // Laravel Telescope toolbar
|
|
||||||
'/_debugbar/.*', // Laravel DebugBar requests
|
|
||||||
],
|
|
||||||
|
|
||||||
// List of URIs that should be collected, any other URI will not be collected if not empty
|
|
||||||
'only' => [
|
|
||||||
// '/api/.*'
|
|
||||||
],
|
|
||||||
|
|
||||||
// Don't collect OPTIONS requests, mostly used in the CSRF pre-flight requests and are rarely of interest
|
|
||||||
'except_preflight' => env('CLOCKWORK_REQUESTS_EXCEPT_PREFLIGHT', true)
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Artisan commands collection
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork can collect data about executed artisan commands. Here you can enable and configure which commands
|
|
||||||
| should be collected.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'artisan' => [
|
|
||||||
// Enable or disable collection of executed Artisan commands
|
|
||||||
'collect' => env('CLOCKWORK_ARTISAN_COLLECT', false),
|
|
||||||
|
|
||||||
// List of commands that should not be collected (built-in commands are not collected by default)
|
|
||||||
'except' => [
|
|
||||||
// 'inspire'
|
|
||||||
],
|
|
||||||
|
|
||||||
// List of commands that should be collected, any other command will not be collected if not empty
|
|
||||||
'only' => [
|
|
||||||
// 'inspire'
|
|
||||||
],
|
|
||||||
|
|
||||||
// Enable or disable collection of command output
|
|
||||||
'collect_output' => env('CLOCKWORK_ARTISAN_COLLECT_OUTPUT', false),
|
|
||||||
|
|
||||||
// Enable or disable collection of built-in Laravel commands
|
|
||||||
'except_laravel_commands' => env('CLOCKWORK_ARTISAN_EXCEPT_LARAVEL_COMMANDS', true)
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Queue jobs collection
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork can collect data about executed queue jobs. Here you can enable and configure which queue jobs should
|
|
||||||
| be collected.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'queue' => [
|
|
||||||
// Enable or disable collection of executed queue jobs
|
|
||||||
'collect' => env('CLOCKWORK_QUEUE_COLLECT', false),
|
|
||||||
|
|
||||||
// List of queue jobs that should not be collected
|
|
||||||
'except' => [
|
|
||||||
// App\Jobs\ExpensiveJob::class
|
|
||||||
],
|
|
||||||
|
|
||||||
// List of queue jobs that should be collected, any other queue job will not be collected if not empty
|
|
||||||
'only' => [
|
|
||||||
// App\Jobs\BuggyJob::class
|
|
||||||
]
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Tests collection
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork can collect data about executed tests. Here you can enable and configure which tests should be
|
|
||||||
| collected.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'tests' => [
|
|
||||||
// Enable or disable collection of ran tests
|
|
||||||
'collect' => env('CLOCKWORK_TESTS_COLLECT', false),
|
|
||||||
|
|
||||||
// List of tests that should not be collected
|
|
||||||
'except' => [
|
|
||||||
// Tests\Unit\ExampleTest::class
|
|
||||||
]
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Enable data collection when Clockwork is disabled
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| You can enable this setting to collect data even when Clockwork is disabled, e.g. for future analysis.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'collect_data_always' => env('CLOCKWORK_COLLECT_DATA_ALWAYS', false),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Metadata storage
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Configure how is the metadata collected by Clockwork stored. Three options are available:
|
|
||||||
| - files - A simple fast storage implementation storing data in one-per-request files.
|
|
||||||
| - sql - Stores requests in a sql database. Supports MySQL, PostgreSQL and SQLite. Requires PDO.
|
|
||||||
| - redis - Stores requests in redis. Requires phpredis.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'storage' => env('CLOCKWORK_STORAGE', 'files'),
|
|
||||||
|
|
||||||
// Path where the Clockwork metadata is stored
|
|
||||||
'storage_files_path' => env('CLOCKWORK_STORAGE_FILES_PATH', storage_path('clockwork')),
|
|
||||||
|
|
||||||
// Compress the metadata files using gzip, trading a little bit of performance for lower disk usage
|
|
||||||
'storage_files_compress' => env('CLOCKWORK_STORAGE_FILES_COMPRESS', false),
|
|
||||||
|
|
||||||
// SQL database to use, can be a name of database configured in database.php or a path to a SQLite file
|
|
||||||
'storage_sql_database' => env('CLOCKWORK_STORAGE_SQL_DATABASE', storage_path('clockwork.sqlite')),
|
|
||||||
|
|
||||||
// SQL table name to use, the table is automatically created and updated when needed
|
|
||||||
'storage_sql_table' => env('CLOCKWORK_STORAGE_SQL_TABLE', 'clockwork'),
|
|
||||||
|
|
||||||
// Redis connection, name of redis connection or cluster configured in database.php
|
|
||||||
'storage_redis' => env('CLOCKWORK_STORAGE_REDIS', 'default'),
|
|
||||||
|
|
||||||
// Redis prefix for Clockwork keys ("clockwork" if not set)
|
|
||||||
'storage_redis_prefix' => env('CLOCKWORK_STORAGE_REDIS_PREFIX', 'clockwork'),
|
|
||||||
|
|
||||||
// Maximum lifetime of collected metadata in minutes, older requests will automatically be deleted, false to disable
|
|
||||||
'storage_expiration' => env('CLOCKWORK_STORAGE_EXPIRATION', 60 * 24 * 7),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Authentication
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork can be configured to require authentication before allowing access to the collected data. This might be
|
|
||||||
| useful when the application is publicly accessible. Setting to true will enable a simple authentication with a
|
|
||||||
| pre-configured password. You can also pass a class name of a custom implementation.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'authentication' => env('CLOCKWORK_AUTHENTICATION', false),
|
|
||||||
|
|
||||||
// Password for the simple authentication
|
|
||||||
'authentication_password' => env('CLOCKWORK_AUTHENTICATION_PASSWORD', 'VerySecretPassword'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Stack traces collection
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork can collect stack traces for log messages and certain data like database queries. Here you can set
|
|
||||||
| whether to collect stack traces, limit the number of collected frames and set further configuration. Collecting
|
|
||||||
| long stack traces considerably increases metadata size.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'stack_traces' => [
|
|
||||||
// Enable or disable collecting of stack traces
|
|
||||||
'enabled' => env('CLOCKWORK_STACK_TRACES_ENABLED', true),
|
|
||||||
|
|
||||||
// Limit the number of frames to be collected
|
|
||||||
'limit' => env('CLOCKWORK_STACK_TRACES_LIMIT', 10),
|
|
||||||
|
|
||||||
// List of vendor names to skip when determining caller, common vendors are automatically added
|
|
||||||
'skip_vendors' => [
|
|
||||||
// 'phpunit'
|
|
||||||
],
|
|
||||||
|
|
||||||
// List of namespaces to skip when determining caller
|
|
||||||
'skip_namespaces' => [
|
|
||||||
// 'Laravel'
|
|
||||||
],
|
|
||||||
|
|
||||||
// List of class names to skip when determining caller
|
|
||||||
'skip_classes' => [
|
|
||||||
// App\CustomLog::class
|
|
||||||
]
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Serialization
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork serializes the collected data to json for storage and transfer. Here you can configure certain aspects
|
|
||||||
| of serialization. Serialization has a large effect on the cpu time and memory usage.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Maximum depth of serialized multi-level arrays and objects
|
|
||||||
'serialization_depth' => env('CLOCKWORK_SERIALIZATION_DEPTH', 10),
|
|
||||||
|
|
||||||
// A list of classes that will never be serialized (e.g. a common service container class)
|
|
||||||
'serialization_blackbox' => [
|
|
||||||
\Illuminate\Container\Container::class,
|
|
||||||
\Illuminate\Foundation\Application::class,
|
|
||||||
\Laravel\Lumen\Application::class
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Register helpers
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork comes with a "clock" global helper function. You can use this helper to quickly log something and to
|
|
||||||
| access the Clockwork instance.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'register_helpers' => env('CLOCKWORK_REGISTER_HELPERS', true),
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Send headers for AJAX request
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| When trying to collect data, the AJAX method can sometimes fail if it is missing required headers. For example, an
|
|
||||||
| API might require a version number using Accept headers to route the HTTP request to the correct codebase.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'headers' => [
|
|
||||||
// 'Accept' => 'application/vnd.com.whatever.v1+json',
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
| Server timing
|
|
||||||
|------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Clockwork supports the W3C Server Timing specification, which allows for collecting a simple performance metrics
|
|
||||||
| in a cross-browser way. E.g. in Chrome, your app, database and timeline event timings will be shown in the Dev
|
|
||||||
| Tools network tab. This setting specifies the max number of timeline events that will be sent. Setting to false
|
|
||||||
| will disable the feature.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'server_timing' => env('CLOCKWORK_SERVER_TIMING', 10)
|
|
||||||
|
|
||||||
];
|
|
||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.342',
|
'release' => '4.0.0-beta.343',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.342';
|
return '4.0.0-beta.343';
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class UpdateServerSettingsDefaultTimezone extends Migration
|
class UpdateServerSettingsDefaultTimezone extends Migration
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class EncryptExistingPrivateKeys extends Migration
|
class EncryptExistingPrivateKeys extends Migration
|
||||||
{
|
{
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
DB::table('private_keys')->chunkById(100, function ($keys) {
|
DB::table('private_keys')->chunkById(100, function ($keys) {
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
DB::table('private_keys')
|
DB::table('private_keys')
|
||||||
@@ -15,5 +16,10 @@ class EncryptExistingPrivateKeys extends Migration
|
|||||||
->update(['private_key' => Crypt::encryptString($key->private_key)]);
|
->update(['private_key' => Crypt::encryptString($key->private_key)]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo 'Encrypting private keys failed.';
|
||||||
|
echo $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class PopulateSshKeysAndClearMuxDirectory extends Migration
|
|
||||||
{
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
// Storage::disk('ssh-keys')->deleteDirectory('');
|
|
||||||
// Storage::disk('ssh-keys')->makeDirectory('');
|
|
||||||
|
|
||||||
// Storage::disk('ssh-mux')->deleteDirectory('');
|
|
||||||
// Storage::disk('ssh-mux')->makeDirectory('');
|
|
||||||
// PrivateKey::chunk(100, function ($keys) {
|
|
||||||
// foreach ($keys as $key) {
|
|
||||||
// $key->storeInFileSystem();
|
|
||||||
// if ($key->id === 0) {
|
|
||||||
// Storage::disk('ssh-keys')->put('id.root@host.docker.internal', $key->private_key);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class AddSshKeyFingerprintToPrivateKeysTable extends Migration
|
class AddSshKeyFingerprintToPrivateKeysTable extends Migration
|
||||||
@@ -13,13 +14,20 @@ class AddSshKeyFingerprintToPrivateKeysTable extends Migration
|
|||||||
$table->string('fingerprint')->after('private_key')->nullable();
|
$table->string('fingerprint')->after('private_key')->nullable();
|
||||||
});
|
});
|
||||||
|
|
||||||
PrivateKey::whereNull('fingerprint')->each(function ($key) {
|
try {
|
||||||
|
DB::table('private_keys')->chunkById(100, function ($keys) {
|
||||||
|
foreach ($keys as $key) {
|
||||||
$fingerprint = PrivateKey::generateFingerprint($key->private_key);
|
$fingerprint = PrivateKey::generateFingerprint($key->private_key);
|
||||||
if ($fingerprint) {
|
if ($fingerprint) {
|
||||||
$key->fingerprint = $fingerprint;
|
$key->fingerprint = $fingerprint;
|
||||||
$key->save();
|
$key->save();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo 'Generating fingerprints failed.';
|
||||||
|
echo $e->getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down()
|
public function down()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class PopulateSshKeysDirectorySeeder extends Seeder
|
|||||||
{
|
{
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
Storage::disk('ssh-keys')->deleteDirectory('');
|
Storage::disk('ssh-keys')->deleteDirectory('');
|
||||||
Storage::disk('ssh-keys')->makeDirectory('');
|
Storage::disk('ssh-keys')->makeDirectory('');
|
||||||
Storage::disk('ssh-mux')->deleteDirectory('');
|
Storage::disk('ssh-mux')->deleteDirectory('');
|
||||||
@@ -23,7 +24,17 @@ class PopulateSshKeysDirectorySeeder extends Seeder
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Process::run('chown -R 9999:9999 '.storage_path('app/ssh/keys'));
|
if (isDev()) {
|
||||||
Process::run('chown -R 9999:9999 '.storage_path('app/ssh/mux'));
|
$user = env('PUID').':'.env('PGID');
|
||||||
|
Process::run("chown -R $user ".storage_path('app/ssh/keys'));
|
||||||
|
Process::run("chown -R $user ".storage_path('app/ssh/mux'));
|
||||||
|
} else {
|
||||||
|
Process::run('chown -R 9999:root '.storage_path('app/ssh/keys'));
|
||||||
|
Process::run('chown -R 9999:root '.storage_path('app/ssh/mux'));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error: {$e->getMessage()}\n";
|
||||||
|
ray($e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Enums\ProxyStatus;
|
||||||
|
use App\Enums\ProxyTypes;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
@@ -16,6 +18,10 @@ class ServerSeeder extends Seeder
|
|||||||
'ip' => 'coolify-testing-host',
|
'ip' => 'coolify-testing-host',
|
||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
'private_key_id' => 1,
|
'private_key_id' => 1,
|
||||||
|
'proxy' => [
|
||||||
|
'type' => ProxyTypes::TRAEFIK->value,
|
||||||
|
'status' => ProxyStatus::EXITED->value,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#!/command/execlineb -P
|
#!/command/execlineb -P
|
||||||
s6-setuidgid webuser
|
s6-setuidgid webuser
|
||||||
php /var/www/html/artisan app:init --full-cleanup
|
php /var/www/html/artisan app:init
|
||||||
|
|||||||
21
hooks/pre-commit
Normal file
21
hooks/pre-commit
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Detect whether /dev/tty is available & functional
|
||||||
|
if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then
|
||||||
|
exec < /dev/tty
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get list of stashed PHP files
|
||||||
|
stashed_files=$(git diff --cached --name-only --diff-filter=ACM -- '*.php')
|
||||||
|
|
||||||
|
# If there are no stashed PHP files, exit early
|
||||||
|
if [ -z "$stashed_files" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set files variable to only include stashed PHP files
|
||||||
|
files="$stashed_files"
|
||||||
|
|
||||||
|
$(pwd)/vendor/bin/pint $files -q
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
git add $files
|
||||||
|
fi
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"service.stop": "This service will be stopped.",
|
"service.stop": "This service will be stopped.",
|
||||||
"resource.docker_cleanup": "Run Docker Cleanup (remove unused images and builder cache).",
|
"resource.docker_cleanup": "Run Docker Cleanup (remove unused images and builder cache).",
|
||||||
"resource.non_persistent": "All non-persistent data will be deleted.",
|
"resource.non_persistent": "All non-persistent data will be deleted.",
|
||||||
"resource.delete_volumes": "All volumes associated with this resource will be permanently deleted.",
|
"resource.delete_volumes": "Permanently delete all volumes associated with this resource.",
|
||||||
"resource.delete_connected_networks": "All non-predefined networks associated with this resource will be permanently deleted.",
|
"resource.delete_connected_networks": "Permanently delete all non-predefined networks associated with this resource.",
|
||||||
"resource.delete_configurations": "All configuration files will be permanently deleted from the server."
|
"resource.delete_configurations": "Permanently delete all configuration files from the server."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,7 @@ DB_PORT=5432
|
|||||||
# Set to true to enable Ray
|
# Set to true to enable Ray
|
||||||
RAY_ENABLED=false
|
RAY_ENABLED=false
|
||||||
# Set custom ray port
|
# Set custom ray port
|
||||||
RAY_PORT=
|
# RAY_PORT=
|
||||||
|
|
||||||
# Clockwork Configuration
|
|
||||||
CLOCKWORK_ENABLED=false
|
|
||||||
CLOCKWORK_QUEUE_COLLECT=true
|
|
||||||
|
|
||||||
# Enable Laravel Telescope for debugging
|
# Enable Laravel Telescope for debugging
|
||||||
TELESCOPE_ENABLED=false
|
TELESCOPE_ENABLED=false
|
||||||
|
|||||||
@@ -398,90 +398,10 @@ if [ ! -f ~/.ssh/authorized_keys ]; then
|
|||||||
chmod 600 ~/.ssh/authorized_keys
|
chmod 600 ~/.ssh/authorized_keys
|
||||||
fi
|
fi
|
||||||
|
|
||||||
checkSshKeyInAuthorizedKeys() {
|
|
||||||
grep -qw "root@coolify" ~/.ssh/authorized_keys
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
checkSshKeyInCoolifyData() {
|
|
||||||
[ -s /data/coolify/ssh/keys/id.root@host.docker.internal ]
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
generateAuthorizedKeys() {
|
|
||||||
sed -i "/root@coolify/d" ~/.ssh/authorized_keys
|
|
||||||
cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys
|
|
||||||
rm -f /data/coolify/ssh/keys/id.root@host.docker.internal.pub
|
|
||||||
}
|
|
||||||
generateSshKey() {
|
|
||||||
echo " - Generating SSH key."
|
|
||||||
ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.root@host.docker.internal -q -N "" -C root@coolify
|
|
||||||
chown 9999 /data/coolify/ssh/keys/id.root@host.docker.internal
|
|
||||||
generateAuthorizedKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
syncSshKeys() {
|
|
||||||
DB_RUNNING=$(docker inspect coolify-db --format '{{ .State.Status }}' 2>/dev/null)
|
|
||||||
# Check if SSH key exists in Coolify data but not in authorized_keys
|
|
||||||
if checkSshKeyInCoolifyData && ! checkSshKeyInAuthorizedKeys; then
|
|
||||||
# Add the existing Coolify SSH key to authorized_keys
|
|
||||||
cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys
|
|
||||||
# Check if SSH key exists in authorized_keys but not in Coolify data
|
|
||||||
elif checkSshKeyInAuthorizedKeys && ! checkSshKeyInCoolifyData; then
|
|
||||||
# Ensure Coolify DB is running before proceeding
|
|
||||||
if [ "$DB_RUNNING" = "running" ]; then
|
|
||||||
# Retrieve DB user and SSH key from Coolify database
|
|
||||||
DB_USER=$(docker inspect coolify-db --format '{{ .Config.Env }}' | grep -oP 'POSTGRES_USER=\K[^ ]+')
|
|
||||||
DB_SSH_KEY=$(docker exec coolify-db psql -U $DB_USER -d coolify -t -c "SELECT \"private_key\" FROM \"private_keys\" WHERE id = 0 AND team_id = 0 LIMIT 1;" -A -t)
|
|
||||||
|
|
||||||
if [ -z "$DB_SSH_KEY" ]; then
|
|
||||||
# If no key found in DB, generate a new one
|
|
||||||
echo " - SSH key not found in database. Generating new key."
|
|
||||||
generateSshKey
|
|
||||||
else
|
|
||||||
# If key found in DB, save it and update authorized_keys
|
|
||||||
echo " - SSH key found in database. Saving to file."
|
|
||||||
echo "$DB_SSH_KEY" > /data/coolify/ssh/keys/id.root@host.docker.internal
|
|
||||||
chmod 600 /data/coolify/ssh/keys/id.root@host.docker.internal
|
|
||||||
chown 9999 /data/coolify/ssh/keys/id.root@host.docker.internal
|
|
||||||
|
|
||||||
# Generate public key from private key and update authorized_keys
|
|
||||||
ssh-keygen -y -f /data/coolify/ssh/keys/id.root@host.docker.internal -C root@coolify > /data/coolify/ssh/keys/id.root@host.docker.internal.pub
|
|
||||||
sed -i "/root@coolify/d" ~/.ssh/authorized_keys
|
|
||||||
cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys
|
|
||||||
rm -f /data/coolify/ssh/keys/id.root@host.docker.internal.pub
|
|
||||||
chmod 600 ~/.ssh/authorized_keys
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# If SSH key doesn't exist in either location
|
|
||||||
elif ! checkSshKeyInAuthorizedKeys && ! checkSshKeyInCoolifyData; then
|
|
||||||
# Ensure Coolify DB is running before proceeding
|
|
||||||
if [ "$DB_RUNNING" = "running" ]; then
|
|
||||||
# Retrieve DB user and SSH key from Coolify database
|
|
||||||
DB_USER=$(docker inspect coolify-db --format '{{ .Config.Env }}' | grep -oP 'POSTGRES_USER=\K[^ ]+')
|
|
||||||
DB_SSH_KEY=$(docker exec coolify-db psql -U $DB_USER -d coolify -t -c "SELECT \"private_key\" FROM \"private_keys\" WHERE id = 0 AND team_id = 0 LIMIT 1;" -A -t)
|
|
||||||
if [ -z "$DB_SSH_KEY" ]; then
|
|
||||||
# If no key found in DB, generate a new one
|
|
||||||
echo " - SSH key not found in database. Generating new key."
|
|
||||||
generateSshKey
|
|
||||||
else
|
|
||||||
# If key found in DB, save it and update authorized_keys
|
|
||||||
echo " - SSH key found in database. Saving to file."
|
|
||||||
echo "$DB_SSH_KEY" > /data/coolify/ssh/keys/id.root@host.docker.internal
|
|
||||||
chmod 600 /data/coolify/ssh/keys/id.root@host.docker.internal
|
|
||||||
ssh-keygen -y -f /data/coolify/ssh/keys/id.root@host.docker.internal -C root@coolify > /data/coolify/ssh/keys/id.root@host.docker.internal.pub
|
|
||||||
sed -i "/root@coolify/d" ~/.ssh/authorized_keys
|
|
||||||
cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
generateSshKey
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
IS_COOLIFY_VOLUME_EXISTS=$(docker volume ls | grep coolify-db | wc -l)
|
IS_COOLIFY_VOLUME_EXISTS=$(docker volume ls | grep coolify-db | wc -l)
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ "$IS_COOLIFY_VOLUME_EXISTS" -eq 0 ]; then
|
if [ "$IS_COOLIFY_VOLUME_EXISTS" -eq 0 ]; then
|
||||||
echo " - Generating SSH key."
|
echo " - Generating SSH key."
|
||||||
ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.$CURRENT_USER@host.docker.internal -q -N "" -C coolify
|
ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.$CURRENT_USER@host.docker.internal -q -N "" -C coolify
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.339"
|
"version": "4.0.0-beta.343"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.340"
|
"version": "4.0.0-beta.344"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.1"
|
"version": "1.0.1"
|
||||||
|
|||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1860,9 +1860,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.29.4",
|
"version": "3.29.5",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
|
||||||
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
|
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
'hideLabel' => false,
|
'hideLabel' => false,
|
||||||
])
|
])
|
||||||
|
|
||||||
<div class="flex flex-row items-center gap-4 px-2 py-1 form-control min-w-fit dark:hover:bg-coolgray-100">
|
<div class="flex flex-row items-center py-1 form-control min-w-fit dark:hover:bg-coolgray-100">
|
||||||
@if (!$hideLabel)
|
@if (!$hideLabel)
|
||||||
<label class="flex gap-4 px-0 min-w-fit label">
|
<label class="flex gap-4 px-0 min-w-fit label">
|
||||||
<span class="flex gap-2">
|
<span class="flex gap-2">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
'w-full' => !$isMultiline,
|
'w-full' => !$isMultiline,
|
||||||
])>
|
])>
|
||||||
@if ($label)
|
@if ($label)
|
||||||
<label class="flex items-center gap-1 mb-1 text-sm font-medium">{{ $label }}
|
<label class="flex gap-1 items-center mb-1 text-sm font-medium">{{ $label }}
|
||||||
@if ($required)
|
@if ($required)
|
||||||
<x-highlighted text="*" />
|
<x-highlighted text="*" />
|
||||||
@endif
|
@endif
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<div class="relative" x-data="{ type: 'password' }">
|
<div class="relative" x-data="{ type: 'password' }">
|
||||||
@if ($allowToPeak)
|
@if ($allowToPeak)
|
||||||
<div x-on:click="changePasswordFieldType"
|
<div x-on:click="changePasswordFieldType"
|
||||||
class="absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer hover:dark:text-white">
|
class="flex absolute inset-y-0 right-0 items-center pr-2 cursor-pointer hover:dark:text-white">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
|||||||
@@ -139,7 +139,7 @@
|
|||||||
@endif
|
@endif
|
||||||
@else
|
@else
|
||||||
@if ($buttonFullWidth)
|
@if ($buttonFullWidth)
|
||||||
<x-forms.button @click="modalOpen=true" class="flex w-full gap-2" wire:target>
|
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" wire:target>
|
||||||
{{ $buttonTitle }}
|
{{ $buttonTitle }}
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@else
|
@else
|
||||||
@@ -162,17 +162,17 @@
|
|||||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||||
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
|
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
|
||||||
class="relative w-full py-6 border rounded min-w-full lg:min-w-[36rem] max-w-[48rem] bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
|
class="relative w-full py-6 border rounded min-w-full lg:min-w-[36rem] max-w-[48rem] bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
|
||||||
<div class="flex items-center justify-between pb-3">
|
<div class="flex justify-between items-center pb-3">
|
||||||
<h3 class="text-2xl font-bold pr-8">{{ $title }}</h3>
|
<h3 class="pr-8 text-2xl font-bold">{{ $title }}</h3>
|
||||||
<button @click="modalOpen = false; resetModal()"
|
<button @click="modalOpen = false; resetModal()"
|
||||||
class="absolute top-2 right-2 flex items-center justify-center w-8 h-8 rounded-full dark:text-white hover:bg-coolgray-300">
|
class="flex absolute top-2 right-2 justify-center items-center w-8 h-8 rounded-full dark:text-white hover:bg-coolgray-300">
|
||||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor">
|
stroke-width="1.5" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative w-auto pb-8">
|
<div class="relative pb-8 w-auto">
|
||||||
@if (!empty($checkboxes))
|
@if (!empty($checkboxes))
|
||||||
<!-- Step 1: Select actions -->
|
<!-- Step 1: Select actions -->
|
||||||
<div x-show="step === 1">
|
<div x-show="step === 1">
|
||||||
@@ -180,9 +180,9 @@
|
|||||||
<h4>Actions</h4>
|
<h4>Actions</h4>
|
||||||
</div>
|
</div>
|
||||||
@foreach ($checkboxes as $index => $checkbox)
|
@foreach ($checkboxes as $index => $checkbox)
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex justify-between items-center mb-2">
|
||||||
<label for="{{ $checkbox['id'] }}"
|
<label for="{{ $checkbox['id'] }}"
|
||||||
class="text-sm leading-5 text-gray-700 dark:text-gray-300 flex-grow pr-4">
|
class="flex-grow pr-4 text-sm leading-5 text-gray-700 dark:text-gray-300">
|
||||||
{{ $checkbox['label'] }}
|
{{ $checkbox['label'] }}
|
||||||
</label>
|
</label>
|
||||||
<x-forms.checkbox :id="$checkbox['id']" :wire:model="$checkbox['id']"
|
<x-forms.checkbox :id="$checkbox['id']" :wire:model="$checkbox['id']"
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
|
|
||||||
<!-- Step 2: Confirm deletion -->
|
<!-- Step 2: Confirm deletion -->
|
||||||
<div x-show="step === 2">
|
<div x-show="step === 2">
|
||||||
<div class="bg-error border-l-4 border-red-500 text-white p-4 mb-4" role="alert">
|
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error" role="alert">
|
||||||
<p class="font-bold">Warning</p>
|
<p class="font-bold">Warning</p>
|
||||||
<p>This operation is permanent and cannot be undone. Please think again before proceeding!
|
<p>This operation is permanent and cannot be undone. Please think again before proceeding!
|
||||||
</p>
|
</p>
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
<ul class="mb-4 space-y-2">
|
<ul class="mb-4 space-y-2">
|
||||||
@foreach ($actions as $action)
|
@foreach ($actions as $action)
|
||||||
<li class="flex items-center text-red-500">
|
<li class="flex items-center text-red-500">
|
||||||
<svg class="w-5 h-5 mr-2 flex-shrink-0" fill="none" stroke="currentColor"
|
<svg class="flex-shrink-0 mr-2 w-5 h-5" fill="none" stroke="currentColor"
|
||||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
d="M6 18L18 6M6 6l12 12"></path>
|
d="M6 18L18 6M6 6l12 12"></path>
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
@foreach ($checkboxes as $checkbox)
|
@foreach ($checkboxes as $checkbox)
|
||||||
<template x-if="selectedActions.includes('{{ $checkbox['id'] }}')">
|
<template x-if="selectedActions.includes('{{ $checkbox['id'] }}')">
|
||||||
<li class="flex items-center text-red-500">
|
<li class="flex items-center text-red-500">
|
||||||
<svg class="w-5 h-5 mr-2 flex-shrink-0" fill="none" stroke="currentColor"
|
<svg class="flex-shrink-0 mr-2 w-5 h-5" fill="none" stroke="currentColor"
|
||||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
d="M6 18L18 6M6 6l12 12"></path>
|
d="M6 18L18 6M6 6l12 12"></path>
|
||||||
@@ -228,16 +228,16 @@
|
|||||||
</ul>
|
</ul>
|
||||||
@if ($confirmWithText)
|
@if ($confirmWithText)
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h4 class="text-lg font-semibold mb-2">Confirm Actions</h4>
|
<h4 class="mb-2 text-lg font-semibold">Confirm Actions</h4>
|
||||||
<p class="text-sm mb-2">{{ $confirmationLabel }}</p>
|
<p class="mb-2 text-sm">{{ $confirmationLabel }}</p>
|
||||||
<div class="relative mb-2">
|
<div class="relative mb-2">
|
||||||
<input type="text" x-model="confirmationText"
|
<input type="text" x-model="confirmationText"
|
||||||
class="w-full p-2 pr-10 rounded text-black input cursor-text" readonly>
|
class="p-2 pr-10 w-full text-black rounded cursor-text input" readonly>
|
||||||
<button @click="copyConfirmationText()"
|
<button @click="copyConfirmationText()"
|
||||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
class="absolute right-2 top-1/2 text-gray-500 transform -translate-y-1/2 hover:text-gray-700"
|
||||||
title="Copy confirmation text" x-ref="copyButton">
|
title="Copy confirmation text" x-ref="copyButton">
|
||||||
<template x-if="!copied">
|
<template x-if="!copied">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 20 20"
|
||||||
fill="currentColor">
|
fill="currentColor">
|
||||||
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
|
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
|
||||||
<path
|
<path
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="copied">
|
<template x-if="copied">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-500"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-green-500"
|
||||||
viewBox="0 0 20 20" fill="currentColor">
|
viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||||
@@ -256,18 +256,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label for="userConfirmationText"
|
<label for="userConfirmationText"
|
||||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mt-4">
|
class="block mt-4 text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
{{ $shortConfirmationLabel }}
|
{{ $shortConfirmationLabel }}
|
||||||
</label>
|
</label>
|
||||||
<input type="text" x-model="userConfirmationText"
|
<input type="text" x-model="userConfirmationText"
|
||||||
class="w-full p-2 rounded text-black input mt-1">
|
class="p-2 mt-1 w-full text-black rounded input">
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Step 3: Password confirmation -->
|
<!-- Step 3: Password confirmation -->
|
||||||
<div x-show="step === 3 && confirmWithPassword">
|
<div x-show="step === 3 && confirmWithPassword">
|
||||||
<div class="bg-error border-l-4 border-red-500 text-white p-4 mb-4" role="alert">
|
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error" role="alert">
|
||||||
<p class="font-bold">Final Confirmation</p>
|
<p class="font-bold">Final Confirmation</p>
|
||||||
<p>Please enter your password to confirm this destructive action.</p>
|
<p>Please enter your password to confirm this destructive action.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -276,11 +276,11 @@
|
|||||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
Your Password
|
Your Password
|
||||||
</label>
|
</label>
|
||||||
<input type="password" id="password-confirm" x-model="password" class="input w-full"
|
<input type="password" id="password-confirm" x-model="password" class="w-full input"
|
||||||
placeholder="Enter your password">
|
placeholder="Enter your password">
|
||||||
<p x-show="passwordError" x-text="passwordError" class="text-red-500 text-sm mt-1"></p>
|
<p x-show="passwordError" x-text="passwordError" class="mt-1 text-sm text-red-500"></p>
|
||||||
@error('password')
|
@error('password')
|
||||||
<p class="text-red-500 text-sm mt-1">{{ $message }}</p>
|
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<x-emails.layout>
|
<x-emails.layout>
|
||||||
Your server ({{ $name }}) disabled because it is not paid! All automations and integrations are stopped.
|
Your server ({{ $name }}) disabled because it is not paid! All automations and integrations are stopped.
|
||||||
|
|
||||||
Please update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions).
|
Please update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).
|
||||||
</x-emails.layout>
|
</x-emails.layout>
|
||||||
|
|||||||
@@ -273,27 +273,23 @@
|
|||||||
Please let me know your server details.
|
Please let me know your server details.
|
||||||
</x-slot:question>
|
</x-slot:question>
|
||||||
<x-slot:actions>
|
<x-slot:actions>
|
||||||
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 lg:pr-10">
|
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 lg:w-96">
|
||||||
<div class="flex flex-col gap-2 lg:flex-row">
|
|
||||||
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
|
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
|
||||||
label="Name" id="remoteServerName" wire:model="remoteServerName" />
|
label="Name" id="remoteServerName" wire:model="remoteServerName" />
|
||||||
<x-forms.input placeholder="Description, so others will know more about this."
|
<x-forms.input placeholder="Description, so others will know more about this."
|
||||||
label="Description" id="remoteServerDescription"
|
label="Description" id="remoteServerDescription"
|
||||||
wire:model="remoteServerDescription" />
|
wire:model="remoteServerDescription" />
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-2 lg:flex-row ">
|
|
||||||
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
|
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
|
||||||
wire:model="remoteServerHost" />
|
wire:model="remoteServerHost" />
|
||||||
</div>
|
|
||||||
<div x-data="{ showAdvanced: false }" class="flex flex-col gap-2">
|
<div x-data="{ showAdvanced: false }" class="flex flex-col gap-2">
|
||||||
<button @click="showAdvanced = !showAdvanced" type="button"
|
<button @click="showAdvanced = !showAdvanced" type="button"
|
||||||
class="text-left text-sm text-gray-600 dark:text-gray-300 hover:underline">
|
class="text-left text-sm text-gray-600 dark:text-gray-300 hover:underline">
|
||||||
Advanced Settings
|
Advanced Settings
|
||||||
</button>
|
</button>
|
||||||
<div x-show="showAdvanced" class="flex flex-col gap-2 lg:flex-row">
|
<div x-show="showAdvanced" class="flex flex-col gap-2">
|
||||||
<x-forms.input placeholder="Port number of your server. Default is 22." label="Port"
|
<x-forms.input placeholder="Port number of your server. Default is 22." label="Port"
|
||||||
id="remoteServerPort" wire:model="remoteServerPort" />
|
id="remoteServerPort" wire:model="remoteServerPort" />
|
||||||
<div class="w-full">
|
<div>
|
||||||
<x-forms.input placeholder="Default is root." label="User"
|
<x-forms.input placeholder="Default is root." label="User"
|
||||||
id="remoteServerUser" wire:model="remoteServerUser" />
|
id="remoteServerUser" wire:model="remoteServerUser" />
|
||||||
<div class="text-xs text-gray-600 dark:text-gray-300">Non-root user is
|
<div class="text-xs text-gray-600 dark:text-gray-300">Non-root user is
|
||||||
@@ -303,11 +299,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="lg:w-64">
|
|
||||||
<x-forms.checkbox
|
|
||||||
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
|
|
||||||
id="isCloudflareTunnel" label="Cloudflare Tunnel" wire:model="isCloudflareTunnel" />
|
|
||||||
</div>
|
|
||||||
<x-forms.button type="submit">Continue</x-forms.button>
|
<x-forms.button type="submit">Continue</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
</x-slot:actions>
|
</x-slot:actions>
|
||||||
|
|||||||
@@ -6,18 +6,10 @@
|
|||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@if ($destination->network !== 'coolify')
|
@if ($destination->network !== 'coolify')
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Destination Deletion?" buttonTitle="Delete Destination" isErrorButton
|
||||||
title="Confirm Destination Deletion?"
|
submitAction="delete" :actions="['This will delete the selected destination/network.']" confirmationText="{{ $destination->name }}"
|
||||||
buttonTitle="Delete Destination"
|
|
||||||
isErrorButton
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="['This will delete the selected destination/network.']"
|
|
||||||
confirmationText="{{ $destination->name }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Destination Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Destination Name below"
|
||||||
shortConfirmationLabel="Destination Name"
|
shortConfirmationLabel="Destination Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" />
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Permanently Delete Destination"
|
|
||||||
/>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@
|
|||||||
confirmationText="{{ $team }}"
|
confirmationText="{{ $team }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
|
||||||
shortConfirmationLabel="Team Name"
|
shortConfirmationLabel="Team Name"
|
||||||
step3ButtonText="Permanently Delete Team"
|
step3ButtonText="Permanently Delete"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -68,17 +68,10 @@
|
|||||||
<option value="www">Redirect to www.</option>
|
<option value="www">Redirect to www.</option>
|
||||||
<option value="non-www">Redirect to non-www.</option>
|
<option value="non-www">Redirect to non-www.</option>
|
||||||
</x-forms.select>
|
</x-forms.select>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Redirection Setting?" buttonTitle="Set Direction"
|
||||||
title="Confirm Redirection Setting?"
|
submitAction="set_redirect" :actions="['All traffic will be redirected to the selected direction.']" confirmationText="{{ $application->fqdn . '/' }}"
|
||||||
buttonTitle="Set Direction"
|
|
||||||
submitAction="set_redirect"
|
|
||||||
:actions="['All traffic will be redirected to the selected direction.']"
|
|
||||||
confirmationText="{{ $application->fqdn . '/' }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the action by entering the Application URL below"
|
confirmationLabel="Please confirm the execution of the action by entering the Application URL below"
|
||||||
shortConfirmationLabel="Application URL"
|
shortConfirmationLabel="Application URL" :confirmWithPassword="false" step2ButtonText="Set Direction">
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Set Direction"
|
|
||||||
>
|
|
||||||
<x-slot:customButton>
|
<x-slot:customButton>
|
||||||
<div class="w-[7.2rem]">Set Direction</div>
|
<div class="w-[7.2rem]">Set Direction</div>
|
||||||
</x-slot:customButton>
|
</x-slot:customButton>
|
||||||
@@ -311,18 +304,15 @@
|
|||||||
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
|
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
|
||||||
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
|
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
|
||||||
</div>
|
</div>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Labels Reset to Coolify Defaults?"
|
||||||
title="Confirm Labels Reset to Coolify Defaults?"
|
buttonTitle="Reset Labels to Coolify Defaults" buttonFullWidth submitAction="resetDefaultLabels"
|
||||||
buttonTitle="Reset Labels to Coolify Defaults"
|
:actions="[
|
||||||
buttonFullWidth
|
'All your custom proxy labels will be lost.',
|
||||||
submitAction="resetDefaultLabels"
|
'Proxy labels (traefik, caddy, etc) will be reset to the coolify defaults.',
|
||||||
:actions="['All your custom proxy labels will be lost.', 'Proxy labels (traefik, caddy, etc) will be reset to the coolify defaults.']"
|
]" confirmationText="{{ $application->fqdn . '/' }}"
|
||||||
confirmationText="{{ $application->fqdn . '/' }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Application URL below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Application URL below"
|
||||||
shortConfirmationLabel="Application URL"
|
shortConfirmationLabel="Application URL" :confirmWithPassword="false"
|
||||||
:confirmWithPassword="false"
|
step2ButtonText="Permanently Reset Labels" />
|
||||||
step2ButtonText="Permanently Reset Labels"
|
|
||||||
/>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<h3 class="pt-8">Pre/Post Deployment Commands</h3>
|
<h3 class="pt-8">Pre/Post Deployment Commands</h3>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
confirmationText="{{ data_get($execution, 'filename') }}"
|
confirmationText="{{ data_get($execution, 'filename') }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Backup Filename below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Backup Filename below"
|
||||||
shortConfirmationLabel="Backup Filename"
|
shortConfirmationLabel="Backup Filename"
|
||||||
step3ButtonText="Permanently Delete Backup"
|
step3ButtonText="Permanently Delete"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,27 +27,24 @@
|
|||||||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||||
<a class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}" href="{{ route('project.database.backup.index', $parameters) }}">
|
<a class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
||||||
|
href="{{ route('project.database.backup.index', $parameters) }}">
|
||||||
<button>Backups</button>
|
<button>Backups</button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</nav>
|
</nav>
|
||||||
<div class="flex flex-wrap gap-2 items-center">
|
<div class="flex flex-wrap gap-2 items-center">
|
||||||
@if (!str($database->status)->startsWith('exited'))
|
@if (!str($database->status)->startsWith('exited'))
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Database Restart?" buttonTitle="Restart" submitAction="restart"
|
||||||
title="Confirm Database Restart?"
|
:actions="[
|
||||||
buttonTitle="Restart"
|
'This database will be unavailable during the restart.',
|
||||||
submitAction="restart"
|
'If the database is currently in use data could be lost.',
|
||||||
:actions="['This database will be unavailable during the restart.', 'If the database is currently in use data could be lost.']"
|
]" :confirmWithText="false" :confirmWithPassword="false" step2ButtonText="Restart Database"
|
||||||
:confirmWithText="false"
|
:dispatchEvent="true" dispatchEventType="restartEvent">
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Restart Database"
|
|
||||||
:dispatchEvent="true"
|
|
||||||
dispatchEventType="restartEvent"
|
|
||||||
>
|
|
||||||
<x-slot:button-title>
|
<x-slot:button-title>
|
||||||
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2">
|
||||||
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||||
<path d="M20 4v5h-5" />
|
<path d="M20 4v5h-5" />
|
||||||
</g>
|
</g>
|
||||||
@@ -55,21 +52,17 @@
|
|||||||
Restart
|
Restart
|
||||||
</x-slot:button-title>
|
</x-slot:button-title>
|
||||||
</x-modal-confirmation>
|
</x-modal-confirmation>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Database Stopping?" buttonTitle="Stop" submitAction="stop"
|
||||||
title="Confirm Database Stopping?"
|
:checkboxes="$checkboxes" :actions="[
|
||||||
buttonTitle="Stop"
|
'This database will be stopped.',
|
||||||
submitAction="stop"
|
'If the database is currently in use data could be lost.',
|
||||||
:checkboxes="$checkboxes"
|
'All non-persistent data of this database (containers, networks, unused images) will be deleted (don\'t worry, no data is lost and you can start the database again).',
|
||||||
:actions="['This database will be stopped.', 'If the database is currently in use data could be lost.', 'All non-persistent data of this database (containers, networks, unused images) will be deleted (don\'t worry, no data is lost and you can start the database again).']"
|
]" :confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue"
|
||||||
:confirmWithText="false"
|
step2ButtonText="Stop Database" :dispatchEvent="true" dispatchEventType="stopEvent">
|
||||||
:confirmWithPassword="false"
|
|
||||||
step1ButtonText="Continue Stopping Database"
|
|
||||||
step2ButtonText="Stop Database"
|
|
||||||
:dispatchEvent="true"
|
|
||||||
dispatchEventType="stopEvent"
|
|
||||||
>
|
|
||||||
<x-slot:button-title>
|
<x-slot:button-title>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||||
|
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||||
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
|
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
|
||||||
</path>
|
</path>
|
||||||
@@ -81,7 +74,9 @@
|
|||||||
</x-modal-confirmation>
|
</x-modal-confirmation>
|
||||||
@else
|
@else
|
||||||
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
|
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M7 4v16l13 -8z" />
|
<path d="M7 4v16l13 -8z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -2,21 +2,13 @@
|
|||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
<x-forms.input id="filename" label="Filename" />
|
<x-forms.input id="filename" label="Filename" />
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm init-script deletion?" buttonTitle="Delete" isErrorButton
|
||||||
title="Confirm init-script deletion?"
|
submitAction="delete" :actions="[
|
||||||
buttonTitle="Delete"
|
|
||||||
isErrorButton
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="[
|
|
||||||
'The init-script of this database will be permanently deleted.',
|
'The init-script of this database will be permanently deleted.',
|
||||||
'If you are actively using this init-script, it could cause errors on redeployment.'
|
'If you are actively using this init-script, it could cause errors on redeployment.',
|
||||||
]"
|
]" confirmationText="{{ $filename }}"
|
||||||
confirmationText="{{ $filename }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the init-script name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the init-script name below"
|
||||||
shortConfirmationLabel="Init-script Name"
|
shortConfirmationLabel="Init-script Name" :confirmWithPassword=false step2ButtonText="Permanently Delete" />
|
||||||
:confirmWithPassword=false
|
|
||||||
step2ButtonText="Permanently Delete Init-script"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<x-forms.textarea id="content" label="Content" />
|
<x-forms.textarea id="content" label="Content" />
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Environment Deletion?" buttonTitle="Delete Environment" isErrorButton
|
||||||
title="Confirm Environment Deletion?"
|
submitAction="delete" :actions="['This will delete the selected environment.']"
|
||||||
buttonTitle="Delete Environment"
|
|
||||||
isErrorButton
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="['This will delete the selected environment.']"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Environment Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Environment Name below"
|
||||||
shortConfirmationLabel="Environment Name"
|
shortConfirmationLabel="Environment Name" confirmationText="{{ $environmentName }}" :confirmWithPassword="false"
|
||||||
confirmationText="{{ $environmentName }}"
|
step2ButtonText="Permanently Delete" />
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Permanently Delete Environment"
|
|
||||||
/>
|
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Project Deletion?" buttonTitle="Delete Project" isErrorButton submitAction="delete"
|
||||||
title="Confirm Project Deletion?"
|
:actions="[
|
||||||
buttonTitle="Delete Project"
|
'This will delete the selected project',
|
||||||
isErrorButton
|
'All Environments inside the project will be deleted as well.',
|
||||||
submitAction="delete"
|
]" confirmationLabel="Please confirm the execution of the actions by entering the Project Name below"
|
||||||
:actions="['This will delete the selected project', 'All Environments inside the project will be deleted as well.']"
|
shortConfirmationLabel="Project Name" confirmationText="{{ $projectName }}" :confirmWithPassword="false"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Project Name below"
|
step2ButtonText="Permanently Delete" />
|
||||||
shortConfirmationLabel="Project Name"
|
|
||||||
confirmationText="{{ $projectName }}"
|
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Permanently Delete Project"
|
|
||||||
/>
|
|
||||||
|
|||||||
@@ -491,7 +491,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pb-4 text-xs">Trademarks Policy: The respective trademarks mentioned here are owned by the
|
<div class="pb-4 text-xs">Trademarks Policy: The respective trademarks mentioned here are owned by the
|
||||||
respective
|
respective
|
||||||
companies, and use of them does not imply any affiliation or endorsement.</div>
|
companies, and use of them does not imply any affiliation or endorsement.<br>Find more services <a
|
||||||
|
class="dark:text-white underline" target="_blank"
|
||||||
|
href="https://coolify.io/docs/services">here</a>.</div>
|
||||||
<input class="input" autofocus wire:model.live.debounce.200ms="search" autofocus
|
<input class="input" autofocus wire:model.live.debounce.200ms="search" autofocus
|
||||||
placeholder="Search...">
|
placeholder="Search...">
|
||||||
@if ($loadingServices)
|
@if ($loadingServices)
|
||||||
|
|||||||
@@ -15,56 +15,32 @@
|
|||||||
<form wire:submit='submit' class="flex flex-col gap-2">
|
<form wire:submit='submit' class="flex flex-col gap-2">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@if ($fileStorage->is_directory)
|
@if ($fileStorage->is_directory)
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Directory Conversion to File?" buttonTitle="Convert to file"
|
||||||
title="Confirm Directory Conversion to File?"
|
submitAction="convertToFile" :actions="[
|
||||||
buttonTitle="Convert to file"
|
'All files in this directory will be permanently deleted and an empty file will be created in its place.',
|
||||||
submitAction="convertToFile"
|
]" confirmationText="{{ $fs_path }}"
|
||||||
:actions="['All files in this directory will be permanently deleted and an empty file will be created in its place.']"
|
|
||||||
confirmationText="{{ $fs_path }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath"
|
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to file" />
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Convert to file"
|
|
||||||
/>
|
|
||||||
@else
|
@else
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm File Conversion to Directory?" buttonTitle="Convert to directory"
|
||||||
title="Confirm File Conversion to Directory?"
|
submitAction="convertToDirectory" :actions="[
|
||||||
buttonTitle="Convert to directory"
|
'The selected file will be permanently deleted and an empty directory will be created in its place.',
|
||||||
submitAction="convertToDirectory"
|
]" confirmationText="{{ $fs_path }}"
|
||||||
:actions="['The selected file will be permanently deleted and an empty directory will be created in its place.']"
|
|
||||||
confirmationText="{{ $fs_path }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath"
|
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to directory" />
|
||||||
:confirmWithPassword="false"
|
|
||||||
step2ButtonText="Convert to directory"
|
|
||||||
/>
|
|
||||||
@endif
|
@endif
|
||||||
@if ($fileStorage->is_directory)
|
@if ($fileStorage->is_directory)
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Directory Deletion?" buttonTitle="Delete Directory" isErrorButton
|
||||||
title="Confirm Directory Deletion?"
|
submitAction="delete" :checkboxes="$directoryDeletionCheckboxes" :actions="[
|
||||||
buttonTitle="Delete Directory"
|
'The selected directory and all its contents will be permanently deleted from the container.',
|
||||||
isErrorButton
|
]" confirmationText="{{ $fs_path }}"
|
||||||
submitAction="delete"
|
|
||||||
:checkboxes="$directoryDeletionCheckboxes"
|
|
||||||
:actions="['The selected directory and all its contents will be permanently deleted from the container.']"
|
|
||||||
confirmationText="{{ $fs_path }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath"
|
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
||||||
step3ButtonText="Permanently Delete Directory"
|
|
||||||
/>
|
|
||||||
@else
|
@else
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm File Deletion?" buttonTitle="Delete File" isErrorButton
|
||||||
title="Confirm File Deletion?"
|
submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']" confirmationText="{{ $fs_path }}"
|
||||||
buttonTitle="Delete File"
|
|
||||||
isErrorButton
|
|
||||||
submitAction="delete"
|
|
||||||
:checkboxes="$fileDeletionCheckboxes"
|
|
||||||
:actions="['The selected file will be permanently deleted from the container.']"
|
|
||||||
confirmationText="{{ $fs_path }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath"
|
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
||||||
step3ButtonText="Permanently Delete File"
|
|
||||||
/>
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (!$fileStorage->is_based_on_git)
|
@if (!$fileStorage->is_based_on_git)
|
||||||
|
|||||||
@@ -7,20 +7,11 @@
|
|||||||
<h2>{{ Str::headline($application->name) }}</h2>
|
<h2>{{ Str::headline($application->name) }}</h2>
|
||||||
@endif
|
@endif
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
|
||||||
title="Confirm Service Application Deletion?"
|
submitAction="delete" {{-- :checkboxes="$checkboxes" --}} :actions="['The selected service application container will be stopped and permanently deleted.']"
|
||||||
buttonTitle="Delete"
|
|
||||||
isErrorButton
|
|
||||||
submitAction="delete"
|
|
||||||
{{-- :checkboxes="$checkboxes" --}}
|
|
||||||
:actions="[
|
|
||||||
'The selected service application container will be stopped and permanently deleted.'
|
|
||||||
]"
|
|
||||||
confirmationText="{{ Str::headline($application->name) }}"
|
confirmationText="{{ Str::headline($application->name) }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||||
shortConfirmationLabel="Service Application Name"
|
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
|
||||||
step3ButtonText="Permanently Delete Service Application"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="pb-4">This will stop your containers, delete all related data, etc. Beware! There is no coming back!
|
<div class="pb-4">This will stop your containers, delete all related data, etc. Beware! There is no coming back!
|
||||||
</div>
|
</div>
|
||||||
<x-modal-confirmation title="Confirm Resource Deletion?" buttonTitle="Delete" isErrorButton submitAction="delete"
|
<x-modal-confirmation title="Confirm Resource Deletion?" buttonTitle="Delete" isErrorButton submitAction="delete"
|
||||||
buttonTitle="Delete" :checkboxes="$checkboxes" :actions="['All containers of this resource will be stopped and permanently deleted.']" confirmationText="{{ $resourceName }}"
|
buttonTitle="Delete" :checkboxes="$checkboxes" :actions="['Permanently delete all containers of this resource.']" confirmationText="{{ $resourceName }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the NAME of the resource below"
|
confirmationLabel="Please confirm the execution of the actions by entering the NAME of the resource below"
|
||||||
shortConfirmationLabel="Resource Name" step3ButtonText="Delete Permanently" />
|
shortConfirmationLabel="Resource Name" step3ButtonText="Permanently Delete" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
<x-forms.input x-show="$wire.is_multiline === false" x-cloak placeholder="production" id="value"
|
<x-forms.input x-show="$wire.is_multiline === false" x-cloak placeholder="production" id="value"
|
||||||
x-bind:label="$wire.is_multiline === false && 'Value'" required />
|
x-bind:label="$wire.is_multiline === false && 'Value'" required />
|
||||||
@if (data_get($parameters, 'application_uuid'))
|
@if (data_get($parameters, 'application_uuid'))
|
||||||
<x-forms.checkbox id="is_build_time" label="Build Variable?" />
|
<x-forms.checkbox id="is_build_time"
|
||||||
|
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
|
||||||
|
label="Build Variable?" />
|
||||||
@endif
|
@endif
|
||||||
<x-forms.checkbox id="is_multiline" label="Is Multiline?" />
|
<x-forms.checkbox id="is_multiline" label="Is Multiline?" />
|
||||||
@if (!$shared)
|
@if (!$shared)
|
||||||
|
|||||||
@@ -11,20 +11,11 @@
|
|||||||
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0-2 0m-3-5V7a4 4 0 1 1 8 0v4" />
|
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0-2 0m-3-5V7a4 4 0 1 1 8 0v4" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Environment Variable Deletion?" isErrorButton buttonTitle="Delete"
|
||||||
title="Confirm Environment Variable Deletion?"
|
submitAction="delete" :actions="['The selected environment variable will be permanently deleted.']" confirmationText="{{ $env->key }}"
|
||||||
isErrorButton
|
|
||||||
buttonTitle="Delete"
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="[
|
|
||||||
'The selected environment variable will be permanently deleted.'
|
|
||||||
]"
|
|
||||||
confirmationText="{{ $env->key }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
|
||||||
shortConfirmationLabel="Environment Variable Name"
|
shortConfirmationLabel="Environment Variable Name" :confirmWithPassword="false"
|
||||||
:confirmWithPassword="false"
|
step2ButtonText="Permanently Delete" />
|
||||||
step2ButtonText="Permanently Delete Environment Variable"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
@if ($isDisabled)
|
@if ($isDisabled)
|
||||||
@@ -51,10 +42,14 @@
|
|||||||
@endif
|
@endif
|
||||||
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
||||||
@if ($type === 'service')
|
@if ($type === 'service')
|
||||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
<x-forms.checkbox instantSave id="env.is_build_time"
|
||||||
|
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
|
||||||
|
label="Build Variable?" />
|
||||||
@else
|
@else
|
||||||
@if ($env->is_shared)
|
@if ($env->is_shared)
|
||||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
<x-forms.checkbox instantSave id="env.is_build_time"
|
||||||
|
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
|
||||||
|
label="Build Variable?" />
|
||||||
<x-forms.checkbox instantSave id="env.is_literal"
|
<x-forms.checkbox instantSave id="env.is_literal"
|
||||||
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
|
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
|
||||||
label="Is Literal?" />
|
label="Is Literal?" />
|
||||||
@@ -62,7 +57,9 @@
|
|||||||
@if ($isSharedVariable)
|
@if ($isSharedVariable)
|
||||||
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
|
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
|
||||||
@else
|
@else
|
||||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
<x-forms.checkbox instantSave id="env.is_build_time"
|
||||||
|
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for dockerfile, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
|
||||||
|
label="Build Variable?" />
|
||||||
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
|
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
|
||||||
@if (!data_get($env, 'is_multiline'))
|
@if (!data_get($env, 'is_multiline'))
|
||||||
<x-forms.checkbox instantSave id="env.is_literal"
|
<x-forms.checkbox instantSave id="env.is_literal"
|
||||||
@@ -80,20 +77,12 @@
|
|||||||
<x-forms.button wire:click='lock'>
|
<x-forms.button wire:click='lock'>
|
||||||
Lock
|
Lock
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Environment Variable Deletion?" isErrorButton
|
||||||
title="Confirm Environment Variable Deletion?"
|
buttonTitle="Delete" submitAction="delete" :actions="['The selected environment variable will be permanently deleted.']"
|
||||||
isErrorButton
|
|
||||||
buttonTitle="Delete"
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="[
|
|
||||||
'The selected environment variable will be permanently deleted.'
|
|
||||||
]"
|
|
||||||
confirmationText="{{ $env->key }}"
|
confirmationText="{{ $env->key }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
|
||||||
shortConfirmationLabel="Environment Variable Name"
|
shortConfirmationLabel="Environment Variable Name" :confirmWithPassword="false"
|
||||||
:confirmWithPassword="false"
|
step2ButtonText="Permanently Delete" />
|
||||||
step2ButtonText="Permanently Delete Environment Variable"
|
|
||||||
/>
|
|
||||||
@else
|
@else
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Update
|
Update
|
||||||
@@ -101,20 +90,12 @@
|
|||||||
<x-forms.button wire:click='lock'>
|
<x-forms.button wire:click='lock'>
|
||||||
Lock
|
Lock
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Environment Variable Deletion?" isErrorButton
|
||||||
title="Confirm Environment Variable Deletion?"
|
buttonTitle="Delete" submitAction="delete" :actions="['The selected environment variable will be permanently deleted.']"
|
||||||
isErrorButton
|
|
||||||
buttonTitle="Delete"
|
|
||||||
submitAction="delete"
|
|
||||||
:actions="[
|
|
||||||
'The selected environment variable will be permanently deleted.'
|
|
||||||
]"
|
|
||||||
confirmationText="{{ $env->key }}"
|
confirmationText="{{ $env->key }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
|
||||||
shortConfirmationLabel="Environment Variable Name"
|
shortConfirmationLabel="Environment Variable Name" :confirmWithPassword="false"
|
||||||
:confirmWithPassword="false"
|
step2ButtonText="Permanently Delete" />
|
||||||
step2ButtonText="Permanently Delete Environment Variable"
|
|
||||||
/>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -11,26 +11,22 @@
|
|||||||
|
|
||||||
<form wire:submit="submit" class="w-full">
|
<form wire:submit="submit" class="w-full">
|
||||||
<div class="flex flex-col gap-2 pb-2">
|
<div class="flex flex-col gap-2 pb-2">
|
||||||
<div class="flex items-end gap-2 pt-4">
|
<div class="flex gap-2 items-end pt-4">
|
||||||
<h2>Scheduled Task</h2>
|
<h2>Scheduled Task</h2>
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
|
||||||
title="Confirm Scheduled Task Deletion?"
|
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
|
||||||
isErrorButton
|
|
||||||
buttonTitle="Delete"
|
|
||||||
submitAction="delete({{ $task->id }})"
|
|
||||||
:actions="['The selected scheduled task will be permanently deleted.']"
|
|
||||||
confirmationText="{{ $task->name }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
|
||||||
shortConfirmationLabel="Scheduled Task Name"
|
shortConfirmationLabel="Scheduled Task Name" :confirmWithPassword="false"
|
||||||
:confirmWithPassword="false"
|
step2ButtonText="Permanently Delete" />
|
||||||
step2ButtonText="Permanently Delete Scheduled Task"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-full gap-2">
|
<div class="w-48">
|
||||||
|
<x-forms.checkbox instantSave id="task.enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 w-full">
|
||||||
<x-forms.input placeholder="Name" id="task.name" label="Name" required />
|
<x-forms.input placeholder="Name" id="task.name" label="Name" required />
|
||||||
<x-forms.input placeholder="php artisan schedule:run" id="task.command" label="Command" required />
|
<x-forms.input placeholder="php artisan schedule:run" id="task.command" label="Command" required />
|
||||||
<x-forms.input placeholder="0 0 * * * or daily" id="task.frequency" label="Frequency" required />
|
<x-forms.input placeholder="0 0 * * * or daily" id="task.frequency" label="Frequency" required />
|
||||||
@@ -48,6 +44,7 @@
|
|||||||
|
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<h3 class="py-4">Recent executions <span class="text-xs text-neutral-500">(click to check output)</span></h3>
|
<h3 class="py-4">Recent executions <span class="text-xs text-neutral-500">(click to check output)</span></h3>
|
||||||
<livewire:project.shared.scheduled-task.executions :task="$task" key="{{ $task->id }}" selectedKey="" :executions="$task->executions->take(20)" />
|
<livewire:project.shared.scheduled-task.executions :task="$task" key="{{ $task->id }}" selectedKey=""
|
||||||
|
:executions="$task->executions->take(20)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,17 +46,13 @@
|
|||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Update
|
Update
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation title="Confirm persistent storage deletion?" isErrorButton buttonTitle="Delete"
|
||||||
title="Confirm persistent storage deletion?"
|
submitAction="delete" :actions="[
|
||||||
isErrorButton
|
'The selected persistent storage/volume will be permanently deleted.',
|
||||||
buttonTitle="Delete"
|
'If the persistent storage/volume is actvily used by a resource data will be lost.',
|
||||||
submitAction="delete"
|
]" confirmationText="{{ $storage->name }}"
|
||||||
:actions="['The selected persistent storage/volume will be permanently deleted.', 'If the persistent storage/volume is actvily used by a resource data will be lost.']"
|
|
||||||
confirmationText="{{ $storage->name }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Storage Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Storage Name below"
|
||||||
shortConfirmationLabel="Storage Name"
|
shortConfirmationLabel="Storage Name" step3ButtonText="Permanently Delete" />
|
||||||
step3ButtonText="Permanently Delete Persistent Storage/Volume"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<form wire:submit.prevent='submit' class="flex flex-col w-full gap-2">
|
<form wire:submit.prevent='submit' class="flex flex-col gap-2 w-full">
|
||||||
<x-forms.input id="cloudflare_token" required label="Cloudflare Token" />
|
<x-forms.input id="cloudflare_token" required label="Cloudflare Token" type="password" />
|
||||||
<x-forms.input id="ssh_domain" label="Configured SSH Domain" required
|
<x-forms.input id="ssh_domain" label="Configured SSH Domain" required
|
||||||
helper="The SSH Domain you configured in Cloudflare" />
|
helper="The SSH domain you configured in Cloudflare. Make sure there is no protocol like http(s):// so you provide a FQDN not a URL. <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels/#automated' target='_blank'>Documentation</a>" />
|
||||||
<x-forms.button type="submit" isHighlighted @click="modalOpen=false">Automated Configuration (experimental)</x-forms.button>
|
<x-forms.button type="submit" isHighlighted @click="modalOpen=false">Continue</x-forms.button>
|
||||||
<h3 class="text-center">Or</h3>
|
|
||||||
<x-forms.button wire:click.prevent='alreadyConfigured' @click="modalOpen=false">I have already set up the tunnel manually on the server.</x-forms.button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user